Commit Graph

692 Commits

Author SHA1 Message Date
db6bad5d1e Merge pull request 'feat(halacha): review-queue triage — defer + batch + quality-flag badges (#84)' (#52) from feat/halacha-review-triage into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m41s
2026-06-03 13:42:53 +00:00
eeb70a5758 feat(halacha): review-queue triage — defer + batch group actions + quality-flag badges (#84)
Make the chair's pending-halacha review faster and less exhausting.

Backend:
- New 'deferred' review_status (snooze): stays out of the active library AND
  out of the default pending queue, without the finality of 'rejected'.
  update_halacha stamps reviewer+reviewed_at on defer; HALACHA_REVIEW_STATUSES
  is the single source of valid statuses (PATCH validation now uses it).
- db.update_halachot_batch(ids, status, reviewer) — one atomic UPDATE for a
  whole group; invalid status / empty ids are a no-op.
- POST /api/halachot/batch (HalachaBatchReviewRequest) wraps it.
- update_halacha now RETURNs quality_flags too (parity with list_halachot).

Frontend (halacha-review-panel):
- Quality-flag badges (#81: non_decision / truncated_quote / thin_restatement /
  quote_unverified) so the chair sees WHY an item was held back.
- Defer action — button + keyboard 'D' — to snooze without rejecting (fixes the
  'leave in pending forever' anti-pattern; reject stays the junk verb).
- Per-precedent batch bar: 'אשר הכל' / 'דחה הכל' via useBatchReviewHalachot
  (one request, one refetch) with confirm guards.
- Halacha/HalachaPatch types gain quality_flags + 'deferred'.

Verified: mcp-server suite 156 passed; web build green; end-to-end integration
against dev DB (batch approve/reject, defer sets status+timestamp, pending
excludes approved+deferred, deferred queryable, invalid status no-op).

Note: api:types regen deferred until deploy (the batch hook is hand-typed, not
dependent on generated types).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 13:42:21 +00:00
7ebddcce6d Merge pull request 'feat(halacha): UNIQUE(case_law_id, halacha_index) backstop (#83)' (#51) from feat/halacha-unique-index into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m37s
2026-06-03 13:07:30 +00:00
0f64b4c062 feat(halacha): UNIQUE(case_law_id, halacha_index) backstop + task tracking (#83)
#83 pipeline robustness — the index-numbering correctness guarantee:
- Add CREATE UNIQUE INDEX idx_halachot_unique_index ON halachot(case_law_id,
  halacha_index). The extractor assigns the index as MAX+1 under an in-process
  store-lock + a cross-process pg advisory lock, so collisions shouldn't occur
  in normal operation — but per the research (FireHydrant/OneUptime) the
  constraint is the actual correctness guarantee while the lock is the
  optimization. A racing/double run now fails LOUDLY (UniqueViolation, chunk
  left un-checkpointed → clean resume) instead of silently appending the
  duplicates that were the 2026-05/06 over-extraction root cause.

Data prep (run against the live DB before the constraint, backed up to
data/audit/halacha-reindex-backup-*.sql): the 6 precedents that still carried
colliding halacha_index values (9 groups, distinct principles that shared a
number — NOT content dups) were renumbered to unique sequential indices.

Verified: advisory lock holds cross-process and the DB path is direct asyncpg
(no transaction-pooler), so the session lock is safe (83.1); force=True does
delete+checkpoint-clear in one transaction (83.5); constraint rejects a
duplicate-index insert (integration-checked). Full suite 156 passed.

Also commits the TaskMaster tracking for the whole halacha-quality initiative
(#81-#84 + research-backed subtasks, statuses).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 13:06:58 +00:00
8e3d14abee Merge pull request 'feat(halacha): strict-rubric quality gate + dedup-on-insert (#81,#82)' (#50) from feat/halacha-quality-gate into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m36s
2026-06-03 12:31:27 +00:00
ca959d4a9c feat(halacha): strict-rubric quality gate + dedup-on-insert (#81,#82)
Bake the 2026-06-03 strict-cleanup rubric into the extraction pipeline so the
corpus stays clean at the source instead of accumulating duplicates, obiter
dicta, truncated quotes and thin restatements that clog the review queue.

#81 — quality gate:
- New pure module halacha_quality.py with unit-tested validators:
  non-decision/obiter (Wambaugh markers), truncated-quote (mid-word cut),
  thin-restatement (rule≈quote), quote-unverified.
- Validators run in halacha_extractor._process; a non-decision is re-typed
  obiter; flags persist in new halachot.quality_flags column.
- Auto-approve now requires confidence>=threshold AND no quality flags;
  flagged items route to pending_review regardless of confidence.
- Both extraction prompts hardened: reject undecided dicta, exclude
  case-specific applications, require abstraction, forbid over-splitting.

#82 — dedup-on-insert (store_halachot_for_chunk):
- Within the same precedent, skip a halacha whose normalized supporting_quote
  already exists, or whose rule-embedding has cosine>=HALACHA_DEDUP_COSINE
  (0.93) against an already-stored one. Makes re-runs idempotent.

Migration: halachot.quality_flags TEXT[] (additive, idempotent ALTER).
Tests: 19 new unit tests; full suite 156 passed. Validated end-to-end against
dev DB (dedup skips dups, flag blocks auto-approve, re-run inserts 0).
Calibration: flags fire on only ~10% of current survivors (low false-positive).

Spec: docs/halacha-strict-rubric.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 12:30:38 +00:00
b0ec24a9d5 Merge pull request 'chore(#80): backfill 8070-25 → appraisal multimodal 12/12; close #80' (#49) from chore/80-multimodal-appraisal-coverage into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 7s
2026-06-03 09:46:44 +00:00
f5d14fd6b8 chore(#80): backfill 8070-25 -> appraisal multimodal coverage 12/12; close #80
Full check found the premise wrong on every count (like #71/#70):
- Not 140 docs/17,700 pages/2hr/$$ needing Dafna+chaim. Of 140 image-less
  docs, only 65 are PDF (rest MD/DOCX — pipeline renders PDF only) = 704 pages.
- The value docs (appraisal, where multimodal's table/image worth is) were
  already 8/12 embedded. The only gap was ONE case, 8070-25 (4 appraisal docs).
- Backfilled 8070-25 locally (voyage-multimodal-3, ~30s, cents): all 14 docs
  embedded. Appraisal coverage now 12/12 (100%).
- Remaining 51 PDFs/649 pages are all text-dense (reference/response/appeal);
  #15 proved multimodal does NOT help text-dense docs, so they're intentionally
  left text-only. Not an inconsistency — the correct config.

No gold-set / Dafna labeling / chaim cost approval needed — cost was cents and
value was already proven in #15. #80 done (technical, not human-gated).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 09:46:23 +00:00
bbe3db7b94 Merge pull request 'chore(#70): delete 15 orphaned cited_only stubs + close #70' (#48) from chore/70-orphan-stub-cleanup into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 8s
2026-06-03 09:38:51 +00:00
7d0d4a9b27 chore(#70): delete 15 orphaned cited_only stubs + close #70
The 4 'ambiguous' citation items flagged for chair turned out to be dead
orphan stubs: 0 inbound/outbound edges across all 5 citation mechanisms,
0 full_text, 0 halachot, 0 chunks/embeddings. A corpus-wide check found 15
such orphans total (incl. clean-looking ones). Per OpenCitations (keep an
id-less entity only if it is CITED — these are cited by nothing), these are
pure noise → deleted, not chair-judgment.

- 15 orphan cited_only stubs deleted (cited_only 46 -> 31); backup in
  data/audit/fu2b-orphan-stub-cleanup-*.json.
- 0 malformed / 0 orphans remain; all 31 remaining stubs are cited.
- Combines with the 3 earlier mechanical normalizations. #70 fully done.
- Known forward-edge (no current data, no task): '+' combined-citation
  handling in citation_extractor if it recurs in future extraction.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 09:38:30 +00:00
61dde4cd83 Merge pull request 'chore(tasks): research-backed decisions — close #71/#42/#14/#76 + #70 normalization' (#47) from chore/close-open-tasks-research-decisions into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 8s
2026-06-03 09:10:02 +00:00
2a9168a1b4 chore(tasks): research-backed decisions to close open tasks (#71/#42/#14/#76/#70)
Per chaim's directive — for decisions not requiring Dafna/chaim, decide after
>=3 authoritative open sources.

#71 DONE — resolved by #15's weight fix (measured: all multi-relevant docs now
  in top-10, the rank-15/16 weak queries fixed). Research (6 sources) said
  enable rerank; tested empirically → it HURT (nDCG@5 0.879 vs 0.960, MRR 0.867
  vs 0.954) because recall is saturated and the cross-encoder demotes exact
  known-item matches. Measurement overrides theory: no rerank, no limit change.
#42 CANCELLED — obviated by BM25 hybrid (already on; handles abbreviation
  tokens lexically); 0 abbrev queries in eval, recall ~0.99, no measured gap.
#14 DEFERRED (reviewed) — no current blocker; YAGNI; trigger documented.
#76 CANCELLED — upstream Paperclip bug (ee=companyId), not safely fixable our
  side; workaround + #78 documented.
#70 — research-backed normalization (ECLI/Akoma Ntoso/ELI/OpenCitations +
  Christen). Applied 3 deterministic mechanical fixes to cited_only (whitespace
  + missing prefix-space); 0 malformed remain. 4 ambiguous items (2 garbled,
  'ערר אדלר', 1 combined citation) flagged for chair — NOT auto-guessed, per
  the entity-resolution false-merge guardrail.

#80 stays pending — human-gated (Dafna value-labeling + chaim cost).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 09:09:30 +00:00
5a00a0ef47 Merge pull request 'chore(#15): adopt MULTIMODAL_TEXT_WEIGHT=0.65 + close #15, open #80' (#46) from chore/15-multimodal-weight-065 into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 7s
2026-06-03 08:45:29 +00:00
4debe9995b chore(#15): adopt MULTIMODAL_TEXT_WEIGHT=0.65 + close #15, open #80
A/B eval (eval_retrieval.py, 86-query gold-set) showed the 0.5 default was
mis-tuned: the image side was too heavy and dragged precedent_library recall
0.971 -> 0.885. Sweep 0.5..0.75 — at 0.65 multimodal beats text-only on every
overall metric AND every corpus (R@5 0.994 vs 0.989, nDCG@5 0.960 vs 0.944,
MRR 0.954 vs 0.936). Dafna approved.

- MULTIMODAL_TEXT_WEIGHT=0.65 set in Coolify (legal-ai, runtime) + redeploy.
- baseline.json updated to the 0.65 config (future regression reference).
- #15 done (premise was stale — multimodal already default on 110 docs; the
  win was tuning the weight, not the backfill).
- #80 opened: the costly 140-doc legacy backfill is deferred until a targeted
  image-answer gold-set proves the table/image value prop (untested here).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 08:45:06 +00:00
bb42aeeff4 Merge pull request 'fix(#79): chunker never emits sub-50-char fragment chunks (#55 follow-up)' (#45) from fix/79-chunker-no-tiny-fragments into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m38s
2026-06-03 08:10:39 +00:00
6fcfdc76db fix(#79): chunker never emits sub-50-char fragment chunks (#55 follow-up)
A section that opens with a short header line ('דיון', 'טענות המשיבים')
followed by a paragraph larger than chunk_size flushed the header alone as a
tiny chunk. #55 added a query-time >=50 filter to hide these; this removes
them at the source.

_split_section: (1) don't flush a buffer still below MIN_CHUNK_CHARS — let it
absorb the next paragraph even if that overflows chunk_size, so a short header
rides with its following content; (2) fold a trailing tiny chunk back into its
predecessor.

Verified: re-chunked the 4 corpus docs that still had a tiny chunk
(ע"א 5138/04, בר"מ 2340/02, בג"ץ 6525/15, 403-17) — corpus-wide chunks<50
went 4 -> 0; all 4 stay embedded/searchable and rank top in a relevant search
(נווה שלום #1 for the s.19(ג)(1) exemption query). No regression.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 08:10:10 +00:00
0a88bed58b Merge pull request 'chore(tasks): #79#55 follow-up (isolated section-heading chunks)' (#44) from chore/task-79-chunker-followup into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 7s
2026-06-03 07:59:26 +00:00
d4046c2fbd chore(tasks): #79#55 follow-up for isolated section-heading chunks
Discovered closing #57: the current chunker still emits 4 tiny chunks that
are standalone section headings ('דיון', 'טענות המשיבים', ...). Low priority
— filtered at query time, search unaffected. Proposed fix: anchor a short
isolated heading forward into the following section.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 07:58:54 +00:00
f74fa13146 Merge pull request 'chore(#57): re-chunk+re-embed legacy precedents (pre-#55 remediation)' (#43) from chore/57-rechunk-legacy-precedents into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m11s
2026-06-03 07:56:12 +00:00
434341cc29 chore(#57): re-chunk+re-embed legacy precedents (pre-#55 chunker remediation)
Adds scripts/rechunk_legacy_precedents.py: selects every case_law with a tiny
chunk (content<50 — the pre-fix chunker fingerprint) and runs
ingest.reindex_case_law (re-chunk+re-embed from stored full_text only, no
re-OCR/LLM, idempotent). Batch-idempotent (re-queries the affected set).

Run result (2026-06-03): 73 precedents reindexed, 0 failed. Tiny chunks
483 -> 4 (99.2%); total precedent_chunks 5019 -> 3115 (fragments merged).
Search verified healthy (substantial coherent passages, no errors).

The 4 residual tiny chunks are isolated section headings ('דיון',
'טענות המשיבים', ...) emitted by the CURRENT (fixed) chunker — not legacy
fragments — and are already filtered at query time (>=50, #55). Minor
chunker edge case, candidate #55 follow-up.

The DB chunk migration is already applied to prod; this commit is the script
+ SCRIPTS.md entry only (no app code change, no deploy needed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 07:55:42 +00:00
c7c6f3eb9c Merge pull request 'chore(tasks): #77+#78 done; #76 deferred with root-cause' (#42) from chore/tasks-76-78-status into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 10s
2026-06-02 12:28:02 +00:00
76fae77393 chore(tasks): #77+#78 done; #76 deferred with root-cause diagnosis
#78 (committee-upload wakeup) + #77 (case_number identity) shipped.
#76 (Paperclip create-task button): root-caused to ee=companyId guard —
button enabled on title only but submit requires a company; not safely
patchable via injection. Deferred with workaround + upstream note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 12:27:45 +00:00
901ec9f869 Merge pull request 'fix(#77 frontend): separate מספר-תיק field on committee upload + editable case_number' (#41) from fix/77-precedent-identity-frontend into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 38s
2026-06-02 12:17:35 +00:00
7be1c3162c fix(#77 frontend): separate מספר-תיק field on committee upload + editable case_number in edit sheet
Pairs with the backend PR. Stops the citation (מראה-מקום) from being stored
as the identifier, and lets a wrong identifier be corrected after the fact.

- upload sheet: new required 'מספר תיק (מזהה ייחודי)' field for committee
  decisions → sent as case_number; the citation field is now sent as the
  separate citation (→ citation_formatted) instead of as case_number.
- edit sheet: the case_number block is now an editable input (was read-only).
  Halachot/chunks key off case_law_id (UUID), so renaming case_number is safe.
- precedent-library.ts: InternalDecisionUploadInput += citation; PrecedentPatch
  += case_number.
- types.ts: regenerated (api:types) — PrecedentUpdateRequest now carries
  case_number.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 12:17:16 +00:00
9295e74762 Merge pull request 'fix(#77 backend): editable case_number + separate citation field on committee upload' (#40) from fix/77-precedent-identity-backend into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 8s
2026-06-02 12:09:59 +00:00
fc0c36b2f8 fix(#77 backend): make case_number editable + separate citation field on committee upload
Two identity fixes for the precedent corpus:
1. PrecedentUpdateRequest += case_number — the canonical identifier was not
   in the edit model, so a wrong id captured at upload (e.g. the full
   citation pasted into the field) could not be corrected. update_case_law
   already whitelists case_number.
2. /api/internal-decisions/upload += citation form field — case_number is
   now the clean identifier (e.g. 8027-25) and citation is the full
   מראה-מקום, stored as citation_formatted up-front (previously the UI sent
   the citation AS case_number, leaving the id polluted and citation_formatted
   empty until extraction). Stored via a post-ingest update_case_law, not the
   core INSERT.

Frontend (separate case_number field in the upload + edit sheets) follows in
a second PR after api:types regen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 12:09:40 +00:00
2d7ab26c71 Merge pull request 'fix(#78): trigger extraction wakeup on committee-decision upload + surface silent failures' (#39) from fix/78-precedent-extraction-wakeup into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 10s
2026-06-02 12:06:56 +00:00
1d3e235556 fix(#78): trigger extraction wakeup on committee-decision upload + surface failures
The /api/internal-decisions/upload path (used by the UI for ועדת-ערר
decisions) never called pc_wake_for_precedent_extraction, so committee
decisions were stuck at halacha_extraction_status='pending' forever — the
CEO was never woken to drain the queue. Root cause behind 8027-25's stuck
extraction. The other two upload paths (precedent_library, missing-precedent)
already wake the CEO; this one was missing it.

- internal-decisions upload: add the wakeup, routing the company by case
  number prefix (1xxx→רישוי, 8xxx→היטל, 9xxx→פיצויים) when practice_area is
  empty (else an 8xxx case wrongly routes to the licensing CEO).
- all three call sites: the wake helper returns {ok:False} WITHOUT raising
  on a skipped/failed wakeup; that was silently dropped. Now logged at
  WARNING with the reason, and the upload progress carries extraction_queued.

Fallback drainer (scheduled precedent_process_pending) deferred — the
missing wakeup was the actual failure; manual precedent_process_pending
remains the recovery path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 12:06:31 +00:00
7471dcf3cc Merge pull request 'chore: tasks #76-78 + weekly chair-feedback lessons #34-35' (#38) from chore/tasks-and-weekly-lessons into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 8s
2026-06-02 11:57:44 +00:00
d790fb26e0 docs(lessons): weekly chair-feedback lessons #34-35 (week ending 2026-05-31)
#34 don't manufacture doubt about unambiguous statutes (s.19(ג)(2));
#35 writer/QA two-sources-of-truth sync gap (DB vs drafts/decision.md).
Output of the weekly-feedback-analysis job, pending commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 11:57:24 +00:00
7e34c53224 chore(tasks): add #76-78 — Paperclip create-task button + 2 precedent-upload bugs
#76 צור-משימה button (enabled but submit no-ops), #77 committee-upload
field mapping (citation→case_number, case_number uneditable), #78 silent
extraction wakeup failure. Discovered while debugging precedent 8027-25.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 11:57:24 +00:00
77ed0361b7 Merge pull request 'fix(appraiser-facts): valid Paperclip priority enum (normal→medium)' (#37) from fix/appraiser-facts-priority-enum into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m18s
2026-06-02 11:49:23 +00:00
5d63a903ce fix(appraiser-facts): valid Paperclip priority enum (normal→medium)
The 'חלץ עובדות שמאיות עכשיו' button returned HTTP 500. Root cause:
wake_analyst_for_appraiser_facts POSTs a child issue to Paperclip with
priority='normal', but Paperclip's ISSUE_PRIORITIES enum is only
critical|high|medium|low. createChildIssueSchema (Zod) rejects 'normal'
with 400 Bad Request; pc_request raise_for_status() turns it into a 500
surfaced to the chair. Fixed to 'medium' (the sole non-normal occurrence
in the repo).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 11:48:58 +00:00
aeddcb41eb Merge pull request 'feat(web-ui): sort corroborated halachot first' (#36) from feat/x11-corroborated-first into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 36s
2026-06-01 05:50:29 +00:00
1aadd3b455 feat(web-ui): sort corroborated halachot first in extracted list (X11)
Halachot carrying a corroboration badge (positive citation count or a
negative treatment) float to the top of 'הלכות שחולצו', ordered by
corroboration strength; the rest keep document order by halacha_index.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 05:50:12 +00:00
f66a2a27e7 Merge pull request 'feat(web-ui): X11 corroboration badge on halachot' (#35) from feat/x11-corroboration-web-ui into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m35s
2026-06-01 05:04:58 +00:00
f46bf47d5b feat(web-ui): expose citation-corroboration badge on halachot (X11)
- db.list_halachot: aggregate corroboration_count (distinct positive sources)
  + corroboration_negative from halacha_citation_corroboration (LEFT JOIN)
- web-ui: CorroborationBadge — 'מתוקף · N ציטוטים' at ≥2 (gold), soft single
  citation, danger badge on negative treatment; native title tooltips
- shown in ExtractedHalachotSection (per-precedent) + halacha review panel

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 05:04:31 +00:00
9f2adc4dd0 Merge pull request 'docs(X11): wire corroboration tools into CEO flow + user guide' (#34) from docs/x11-phase2-tool-integration into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 8s
2026-06-01 04:52:25 +00:00
e79f74bc23 docs(X11): wire corroboration tools into CEO halacha flow + guide (X11 Phase 2)
- CEO: run corroboration_rebuild after precedent_process_pending(halacha);
  report {approved, demoted}; tools added to allowlist
- researcher: halacha_corroboration (read) in allowlist
- TaskMaster #75 → done

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 04:52:02 +00:00
3bd2d16652 Merge pull request 'feat(X11): citation-corroboration Phase 2 — wire the approval gate + backfill' (#33) from feat/x11-corroboration-phase2 into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m35s
2026-06-01 04:43:24 +00:00
b4d1fc5539 docs(audit): X11 Phase 2 corroboration backfill result (X11 Phase 2)
12 precedents, 20 links, 0 negatives. 4 halachot corroborated — all already
confidence-approved (signal fully overlaps confidence set), so 0 transitions.
Approve path proven in rolled-back tx; no chair-final state touched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 04:41:58 +00:00
ed547e20ad feat(corroboration): wire approval gate + backfill driver + rebuild tool (X11 Phase 2)
- db: approve_halacha_by_corroboration (pending_review→approved only),
  demote_halacha_overruled (approved→pending_review only), list_corroboration_grouped,
  precedents_with_halachot_and_incoming_citations
- corroboration: reconcile_approvals (INV-COR2/COR4/COR5), build_all backfill;
  build_for_precedent now returns approved/demoted counts
- mcp: corroboration_rebuild write tool (single precedent or full-corpus backfill)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 04:35:37 +00:00
df007784c9 feat(corroboration): approval_action decision fn + kill-switch (INV-COR2/COR4, X11 Phase 2)
- HALACHA_CORROBORATION_AUTO_APPROVE config (default ON, Dafna validated 2026-06-01)
- approval_action(agg, has_overruled): overruled→demote, corroborated→approve, else None
- 4 offline unit tests; Phase 2 plan + TaskMaster #75

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 04:34:23 +00:00
391b025e8a Merge pull request 'feat(halacha): effort קל-יותר לחילוץ-bulk (מהירות בקנה-מידה)' (#32) from feat/halacha-bulk-effort into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m36s
2026-05-31 21:34:44 +00:00
885cba543e feat(halacha): lighter effort for BULK queue-drain extraction (speed at scale)
xhigh is the quality sweet-spot for a single precedent but very slow at scale
(64-chunk case ≈ 20 min). Bulk queue-drains (process_pending over many
precedents) now use a lighter effort to cut wall-clock; interactive single
re-extraction keeps xhigh quality.

- config.HALACHA_BULK_EXTRACT_EFFORT (env, default 'high'; set 'medium' for max
  speed, 'xhigh' to match single).
- extract()/_extract_impl()/_extract_chunk() take an `effort` override threaded
  to claude_session.query_json; None falls back to HALACHA_EXTRACT_EFFORT (xhigh).
- process_pending_extractions(kind='halacha') passes the bulk effort; single
  reextract_halachot keeps xhigh.

Verified end-to-end (mocked LLM): _extract_chunk(effort='medium') → query_json
effort='medium'; effort=None → 'xhigh' fallback. Closes the open item in #72.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:34:13 +00:00
acfd5bae3e Merge pull request 'feat(halacha): חילוץ מצטבר crash-safe + resume (A + resume)' (#31) from feat/halacha-incremental-resume into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 3m5s
2026-05-31 21:28:19 +00:00
8e4ea23882 feat(halacha): crash-safe incremental extraction + resume (A + resume)
Halacha extraction held ALL chunk results in memory and stored once at the very
end — a crash/interrupt mid-run (e.g. the 2026-05-31 freeze) lost everything and
re-paid the full LLM cost on retry.

Now each chunk's halachot are stored AND the chunk is checkpointed
(precedent_chunks.halacha_extracted_at) the moment it finishes:

- V25 schema: precedent_chunks.halacha_extracted_at (per-chunk checkpoint).
- db.store_halachot_for_chunk: atomic per-chunk insert (halacha_index continues
  from MAX, caller serializes via an in-process store-lock) + checkpoint mark.
- db.reset_halacha_extraction (force) / mark_all_chunks_extracted (legacy backfill).
- _extract_impl rewritten: resume by default (skip checkpointed chunks; failed
  chunks stay pending and are retried; status stays 'processing' until all done);
  force=True wipes + redoes all. reextract_halachot passes force=True; the queue
  drain (process_pending) resumes by default.
- Legacy guard: a pre-V25 precedent (halachot exist, no checkpoints) is
  backfilled and treated as complete — never re-extracted (would duplicate).

Verified on 9002-24 (55 halachot, legacy): resume → legacy-backfill, NO
duplication (stays 55), all chunks checkpointed. Index continuation: store at
55,56 after max 54, no collision. Tracks #72.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 21:27:46 +00:00
6183e24316 Merge pull request 'fix(halacha): נעילה גלובלית — חילוץ אחד בכל רגע (מונע הקפאת מכונה)' (#30) from fix/halacha-extract-global-lock into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m35s
2026-05-31 20:43:10 +00:00
807053ec54 fix(halacha): global advisory lock — one extraction at a time (prevents box freeze)
2026-05-31: opus-4-8 @ xhigh extraction + overlapping driver processes (agent
fallback retries each spawn an independent `python -c` driver; process_pending is
serial WITHIN a process but the box ran 4-5 drivers in parallel) → 12-16 concurrent
xhigh `claude -p` procs → load 69 → hard reboot.

Fix: halacha_extractor.extract() now takes a Postgres advisory lock
(pg_try_advisory_lock, key 'HALA') before any work. If another extraction (any
process/agent/driver — all share the legal-ai DB) holds it, the call returns
status='busy' and the precedent stays pending for the next drain. Guarantees ONE
extraction at a time ACROSS PROCESSES — an in-process Semaphore cannot (drivers
are separate OS processes). Core logic moved to _extract_impl (unchanged) under
the lock. CHUNK_CONCURRENCY now env-tunable (HALACHA_CHUNK_CONCURRENCY, default 3).

Verified: while a lock is held, extract() returns 'busy' with no LLM call; lock
releases cleanly and the next extraction proceeds. Tracks #72.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 20:42:15 +00:00
62e5e5183d Merge pull request 'fix(precedents): החלטות ועדת ערר אינן מחייבות (is_binding=false)' (#29) from fix/committee-decisions-not-binding into main
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 43s
2026-05-31 20:40:54 +00:00