Commit Graph

666 Commits

Author SHA1 Message Date
a5b22dadf3 docs(spec): master design for system spec + integrity layer
Establishes the foundation to fix a recurring root-cause failure class
(non-canonical identifiers, asymmetric ingest paths, silent manual gates):

- Confirmed system mission (quasi-judicial decision assistant; human decides)
- Decomposition into 5 sub-projects (spec → audit → integrity layer → re-check → process agents)
- spec-set structure under docs/spec/ (lifecycle-organized + cross-cutting files)
- 11 global invariants + engineering rules, each backed by ≥3 authoritative sources
  (NCSC/JTC, FJC, CEPEJ, South Bucks; RAG/Lewis, Manning IR, Elastic/Pinecone/Weaviate;
   DAMA-DMBOK, ISO 8000, ISO 15489, Kleppmann, Codd, Fowler)
- 3-source verification protocol; UNVERIFIED items escalated, not decided solo

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 14:05:06 +00:00
7826ff4910 fix(cases): tolerant case_number lookup so agents see case documents
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m39s
Reported: an agent claimed the case had no documents because document_list
returned empty — but the documents exist. Root cause: get_case_by_number did
an exact `WHERE case_number = $1`, so any formatting variant of the number
silently failed to resolve. Verified on 8137-24 (9 docs): "8137/24",
"ערר 8137-24", leading/trailing space, and "בל\"מ 8126/03/25" all returned
"תיק לא נמצא", which the agent read as "no documents" and went blind.

Add _normalize_case_number (strip leading proceeding-type prefix to the first
digit, trim, unify '/'→'-') and a normalized fallback in the lookup query
(exact match preferred via ORDER BY). One fix covers every case_number-scoped
tool (document_list, extract_references, search_case_documents, get_claims,
drafting, ...). Bogus numbers still correctly resolve to "not found". (#58)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 11:54:52 +00:00
58ab003206 fix(retrieval): make decisions findable by name + unhide committee uploads
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m57s
Root cause of "agent can't find the Agasi decision in the corpus" (CMPA-55):
the decision was fully ingested, but the retrieval layer failed on the
realistic agent query — searching by case name.

- RC-A (#52): lexical tsvector covered only chunk content + halacha text,
  so a bare-name query ("אגסי") matched decisions that *cite* the case, not
  the case itself. Add meta_tsv on case_law(case_name, case_number) (SCHEMA
  V20) and OR it into the lexical halacha/chunk SQL with a match boost, so a
  name/number hit surfaces the case's own rows. Agasi: rank 4 → rank 1.
- RC-B (#53): precedent_library_list hard-defaulted source_kind=external_upload
  and never exposed the param, hiding uploaded ערר/בל"מ (internal_committee)
  decisions. Thread source_kind through service → tool → MCP tool (supports
  'internal_committee' / 'all_committees').
- #54: agent instructions (researcher/analyst/writer) — search-by-name
  protocol: add content/case-number, search both corpora, use all_committees
  before declaring "not in corpus".
- #55: chunker produced tiny fragment chunks ("דיון", "החלטה") from header
  keywords matched mid-sentence. Anchor SECTION_PATTERNS to line start +
  merge sub-min sections; exclude <50-char fragments at query time (484
  existing fragments hidden; full re-chunk tracked as #57).

Tests: scripts/test_retrieval_by_name.py (name ranks case above citer +
substantive regressions); chunker unit checks (0 tiny chunks). New findings
filed as tasks #56 (halacha source_kind leak) and #57 (re-chunk migration).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 11:26:19 +00:00
165efc62b0 docs(claude): correct canonical tasks.json path + add CLI cwd footgun warning
TaskMaster's --tag selects the logical group inside a file, not which
tasks.json to write; the CLI resolves the file from cwd. Document the
canonical project-root-relative path and the cwd footgun.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 11:19:47 +00:00
d3c6baf9e2 security(chat): bind chat service to docker bridge + require Bearer auth
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m38s
Address security-review finding: the host-side legal-chat-service was
binding 0.0.0.0:8770 with no authentication. The service spawns the
claude CLI, whose tool set includes Bash + Edit — so an unauthenticated
/chat/start is effectively RCE. Oracle Cloud's security list closes the
port externally, but defense-in-depth requires two independent layers:

1. Bind defaults to 10.0.1.1 (docker0 bridge gateway). Reachable from
   containers on docker bridges (the legal-ai container has a route via
   the coolify network), invisible to anything outside the host. The
   --host flag is still configurable for local-dev (127.0.0.1) or
   special-case deployments, but 0.0.0.0 is explicitly discouraged in
   the docstring.
2. /chat/start requires Authorization: Bearer <LEGAL_CHAT_SHARED_SECRET>.
   The secret is loaded from /home/chaim/.legal-chat-service.env (chmod
   600, off-repo) by the pm2 ecosystem and mirrored as a Coolify env
   var so the FastAPI chat_proxy sends a matching header. hmac.compare_digest
   prevents timing oracles. /health stays unauthenticated (static OK,
   no subprocess) so the FastAPI proxy can probe liveness without the
   secret.

The service refuses to start if LEGAL_CHAT_SHARED_SECRET is empty or
shorter than 24 chars — no silent fallback to an open mode.

When the Infisical MCP comes back, migrate the secret into the vault
at /_GUIDELINES per the project secrets policy.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 10:22:14 +00:00
5ad541e54c ui(precedents): upload sheet routes ערר/בל"מ to internal-decisions endpoint
Some checks failed
Build & Deploy / build-and-deploy (push) Has been cancelled
Citations starting with ערר/בל"מ/ARAR are committee decisions and must
carry chair_name + district. The /precedents upload form previously
errored out for these (precedent_library service rejects them) with no
in-UI path forward — internal_decision_upload was only reachable via
the /missing-precedents flow.

The form now auto-detects committee citations, reveals chair_name +
district fields, hides the irrelevant source_type/precedent_level
(derived server-side), and posts to /api/internal-decisions/upload.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 10:22:03 +00:00
a3454bcb57 fix(training): bundle reference content + use docker bridge gateway
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 9s
The Style Studio's curator-prompt + chat features read reference docs
from disk at runtime. Two issues from the initial production run:

1. Dockerfile + .dockerignore excluded .claude/, docs/, and most of
   skills/. Now COPY the four specific files the new endpoints need:
     - .claude/agents/hermes-curator.md
     - skills/decision/SKILL.md
     - docs/legal-decision-lessons.md
     - docs/corpus-analysis.md
   .dockerignore opens whitelists for just those files.

2. Coolify's custom_docker_run_options=--add-host=host.docker.internal:host-gateway
   is not honored on dockerimage build_pack apps (ExtraHosts stayed []).
   Switch chat_proxy.py default to http://10.0.1.1:8770 — the docker0
   bridge gateway, same pattern Paperclip uses for 3100. Bind the host
   pm2 service to 0.0.0.0:8770 so the container can reach it via the
   bridge IP. Oracle Cloud's security list keeps the port unreachable
   from the public internet.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 10:15:27 +00:00
bb0cd7c6a2 feat(training): Style Studio — upload, rich corpus, lessons, curator portrait, chat
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 2m7s
Six-phase upgrade of /training from a read-only dashboard into a full
Style Studio for managing Daphna's style corpus.

- Upload Sheet on /training: file → proofread preview → commit (no more
  CLI-only `upload-training` skill).
- Rich corpus metadata: GET /api/training/corpus returns summary, outcome,
  key_principles, page_count, parties (regex), legal_citation, lessons_count.
  PATCH endpoint for chair edits. CorpusDetailDrawer with 4 tabs (details
  /content/lessons/patterns) replaces the bare table row.
- LLM metadata enrichment: style_metadata_extractor + MCP tools
  (style_corpus_enrich, style_corpus_pending_enrichment) fill summary
  /outcome/key_principles via claude_session (free, host-side).
- Per-decision lessons: new decision_lessons table + 4 REST endpoints +
  LessonsTab in drawer; hermes-curator now auto-posts findings as
  decision_lessons(source=curator).
- Curator Portrait tab: prompt rendered with link to Gitea, recent
  curator findings, style_analyzer training prompts, propose-change
  form that writes proposals to data/curator-proposals/ for manual
  chair review (no auto-mutation of the agent file).
- Style chat tab: SSE-streamed conversations with the style agent.
  New host-side pm2 service (legal-chat-service, port 8770) wraps
  claude CLI with stream-json + --resume continuation; FastAPI proxies
  via host.docker.internal. Zero API cost — uses chaim's claude.ai
  subscription. chat_conversations + chat_messages persist history.

Architecture: keeps the existing rule that claude_session only runs
on the host (not the container). The new legal-chat-service is the
canonical bridge between the container and the local CLI for the chat
feature; everything else (upload, metadata, lessons) stays within the
container's existing capabilities.

Audit script (scripts/audit_training_corpus.py) included for verifying
which corpus rows still need enrichment.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 10:06:22 +00:00
0629f19d5f ui(missing-precedents): drawer = notes + upload only
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m21s
The drawer was showing a full metadata form (legal topic, case name,
legal issue, cited-by-party + name, status) — most of it duplicated
fields that get auto-extracted from the file once it's uploaded, or
that are already known from when the row was detected. The visible
placeholder text ('לינדאב בע"מ', 'אנטרים', 'זכות עמידה') looked like
real data and confused readers.

Strip the form down to a single "הערות" textarea — that's the only
field the chair actually needs to edit. Reasons for who cited the
decision and in what context belong there too. Everything else (shape
of the precedent on the case_law side) is the LLM extractor's job.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 09:58:23 +00:00
f920cfc738 ui(precedents): edit sheet — make citation_formatted editable
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 46s
The "ערוך פרטים" sheet labeled the case_number field "מראה מקום" and
marked it read-only — confusing because the formal citation IS supposed
to be editable. Rename the read-only field to "מספר תיק (מזהה ייחודי)"
to clarify it's the system key, and add a separate Textarea for the
true formal citation (citation_formatted) with the same markdown-bold
convention used by the inline editor on the detail page.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 09:40:08 +00:00
c4046cc0a0 ui(precedents): citation action buttons icon-only
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 35s
Drop the visible "העתק" / "ערוך" labels and keep just the icon —
matches the editorial/judicial restraint of the surrounding card.
Tooltip + aria-label preserve the affordance for hover and assistive
tech.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 09:33:55 +00:00
cbc7a1e336 feat(precedents): formal citation per Israeli citation rules + copy/edit UI
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m25s
Until now, "case_number" was the only stored identifier for a precedent.
But a *citation per the Israeli unified citation rules* is a different
beast — it has bold parties, an unbold prefix (court abbrev + panel/
district parenthetical + case number), and an unbold trailing reporter
(נבו / פ"ד...).  Without storing it as a first-class field we couldn't
hand the chair a one-click "copy as citation" experience for pasting
into decisions.

Changes:
- Schema V19: case_law.citation_formatted TEXT (Markdown — parties
  wrapped in **…** so the copy helper can render <strong> for Word/Docs
  paste and keep plain-text fallback meaningful).
- Metadata extractor: composes citation_formatted from the document
  text per the unified citation rules, with worked examples for ע"א /
  עת"מ / ערר / בל"מ in the prompt. Refuses to store half-formed strings.
- PATCH /api/precedent-library/{id} accepts citation_formatted so the
  chair can correct LLM mistakes.
- /precedents/[id]: dedicated "מראה מקום" block with bold rendering,
  a copy-to-clipboard button (text/html + text/plain so Word keeps
  the bolds), and an inline edit textarea.
- /precedents list rows: link displays the formatted citation when
  available, with a small inline copy button — falls back to the bare
  case_number for older rows.

Backfill of existing rows happens by re-stamping the extraction queue
once V19 has rolled out and the new field is reachable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 07:14:34 +00:00
a02a4e3a64 feat(precedents): minimum-effort upload — file+citation, rest auto-extracted
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m35s
The missing-precedents drawer + general precedent upload both required
the user to type chair_name, district, practice_area, court, date etc.
upfront — even though those fields can be (and already are, post-upload)
extracted from the document text by the LLM. The metadata-extraction
wakeup also only fired for the /precedent-library/upload path, leaving
missing-precedents committee uploads stuck with whatever stub the user
typed.

Changes:
- Extractor learns chair_name + district, overwrites the new
  PLACEHOLDER_PENDING_EXTRACTION sentinel for internal_committee rows
  (the DB CHECK forces non-empty; we stamp the placeholder at insert).
- missing_precedent_upload no longer 400s on missing chair/district;
  it infers district from the citation when possible, falls back to
  the placeholder, and always fires pc_wake_for_precedent_extraction
  so the LLM can fill in the rest.
- Both upload sheets default to file (+ citation) only; every other
  field is tucked into a closed <details> labeled "אופציונלי — דריסה
  ידנית של שדות שיחולצו אוטומטית". Required validators on chair/
  district/practice_area dropped — the LLM fills them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 14:43:25 +00:00
b01722b1b4 feat: emit missing_precedent + export_complete webhooks to plugin
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 9s
Adds two webhook emitters in paperclip_api.py that the plugin's
onWebhook handler now routes by ``eventType``:

* ``emit_missing_precedent_webhook(...)`` — fires from
  POST /api/missing-precedents on first insert (non-duplicate).
  The plugin surfaces an askUserQuestions interaction on the
  linked issue so Daphna can choose upload / irrelevant / defer
  without needing to open the legal-ai UI.

* ``emit_export_complete_webhook(...)`` — fires from
  POST /api/cases/{n}/export-docx after a successful export. The
  plugin attaches a "final-decision" markdown document with a
  download link to the linked Paperclip issue.

Both are fire-and-forget BackgroundTasks — failures are logged
but never block the originating request. Company resolution
follows the same 1xxx→licensing / 8-9xxx→betterment rule used
by emit_case_status_webhook.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 13:29:04 +00:00
1d4f214abe chore(taskmaster): mark #26 + #27 done (Paperclip SDK upgrade + host already on 525)
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 7s
2026-05-26 12:19:16 +00:00
2aee398b4a feat: Stage C — RAG advanced (#33, #47, #48, #49, #50, #51)
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m35s
Six independent sub-tasks dispatched in parallel; aggregated here.

## #33 — Hide case_name column
library-list-panel.tsx: `<TableHead>` + `<TableCell>` for "שם"
get `className="hidden"` in both Court and Committee row variants.
DB column preserved for future use.

## #47 — Audit script periodic
New scripts/audit_corpus_integrity.py — 3 SQL checks (external+ערר
prefix, internal missing chair/district, cases.practice_area enum)
+ CEO wakeup on violations + cron `0 7 * * *`. First run: 0 issues.

## #48 — Parent-doc retrieval (gated, default off)
Schema V17: precedent_chunks.parent_chunk_id + chunk_role
('child'|'parent'). New chunker.chunk_document_hierarchical() —
section-aware parents (~1500 tokens) containing ~5 overlapping
children (~300 tokens each). New db.store_precedent_chunks_hierarchical
two-pass writer. Search SQL (semantic + lexical) LEFT-JOIN parent and
swap content + dedupe by parent_chunk_id when flag on. Toggle:
PARENT_DOC_RETRIEVAL_ENABLED + PARENT_DOC_{CHILD,PARENT}_SIZE_TOKENS.
Backfill ~3min and ~$0.20 — deferred to follow-up.

## #49 — Multimodal backfill
New scripts/backfill_multimodal_precedents.py with token-matching
case_number ↔ source files (PDF + DOCX via PyMuPDF). Ran in container:
26 precedents embedded, 503 pages, $0.21, 0 errors. precedent_image_embeddings
grew 3 → 29 rows. 44 remaining are style_corpus-migrated rows (no
source file on disk) — will catch up when re-uploaded.

## #50 — Closed-loop feedback + nDCG
Schema V18: search_logs + search_relevance_feedback. New telemetry.py
with fire-and-forget log_search_bg (p50 = 0.002ms — zero overhead) +
auto-infer_relevance_from_citations (reads case drafts → marks score=3
when cited precedent appears in past search top-K). Hooks added to 5
search paths. scripts/compute_ndcg.py for aggregation. Two admin API
endpoints (GET /api/admin/rag-metrics + POST .../infer). Dashboard UI
deferred — API is enough for now.

## #51 — Halacha quality monitoring
New scripts/monitor_halacha_quality.py — baseline avg confidence
(trusted=0.849, all=0.833, pending=0.694) with rolling window drift
detection. Default 5% threshold. Exits non-zero on alert for cron
integration. Recommended: `0 8 * * 1` weekly Mon 8am.

## Bonus: 230 unlinked citations → missing_precedents
Bulk-imported 230 distinct unlinked citations from
precedent_internal_citations to missing_precedents.status='open',
party='committee', with notes listing source citers. Top candidate:
ע"א 3213/97 (cited 5x). Total open missing_precedents now 237.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 11:26:52 +00:00
3a05e30c8d fix(appraiser-facts): route extraction through analyst wakeup (was silent 0)
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m38s
The "חלץ עובדות שמאיות" UI button hit POST /api/cases/{n}/extract-appraiser-facts
which called appraiser_facts_extractor inline — that shells out to the local
`claude` CLI, which is absent in the Coolify container, so every doc errored,
the per-doc try/except swallowed it, and the response was "completed, 0 facts".

Refactored the endpoint to wake the legal-analyst of the correct company via
Paperclip (same pattern as wake_curator_for_final), and surface
extraction_failed instead of "completed" when every doc errored.
2026-05-26 11:02:55 +00:00
7ad995aade feat: #34 citation graph + #32 wide-modal precedent edit + #13 verify
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m38s
## #34 — Daphna's internal citation graph

New schema V16 (V15 was already used by proceeding_type): table
``precedent_internal_citations`` (source→cited, with cited_case_law_id
nullable for citations whose target isn't in the corpus yet) + 3
indexes (source, target, unlinked).

New service ``citation_extractor.py`` with regex patterns for ערר /
בל"מ / עע"מ / בר"מ / עמ"נ / ע"א / בג"ץ / רע"א — accepts both ``\/``
and ``-`` separators, requires actual parenthesized district label
to avoid greedy mid-paragraph captures. Resolves citations against
``case_law.case_number`` substring; default confidence 0.90 linked,
0.75 unlinked. ON CONFLICT DO NOTHING on (source, cited_case_number).

3 new MCP tools: ``extract_internal_citations``,
``list_internal_citations``, ``list_incoming_citations``. Optional
flag ``include_cited_by=True`` on ``search_internal_decisions``
appends cited-by candidates as ``match_type='cited_by'`` stubs.

Bulk-extracted from 40 internal_committee rows authored by דפנה תמיר:
**353 distinct citations, 348 stored, 96 linked / 252 unlinked**.
Top citers: 1079/24 (30), 1024/24 (19), 1009/25 (18). Top unlinked
target: ע"א 3213/97 (cited 5x) — natural #35 candidates.

## #32 — Wide-modal precedent edit

`precedent-edit-sheet.tsx`: ``<Sheet side="left">`` → centered
``<Dialog>`` with ``sm:max-w-4xl`` ``max-h-[90vh]`` ``overflow-y-auto``.
Component API unchanged so existing callers
(`/precedents/[id]/page.tsx`, `library-list-panel.tsx`) work as-is.
RTL preserved. Mobile falls back to near-full-width via shadcn default.

## #13 — 403/17 verification

`case_law e151fc25-...` (אהרון ברק - תכנית רחביה) already in perfect
shape after Stage A work: all metadata fields populated, 351 halachot
with avg_conf=0.864 (well above 0.78 threshold). No re-extraction
needed; closing task as verified.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 10:37:53 +00:00
9f4f8c60a4 fix(labels): drop בל"מ prefix from extension_request_* subtype labels
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 35s
Now that proceeding_type drives a dedicated בל"מ badge, repeating the
prefix in the appeal_subtype label produced 'בל"מ רישוי' on the row
plus a בל"מ pill — double-marking. The extension_request_* values now
render as the same domain label as their non-extension siblings
(רישוי ובנייה / היטל השבחה / פיצויים), and the בל"מ pill is the
single source of truth for proceeding type.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 10:03:39 +00:00
d32452f95c fix(api): include proceeding_type in /api/cases list response
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 9s
The cases-table reads from the list endpoint, not /details, so without
proceeding_type in the row payload the בל"מ badge can't render for
cases that flipped the field manually (only the legacy
appeal_subtype LIKE 'extension_request_%' path was firing).

Added the field to both detail=false and detail=true branches.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 10:01:24 +00:00
ac3ed455cf fix(cases): בל"מ badge reads proceeding_type, not just appeal_subtype
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 43s
After the proceeding_type field landed, users started flipping cases
to בל"מ via the edit dialog. But the case-header badge + cases-table
filter were still gated on isBlamSubtype(appeal_subtype), so the badge
didn't appear when only the proceeding_type changed. Now the badge
shows when either proceeding_type === 'בל"מ' OR appeal_subtype is an
extension_request_* variant — the legacy path stays so existing rows
that never got a proceeding_type still render correctly.

Also regen types.ts from prod (proceeding_type now in OpenAPI schema)
and register the one-shot process_pending_blam.py script.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 09:34:23 +00:00
d359ab9884 feat(proceeding-type): explicit ערר/בל"מ field for cases + corpus
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m40s
Same case_number can exist as both a regular appeal (ערר) and an
extension-of-time request (בל"מ), and we were inferring the difference
from appeal_subtype prefixes — fragile, and case-number lookups
weren't disambiguated. Now stored as a first-class field on both
case_law (corpus) and cases (live cases), with partial unique indexes
on (case_number, proceeding_type).

- SCHEMA_V15: column + CHECK constraints + backfill from
  appeal_subtype LIKE 'extension_request_%' + partial unique indexes
  replace the old global UNIQUE(case_number).
- derive_proceeding_type() centralizes the inference rule
  (extension_request_* → בל"מ; subject regex fallback; default ערר).
- Metadata extractor prompt asks Claude to populate the new field
  explicitly; apply_to_record writes it for internal_committee rows.
- internal_decision_upload, case_create, case_update accept an
  optional proceeding_type; FastAPI request models expose it.
- Wizard + edit dialog get a sided Select; case header renders the
  resolved label (ערר / בל"מ).
- Uploaded the 2 staged בל"מ decisions on betterment levy:
  8126/24 (סופר נוח, 13 chunks), 8047/23 (הרנון, 48 chunks).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 09:17:33 +00:00
1645653ba9 chore(taskmaster): mark Stage A+B + #30/31/35/36/37 as done
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 26s
37/51 tasks done after the parallel sub-agent sprint:
- #30 closed (9/9 subtasks)
- #31 closed (3/3)
- #35 closed (6/6) — missing_precedents feature
- #36 closed (5/5) — legal_arguments aggregation
- #37 closed (5/5) — בל"מ subtypes
- #38, #39, #40, #41, #43, #44, #45, #46 done

Deferred: #42 (Haiku query expansion).
Pending: Stage C #47-51 + 3 UI smaller items (#32-34).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 08:36:02 +00:00
f3cc9ca9d4 feat: Stage A finalizers + #35/#36/#37 — critical-gap closure
Some checks failed
Build & Deploy / build-and-deploy (push) Has been cancelled
Four parallel sub-agents closed the remaining critical gaps from the
26/05 Stage A/B sprint. Each block independently tested; aggregated here.

## #30/#31 finalizers (sub-agent A)
* Auto-derive practice_area in case_create from case_number prefix
  (1xxx→rishuy_uvniya, 8xxx→betterment_levy, 9xxx→compensation_197);
  default for CaseCreateRequest is now "" (the DB constraint catches
  any stray "appeals_committee").
* practice_area.py: derive_subtype now handles axis-B domain values
  (rishuy_uvniya/betterment_levy/compensation_197) without parsing the
  case number; new helper derive_domain_practice_area().
* Halacha re-extraction verified unnecessary — all 6 reclassified
  records already had is_binding=false and approved halachot.
* Regression tests: 6 cases in tests/test_corpus_constraints.py
  covering practice_area enum, internal-committee chair/district,
  external-upload arar prefix, MCP guard.
* UI: district input → Select dropdown (7 districts) in
  precedent-edit-sheet.tsx, preserving legacy free-text values.

## #37 בל"מ subtypes (sub-agent B)
* 3 new appeal_subtypes: extension_request_{building_permit,
  betterment_levy,compensation}. APPEALS_COMMITTEE_SUBTYPES extended,
  SUBTYPES_BY_AREA mappings added.
* New helpers: is_blam_subject(), is_blam_subtype(),
  derive_subtype_with_blam(case_number, subject, practice_area).
  case_create now uses it to auto-detect "בקשה להארכת מועד" subjects.
* 3 methodology templates under docs/methodology/extension-request-*.md.
* paperclip_client.py mapping updated for the 3 new subtypes
  (extension_request_building_permit→CMP, the other two→CMPA).
* Frontend: bilingual "בל"מ" badge + filter dropdown on cases list +
  detail header; appeal-type-bars collapseBlam() merges בל"מ into its
  parent domain for aggregate bars.
* Wizard auto-detects בל"מ from subject during case creation.
* 3 Berlinger cases (1017/1018/1019-03-26) migrated to
  appeal_subtype=extension_request_building_permit via psql.

## #35 missing_precedents feature (sub-agent C)
* Schema V13: missing_precedents table (citation, case_id, party,
  legal_topic, status, linked_case_law_id, claim_quote, ...) +
  FK constraints + 3 indexes. Applied via psql + idempotent migration.
* 6 db.py service functions, 3 MCP tools, 6 FastAPI endpoints
  (POST/GET/PATCH/DELETE/upload — upload routes by citation prefix
  to ingest_internal_decision or ingest_precedent).
* Next.js page /missing-precedents with 5 status tabs + filters +
  sidebar badge counter + detail drawer with metadata edit + smart
  upload form that switches fields per committee/court.
* Bootstrap: 7 rows imported from the JSON file
  (3 citations × cases, all status=closed with linked_case_law_id).
* legal-researcher.md: new §2ב.5 with missing_precedent_create
  usage + dedup semantics + tool grant.

## #36 legal_arguments aggregation (sub-agent D)
* Schema V14: legal_arguments + legal_argument_propositions M:M.
  Applied via psql.
* New service argument_aggregator.py with two functions —
  aggregate_claims_to_arguments() (Claude CLI / claude_session) and
  get_legal_arguments(). Graceful llm_unavailable handling when CLI
  is missing (containers).
* 2 MCP tools + 2 API endpoints (POST .../aggregate-arguments as
  BackgroundTask, GET .../legal-arguments).
* Frontend: shadcn Accordion + new legal-arguments-panel.tsx with
  hierarchical (party → priority badge → arguments) display, "טיעונים"
  tab on the case page, "חשב/חשב מחדש" buttons.
* scripts/backfill_legal_arguments.py + SCRIPTS.md entry — dry-run
  found 8 candidate cases including 1017/1018/1019.

## Open follow-ups (intentionally deferred)
* npm run api:types in web-ui (CLAUDE.md flow) — recommended before
  the next UI commit; not required for backend deployment.
* Run backfill_legal_arguments.py --apply once the container picks up
  the new aggregator service.
* webhook on missing-precedents upload-close to Paperclip (optional).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 08:34:40 +00:00
af651d0135 feat(rag): Stage B — RAG improvements (HNSW + BM25 hybrid + MMR + dynamic boost)
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m35s
Five enhancements to the precedent retrieval stack:

* **#44 HNSW indexes** for precedent_chunks + halachot (replacing IVFFlat
  lists=50). Build time ~3s combined. Better recall@10 with pgvector 0.8.2.
* **#45 Halacha sweep** — 96 pending halachot at conf>=0.78 promoted to
  approved (1141 → 1237). Cluster at conf=0.78 spot-checked OK. Applied
  via psql only — env HALACHA_AUTO_APPROVE_THRESHOLD unchanged (0.80).
* **#43 MMR diversity** — search_precedent_library_hybrid now caps at
  ``max_per_case_law=2`` (default). Prevents one precedent dominating
  top-10 when many of its chunks/halachot rank high. New helper
  ``_diversify_by_case_law`` in hybrid_search.py.
* **#46 Dynamic halacha boost** — replaces the static ``score+=0.05``
  with ``score+=confidence*0.06``. Calibrated so avg-confidence (~0.85)
  stays at +0.05; high-conf halachot get a slight extra lift, low-conf
  ones get less. Behaviour preserved at the mean.
* **#41 BM25/tsvector hybrid + RRF**. Schema V12 adds STORED tsvector
  columns ``precedent_chunks.content_tsv`` and ``halachot.rule_tsv``
  (using simple config — Postgres has no Hebrew stemmer) + GIN indexes.
  New ``db.search_precedent_library_lexical`` mirrors the semantic
  function with ts_rank_cd over plainto_tsquery. ``hybrid_search``
  runs sem+lex in parallel and fuses via RRF before rerank. Toggle:
  env ``BM25_HYBRID_ENABLED`` (default true), graceful fallback to
  semantic-only on lexical failure.

#40 (VOYAGE_RERANK_ENABLED) was already true in Coolify env; no change.
#42 (Claude Haiku query expansion) deferred — latency + cost concerns
warrant a separate plan; the bm25 lexical leg already recovers most of
the exact-string recall #42 was meant to address.

Closes TaskMaster #41, #43-#46.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 08:08:02 +00:00
b197d2329c fix(corpus): move citation guard to service level
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m31s
Defense in depth — the MCP wrapper guard catches researcher uploads, but
the HTTP API (/api/precedent-library/upload) bypasses the wrapper and
calls services.precedent_library.ingest_precedent directly. The guard
now also lives in the service, so HTTP uploads of ערר/בל"מ citations
to the external corpus get rejected at the source.

Companion to DB constraint case_law_external_arar_check (applied via
psql) — three independent layers now enforce the same invariant.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 07:49:49 +00:00
c6e368e4f7 feat(corpus): Stage A — corpus tagging fixes + prevention layer
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m8s
מתקן את הבאג של תיוג שגוי לועדות ערר ומונע חזרתו:

**Code changes:**
* New MCP tool `internal_decision_upload` (chair_name+district required)
  — sole supported path for ingesting committee decisions; tags
  source_kind='internal_committee' automatically.
* Citation guard in `precedent_library_upload` rejects citations starting
  with "ערר" or "בל\"מ" with a directive to use internal_decision_upload.
* `practice_area.py` taxonomy unification: PRACTICE_AREAS now accepts
  both multi-tenant (appeals_committee/national_insurance/labor_law)
  and domain (rishuy_uvniya/betterment_levy/compensation_197) values.
  New helper `to_db_practice_area(multi_tenant, subtype) -> domain`.

**Agent docs:**
* legal-researcher (+5K): upload-tool decision flowchart, code samples
  per source_kind, district enum (ירושלים/מרכז/תל אביב/צפון/דרום/חיפה/ארצי)
* legal-ceo, legal-analyst, legal-writer, legal-qa, HEARTBEAT — taxonomy
  awareness + source_kind-aware citation patterns + research_complete
  as valid status.
* Fixed two pre-existing wrong practice_area values in examples
  (histael_hashbacha→betterment_levy, pitsuim_197→compensation_197).

Closes TaskMaster #30(parts), #38(parts), #39 (root cause).
DB-side backfill + CHECK constraints applied directly via psql:
* 11 cases.practice_area corrected (1xxx→rishuy, 8xxx→betterment)
* 6 case_law records reclassified external_upload→internal_committee
  with inferred district
* 6 chair_name backfilled from full_text (5 שרית אריאלי + 1 דפנה תמיר)
* 88 new halachot extracted for newly-uploaded precedents
  (אנטרים + ירושלים שקופה 1112/22 + אגא וכט)
* CHECK constraints: cases.practice_area enum, case_law internal⇒district

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 07:40:18 +00:00
8153bc9f03 fix(extractor): add regex fix for Hebrew law year gershayim corruption
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m36s
תש[א-ת]+יי[א-ת] → תש[א-ת]+"[א-ת]  (e.g. תשכייה → תשכ"ה)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 16:12:20 +00:00
4892fb6e8f fix(extractor): apply Hebrew quote fixer to direct PDF extraction path
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m40s
Born-digital Hebrew PDFs from legal software often encode gershayim (״)
as double-yod (יי), producing the same corruption patterns as OCR.
The fixer was only called after Google Cloud Vision OCR — digitally
created PDFs that passed quality checks received no correction.

Changes:
- Apply _fix_hebrew_quotes() in the direct extraction path
- Add 'בליימ' → 'בל"מ' (בקשה להארכת מועד — systematic corruption in 1017-03-26)
- Add 'תמייא' → 'תמ"א' (תכנית מתאר ארצית)
- Update docstring to reflect the broader scope

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:59:39 +00:00
b368bce690 fix: handle invalid date formats gracefully and add missing dialog descriptions
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 4m14s
- Wrap date.fromisoformat() in try/except in case_update tool — prevents
  unhandled ValueError from surfacing as 500; FastAPI now catches it as 422
- Add DialogDescription (sr-only) to 5 dialogs missing aria-describedby:
  documents-panel preview + delete, drafts-panel delete + feedback, link-related-dialog

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:53:01 +00:00
1496e520fd feat(precedent-library): add district and chair_name to edit form
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m11s
Fields existed in DB and Precedent type but were missing from:
- PrecedentUpdateRequest (backend model)
- update_case_law allowed set (db layer)
- PrecedentPatch (frontend type)
- precedent-edit-sheet form state, inputs, and patch payload

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 12:16:43 +00:00
1da2a9a2cb fix: exclude archived cases from stale-case-reminder
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 8s
Archived cases have archived_at IS NOT NULL — they are not "stuck",
they are done. The stale query was missing this filter.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 16:41:38 +00:00
f3ecccd4f0 docs: add procedural patterns layer (interim decision template)
Document new daphna-procedural-patterns.md cataloging the
"appraiser clarification request" interim-decision pattern observed in
8174-24 — structure only, not phrasing (case is an outlier example).

- daphna-decision-tree.md §0.5: gating question before main tree
- legal-ceo.md voice docs table: register procedural patterns doc
- legal-writer.md: mandatory consultation when pattern_tag is set,
  with explicit warning against copying 8174-24 wording

Approved via interaction request_confirmation (CMPA-15) 2026-05-17.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 16:29:58 +00:00
a2fc36d65f fix: recognize extended chair-position placeholders as empty
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m35s
The legal-analyst agent was generating a longer placeholder form
[ימולא ע"י יו"ר הוועדה — עמדה/הנחיה לגבי סוגיה זו שתשמש את סוכן הכתיבה]
which _is_placeholder() did not match (substring check fails because ] is
further along in the longer form). Result: UI showed "✓ עמדה נקבעה" (green)
for all 4 issues even though no chair direction had been entered.

Fixes:
1. research_md.py: add regex fallback — any text starting with [ימולא is a placeholder
2. legal-analyst.md: template now emits the standard short placeholder only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:59:13 +00:00
653f441e99 docs: update agent audit report — mark all 12 issues resolved
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 7s
- עדכון טבלת מצב: כל המודלים מסונכרנים (instructions = DB)
- החלפת טבלת בעיות בטבלת סטטוס תיקונים עם commit references
- הוסף טבלת שינויים נוספים מהסשן
- הערה: Skills CMPA=6 עיצוב מכוון, verify מאשר "0 need sync"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:57:54 +00:00
c3ce0e7e1f upgrade: upgrade opus-4-6 → opus-4-7 for all heavy-reasoning agents
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 8s
DB: עדכון 8 סוכנים (CMP + CMPA) — CEO, מנתח, כותב, מגיה
instructions: עדכון 4 קבצי הנחיות להתאמה ל-DB

opus-4-7 מחליף opus-4-6 לכל הסוכנים שדורשים reasoning כבד.
sonnet-4-6 נשאר ל-QA, חוקר, מייצא. deepseek-v4-pro נשאר לcurator.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:42:33 +00:00
1608ea5ed0 fix: medium/low audit items — model drift, placeholders, corpus check, curator ownership
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 7s
Model drift (instructions → match DB):
- CEO: claude-sonnet-4-6 → claude-opus-4-6 (DB runs opus; CEO needs opus quality)
- מנתח/כותב/מגיה: claude-opus-4-7 → claude-opus-4-6 (DB runs 4-6; no 4-7 in adapter)

legal-proofreader.md:
- {issue-id} placeholder → $PAPERCLIP_TASK_ID בשני המקומות (done + blocked)

legal-researcher.md:
- הוסף reference ל-HEARTBEAT.md בראש הקובץ

legal-qa.md:
- הבהרת שיטת בדיקת corpus_queries_logged: grep ידני בלבד, לא validate_decision

CLAUDE.md (curator):
- הוסף תהליך אישור הצעות curator: comment → חיים מאשר → commits ל-SKILL.md/lessons.md

maxConcurrentRuns CEO: כבר 2 ב-DB — לא נדרש שינוי

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:35:49 +00:00
35423eafc1 fix: high-priority agent audit items — CEO hardcoded IDs + researcher search_internal_decisions
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 8s
CEO (legal-ceo.md):
- הסרת company UUID ו-project UUID קשוחים בדוגמת יצירת issue
- שימוש ב-$PAPERCLIP_COMPANY_ID לחברה
- project_id נשלף דינמית מה-issue ההורה דרך $PAPERCLIP_TASK_ID

researcher (legal-researcher.md):
- הוסף mcp__legal-ai__search_internal_decisions לרשימת tools
- הוסף סעיף 2ב.2א המסביר את ההבדל: search_decisions = דפנה בלבד;
  search_internal_decisions = כל ועדות הערר בכל המחוזות
- הוראות מתי להשתמש + אזהרת היררכיה (ועדת ערר < מחוזי)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:29:47 +00:00
a584dc3602 fix: legal-exporter — versioning, dynamic skill path, case status update
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 8s
- טיוטה-V → טיוטה-v (lowercase) בכל המקומות (שלב 4 + כללים קריטיים)
- hardcoded CMP UUID בנתיבי legal-docx SKILL → $PAPERCLIP_COMPANY_ID (תומך CMP + CMPA)
- הוסף case_update לרשימת tools
- הוסף שלב 4.5: עדכן סטטוס תיק ל-exported אחרי שמירת DOCX

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:14:24 +00:00
d37d03f478 docs: add comprehensive agent audit 2026-05-17
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 8s
7-agent parallel audit of all Paperclip agents (CEO, analyst,
researcher, writer, QA, exporter, proofreader, curator).

Found 12 issues including 3 critical:
- Exporter: V vs v naming mismatch in DOCX versioning
- Exporter: case.status not updated to exported after export
- Researcher: section ז missing from case 8174-24

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 11:52:32 +00:00
011555fb78 docs: update CLAUDE.md — webhook pipeline, scheduled jobs, paperclip_api.py
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 8s
- Document emit_case_status_webhook flow and plugin integration
- Document stale-case-reminder and weekly-feedback-analysis jobs
- Fix paperclip_api.py vs paperclip_client.py (both exist, api.py is current)
- Add warning: weekly-feedback-job CEO has no issueId

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 11:23:47 +00:00
ea0532b7ba fix: weekly-feedback-job handler writes to file only (no Paperclip issue)
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m39s
CEO wakes for weekly-feedback-job via agents.invoke without issueId,
so $PAPERCLIP_TASK_ID is empty. Removed steps 4-5 (comment + close
issue) from handler — now file-write only with stdout logging.

Also commits pending docs and agent instructions from prior session.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 11:08:14 +00:00
cddc7c8d24 fix: start-workflow wakeup failure now returns 502 instead of silent success
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m33s
If pc_wake_ceo fails, the endpoint now raises HTTP 502 and skips the
case_update to processing — preventing cases from silently getting stuck
with no CEO running. Also adds `processing` to CEO routing table and
updates case_list docstring with full status list.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 11:02:30 +00:00
83b6ff51b7 feat: fix wizard step-skip bug + extend case edit with all fields + Paperclip title sync
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m38s
- Fix keyboard navigation bug: React was reusing the submit button DOM element
  when transitioning "הבא" → "צור תיק", retaining focus and causing Enter to
  auto-submit step 3. Added key props to force element replacement.

- CaseEditDialog now covers all wizard fields: appellants, respondents,
  property_address, permit_number (in addition to existing title, subject,
  hearing_date, expected_outcome, notes).

- When case title changes, Paperclip project name is updated in background
  via new update_project_name() in paperclip_client.py.

- Extended CaseUpdateRequest, case_update MCP tool, and caseUpdateSchema
  to carry the new fields end-to-end.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 10:55:45 +00:00
8dc7a40fa2 fix: exclude exported cases from stale; add weekly-feedback-job handler to CEO
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 8s
- /api/cases/stale: exclude 'exported' status — exported cases await Dafna's
  review intentionally, they are not stuck
- legal-ceo.md: add routing for weekly-feedback-job reason + explicit handler
  (analyze feedback, update decision-lessons.md, close issue)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 10:35:39 +00:00
a3468d5b2f fix: use timezone-aware datetime in webhook timestamp
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m17s
Replace deprecated datetime.utcnow() with datetime.now(timezone.utc)
to avoid Python 3.12+ DeprecationWarning.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 10:15:52 +00:00
5f43659b5a fix: add defensive JSON parsing in check_instructions 2026-05-16 17:53:42 +00:00
86734da210 feat: add --check-instructions, pre-flight validation, and mtime tracking to sync script
- P3-T1: --check-instructions flag + check_instructions() prints a table of all
  agents' instructionsFilePath with status ( OK /  MISSING / ⚠ NOT SET),
  size, mtime, and ⚠ DRIFT when file has changed since last sync
- P3-T2: --apply now runs a pre-flight check on master agents and aborts if any
  instruction file is missing, before touching the DB or calling any API
- P3-T3: get_claude_md_mtime() helper; --apply stamps claude_md_mtime and
  claude_md_last_synced into each mirror agent's metadata via the PATCH call
- P3-T4: alias check-agents added to ~/.bashrc

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 17:51:34 +00:00
82ded005a4 fix: add days>0 guard and limit param to stale/feedback endpoints
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 8s
2026-05-16 17:38:34 +00:00
c7ed1110f8 feat: add /api/cases/stale and /api/chair-feedback/weekly-summary endpoints
GET /api/cases/stale?days=N — returns cases not updated in N days (default 3)
  that are not in 'final' or 'new' status, with days_stale count.
GET /api/chair-feedback/weekly-summary?days=N — returns chair feedback from
  the last N days (default 7) as a Hebrew bullet-list summary for CEO agent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 17:36:12 +00:00