Skip to content

fix(acquisition): account terminal recovery operations - #460

Merged
ai-agent-on-behalf-of-john-hughes[bot] merged 5 commits into
mainfrom
fix/recovery-terminal-operation-accounting
Aug 6, 2026
Merged

fix(acquisition): account terminal recovery operations#460
ai-agent-on-behalf-of-john-hughes[bot] merged 5 commits into
mainfrom
fix/recovery-terminal-operation-accounting

Conversation

@johnjhughes

@johnjhughes johnjhughes commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • partition authenticated RECAP recovery authority into recovered material and canonical terminal-unavailable operations
  • skip terminal queue statuses 3/6/7 before provider or PDF I/O while preserving exact cap and ledger commitments
  • authenticate optional broker receipt history to the owning cycle, purchase policy, operation, queue, and reservation
  • bind terminal records through materializer replay and exact run-card/output reconciliation

Validation

  • Ruff format/check
  • Pyright: 0 errors
  • 297 focused tests passed on final merged dependency base
  • independent security/correctness re-review clean
  • hosted Python full suite will provide exact-head full proof

No live purchase ledger, provider, paid operation, evaluation, freeze, or dispatch was used.


Note

Medium Risk
Touches purchase-ledger interpretation, materializer verification, and official-run recovery boundaries; incorrect terminal classification could mis-account spend or skip recovery incorrectly, though changes are heavily validated and fail-closed.

Overview
RECAP Fetch quarantine recovery now exactly partitions every attempt-policy document into either a recoverable quarantine manifest row or a new terminal-unavailable-operations.jsonl artifact (legalforecast.recap_fetch_terminal_unavailable.v1). Documents whose purchase ledger operation is a canonical failed RECAP Fetch queue outcome (statuses 3, 6, or 7) are recorded as terminal-unavailable without CourtListener detail/PDF I/O or paid redispatch; all other authorized operations still go through fresh public recovery. Non-canonical failures remain fail-closed.

The recover-recap-fetch-quarantine CLI adds --terminal-unavailable-output, extends the run card with authorized / recovered / terminal counts and a committed hash for the terminal file, and materializer verification replays terminal rows against the authenticated purchase snapshot (including optional broker receipt history). Docs and schema reference describe the partition contract.

Reviewed by Cursor Bugbot for commit 064742d. Bugbot is set up for automated code reviews on this repo. Configure here.

Summary by CodeRabbit

  • New Features

    • Added quarantine recovery handling for documents that cannot be recovered, with separate terminal-unavailable records.
    • Added a recovery command option to write terminal-unavailable results to a specified output file.
    • Recovery summaries now report terminal-unavailable counts and include corresponding output commitments.
  • Documentation

    • Added the RECAP Fetch quarantine recovery v1 schema reference.
    • Expanded recovery guidance for tracking terminal failures and separating recoverable documents from terminal queue failures.

Copilot AI review requested due to automatic review settings August 6, 2026 02:02
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@secure-gate-dev-token-broker[bot], you've reached your PR review limit, so we couldn't start this review.

Next review available in: 6 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5662ff72-9a98-4b53-a76c-617b92f2f5b6

📥 Commits

Reviewing files that changed from the base of the PR and between 064742d and 86ca12b.

📒 Files selected for processing (9)
  • docs/schemas/recap-fetch-quarantine-recovery-v1.md
  • legalforecast/cli.py
  • legalforecast/ingestion/case_dev_purchase.py
  • legalforecast/ingestion/recap_fetch_quarantine_recovery.py
  • tests/test_cohort_document_materializer.py
  • tests/test_provenance_clearance_cli.py
  • tests/test_replacement_recovery_consolidation.py
  • tests/test_resolved_post_recovery.py
  • tests/test_unknown_public_recovery.py

Walkthrough

The recovery flow now records canonical terminal-unavailable operations in a URL-free JSONL artifact. The CLI writes and verifies this artifact, binds records to authenticated purchase state, updates run-card commitments and counts, and avoids provider I/O for terminal failures.

Changes

Terminal-unavailable recovery

Layer / File(s) Summary
Recovery contract and terminal partitioning
docs/schemas/..., docs/official-run-runbook.md, legalforecast/ingestion/...
Recovery separates terminal failures from recoverable and restricted documents. It validates queue state, reservations, broker receipts, identities, hashes, and prohibited recovery actions.
Terminal artifact command wiring
legalforecast/cli.py
The command accepts --terminal-unavailable-output, writes the JSONL artifact, and includes its path, digest, bytes, and count in recovery metadata.
Authenticated recovery verification
legalforecast/cli.py
Verification checks attempt policies, purchase snapshots, ledger bindings, document partitioning, selected-document membership, artifact integrity, and run-card conservation.
End-to-end and failure validation
tests/test_resolved_post_recovery.py, tests/test_unknown_public_recovery.py, tests/test_purchase_approval.py
Tests cover empty outputs, terminal queue status 6, malformed receipts, forged records, tampering, failed-before-dispatch operations, and provider-free recovery.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RecoveryCLI
  participant QuarantineRecovery
  participant PurchaseLedger
  participant CourtListener
  RecoveryCLI->>PurchaseLedger: read authenticated purchase snapshot
  RecoveryCLI->>QuarantineRecovery: verify attempt policy and operations
  QuarantineRecovery->>PurchaseLedger: validate terminal ledger bindings
  QuarantineRecovery->>CourtListener: request recoverable documents only
  QuarantineRecovery-->>RecoveryCLI: return recovered, restricted, and terminal-unavailable records
  RecoveryCLI-->>RecoveryCLI: commit artifact bytes and run-card counts
Loading

Possibly related PRs

Suggested reviewers: copilot

Poem

A rabbit guards the failure queue,
With ledger seals and receipts true.
No provider hop for terminal fare,
Three paths are counted with care.
JSONL leaves a faithful trace.
Nibble, verify, and close the case.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: accounting for terminal recovery operations in acquisition recovery.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/recovery-terminal-operation-accounting

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR tightens RECAP Fetch quarantine recovery accounting by splitting authorized recovery authority into (a) recoverable unknown-status documents that require fresh CourtListener detail + PDF I/O and (b) canonical “terminal-unavailable” failed queue outcomes (statuses 3/6/7) that are recorded for spend/cap accounting but intentionally skip any provider/PDF activity. It also extends materializer verification and run-card commitments to bind this new partition back to authenticated purchase state (including optional broker receipt history).

Changes:

  • Extend quarantine recovery to emit a new terminal-unavailable-operations.jsonl partition and return it from recover_recap_fetch_quarantine_documents.
  • Add strict validation + purchase-snapshot replay verification for terminal-unavailable rows during materializer verification.
  • Update CLI, runbook/docs, and tests to cover the new output artifact, commitments, and partition invariants.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/test_unknown_public_recovery.py Adds coverage for terminal-unavailable partitioning and broker-receipt tamper rejection.
tests/test_resolved_post_recovery.py Extends CLI/materializer verification tests to include the terminal-unavailable artifact and commitments.
tests/test_purchase_approval.py Updates CLI argument wiring tests for the new --terminal-unavailable-output parameter.
legalforecast/ingestion/recap_fetch_quarantine_recovery.py Implements terminal-unavailable record generation plus validation and ledger-binding replay.
legalforecast/cli.py Adds new CLI output flag, commits the terminal artifact in run cards, and verifies it during materializer replay.
docs/schemas/recap-fetch-quarantine-recovery-v1.md Documents the v1 partition contract and terminal artifact semantics.
docs/README.md Links the new quarantine recovery schema doc from the schema index.
docs/official-run-runbook.md Updates official-run recovery instructions to include terminal-unavailable output and the partition rules.
Suppressed comments (1)

legalforecast/cli.py:45305

  • In recovered-public provenance clearance, the verifier checks that the recovery committed the expected manifest/restriction/case relevance/review-request/document-root paths, but it does not check the newly committed terminal_unavailable_path. That omission makes recovered-public authority verification less strict than the underlying quarantine recovery contract for terminal-unavailable artifacts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread legalforecast/cli.py
Comment thread legalforecast/cli.py

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 064742dcf2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread legalforecast/cli.py Outdated
Comment thread legalforecast/ingestion/recap_fetch_quarantine_recovery.py
Comment thread legalforecast/cli.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (3)
legalforecast/ingestion/recap_fetch_quarantine_recovery.py (1)

923-931: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consolidate the two canonical hashers.

_canonical_mapping_sha256 and _canonical_operation_sha256 serialize with identical options. They differ only by the dict() copy and the "sha256:" prefix. Two copies of a canonicalization rule can drift, and a drift here changes every committed digest. Define one helper and derive the prefixed form from it.

♻️ Proposed consolidation
 def _canonical_operation_sha256(value: Mapping[str, Any]) -> str:
-    payload = json.dumps(
-        value,
-        ensure_ascii=False,
-        separators=(",", ":"),
-        sort_keys=True,
-        allow_nan=False,
-    ).encode("utf-8")
-    return "sha256:" + hashlib.sha256(payload).hexdigest()
+    return "sha256:" + _canonical_mapping_sha256(value)

The two (use-jsonify) findings from ast-grep on these functions are false positives. That rule targets HTTP response bodies. These functions compute digests.

Also applies to: 1172-1180

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@legalforecast/ingestion/recap_fetch_quarantine_recovery.py` around lines 923
- 931, Consolidate _canonical_mapping_sha256 and _canonical_operation_sha256
around one shared canonical JSON/SHA-256 helper, preserving the existing
serialization options and digest values. Keep the mapping helper’s unprefixed
hash and have the operation helper derive its "sha256:"-prefixed result from the
shared helper; remove duplicated canonicalization logic without changing
callers.

Source: Linters/SAST tools

tests/test_unknown_public_recovery.py (1)

347-360: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Reuse the existing canonical encoder instead of duplicating it.

_canonical(receipt) is the production encoder used when appending a broker receipt, but this test rebuilds the receipt digest with its own matching json.dumps(..., allow_nan=False) call. Import the helper from legalforecast.ingestion.case_dev_purchase for the test digest and persistence updates, or define one locally, so canonicalization cannot drift and the malformed receipt cases stop depending on a duplicated encoder format.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_unknown_public_recovery.py` around lines 347 - 360, Replace the
duplicated json.dumps canonicalization in the test setup around the response
update with the existing production _canonical helper imported from
legalforecast.ingestion.case_dev_purchase. Use that helper when computing
receipt_item["sha256"] and when serializing response for the purchase_operations
persistence update, preserving the test’s existing behavior while ensuring both
paths share the canonical encoder.
tests/test_resolved_post_recovery.py (1)

2104-2116: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add recovery coverage for terminal queue statuses 3 and 7.

This test covers terminal accounting only for queue_status == 6. Recovery parses status from CourtListenerRecapFetchError: RECAP Fetch terminal queue status <status> and treats statuses 3, 6, and 7 as terminal, so statuses 3 and 7 also need assertions for terminal_record["queue_status"], terminal_record["recovery_provider_request_executed"] is False, and terminal_record["paid_redispatch_executed"] is False.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_resolved_post_recovery.py` around lines 2104 - 2116, Add recovery
test coverage for terminal queue statuses 3 and 7 alongside the existing
status-6 assertions in the resolved post recovery test. Verify each terminal
record reports the expected queue_status and that
recovery_provider_request_executed and paid_redispatch_executed are both False,
preserving the existing terminal accounting checks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@legalforecast/ingestion/recap_fetch_quarantine_recovery.py`:
- Around line 416-426: Update the recovery flow around
_terminal_unavailable_record so selection_document_sha256 is taken from the
authenticated purchase snapshot’s operation["attempt_document_sha256"], rather
than record["attempt_document_sha256"]. Preserve the existing comparison at the
caller so it rejects artifacts whose top-level attempt_document_sha256 differs
from the snapshot value.
- Around line 336-337: Update validate_terminal_unavailable_records to require
queue_status to be an exact int before checking membership in
_TERMINAL_QUEUE_STATUSES or constructing/comparing terminal_reason; reject
booleans and floats such as 6.0 while preserving acceptance of valid integer
statuses.
- Around line 786-788: Replace the queue_id.isdigit() validation in the affected
queue-id validation flow with the module-level _CANONICAL_QUEUE_ID pattern,
defined alongside the other patterns as the canonical positive ASCII decimal
check. Preserve the existing string and leading-zero rejection behavior while
ensuring only values matching that pattern can enter terminal records.
- Around line 883-900: The terminal receipt validation in
_validate_terminal_broker_receipts must reject retained history containing
billing evidence. Extend the existing conflict condition to fail when
authoritative_fee_usd is not None or "0.00", or when billing_evidence is
non-empty, while preserving the current identity and ordering checks; do not
alter validate_broker_receipt or introduce a separate failed-state path.
- Line 888: Update the timestamp comparison in the quarantine recovery logic to
use exact string inequality rather than lexical ordering. Preserve the existing
previous-value guard, but replace the ordering check so valid non-equal
canonical timestamps are not rejected based on string sort order.

In `@tests/test_resolved_post_recovery.py`:
- Around line 2067-2085: Close both SQLite ledger connections deterministically
by wrapping sqlite3.connect in contextlib.closing: update
tests/test_resolved_post_recovery.py lines 2067-2085 and keep
connection.commit() plus PRAGMA wal_checkpoint(TRUNCATE) inside the block before
the handle is used by cli.main or read_case_dev_purchase_snapshot; update
tests/test_unknown_public_recovery.py lines 328-360 similarly and add an
explicit connection.commit() after the purchase_operations UPDATE so the
mutation is preserved before closing.

---

Nitpick comments:
In `@legalforecast/ingestion/recap_fetch_quarantine_recovery.py`:
- Around line 923-931: Consolidate _canonical_mapping_sha256 and
_canonical_operation_sha256 around one shared canonical JSON/SHA-256 helper,
preserving the existing serialization options and digest values. Keep the
mapping helper’s unprefixed hash and have the operation helper derive its
"sha256:"-prefixed result from the shared helper; remove duplicated
canonicalization logic without changing callers.

In `@tests/test_resolved_post_recovery.py`:
- Around line 2104-2116: Add recovery test coverage for terminal queue statuses
3 and 7 alongside the existing status-6 assertions in the resolved post recovery
test. Verify each terminal record reports the expected queue_status and that
recovery_provider_request_executed and paid_redispatch_executed are both False,
preserving the existing terminal accounting checks.

In `@tests/test_unknown_public_recovery.py`:
- Around line 347-360: Replace the duplicated json.dumps canonicalization in the
test setup around the response update with the existing production _canonical
helper imported from legalforecast.ingestion.case_dev_purchase. Use that helper
when computing receipt_item["sha256"] and when serializing response for the
purchase_operations persistence update, preserving the test’s existing behavior
while ensuring both paths share the canonical encoder.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1b1efa85-4bfa-4daa-98d2-d3fce2168d17

📥 Commits

Reviewing files that changed from the base of the PR and between 5b3efdb and 064742d.

📒 Files selected for processing (8)
  • docs/README.md
  • docs/official-run-runbook.md
  • docs/schemas/recap-fetch-quarantine-recovery-v1.md
  • legalforecast/cli.py
  • legalforecast/ingestion/recap_fetch_quarantine_recovery.py
  • tests/test_purchase_approval.py
  • tests/test_resolved_post_recovery.py
  • tests/test_unknown_public_recovery.py

Comment thread legalforecast/ingestion/recap_fetch_quarantine_recovery.py
Comment thread legalforecast/ingestion/recap_fetch_quarantine_recovery.py
Comment thread legalforecast/ingestion/recap_fetch_quarantine_recovery.py Outdated
Comment thread legalforecast/ingestion/recap_fetch_quarantine_recovery.py
Comment thread legalforecast/ingestion/recap_fetch_quarantine_recovery.py
Comment thread tests/test_resolved_post_recovery.py Outdated
@ai-agent-on-behalf-of-john-hughes

Copy link
Copy Markdown
Contributor

[awt-judge] Opus decision

Decision: block_for_fixes
Reasoning: All required CI is green (Python quality gates SUCCESS at head 064742d; 6/6 CI tasks passed; 0 abnormal), but the change carries multiple concrete, line-anchored, cross-corroborated correctness/security/data-integrity defects at the current head, so merging would ship broken functionality. Weighted most heavily per rubric: Codex P1 (r3725405430) shows the recovery-era full-ledger purchase-state hash comparison breaks downstream materialization for EVERY successfully resolved unknown-origin document — even when the terminal partition is empty — because clear_unknown_material legitimately advances state to cleared_public after recovery; this is a functional regression on the normal path that focused tests apparently do not cover. Codex P2 (r3725405436) breaks replay of pre-existing v1 run cards (six-output, no terminal commitment), blocking resumption after upgrade. Codex P2 (r3725405432) and CodeRabbit Major (r3725429311) show terminal-row replay skips the operation-authority validation used on the write path and binds selection_document_sha256 to the untrusted record rather than the authenticated snapshot, allowing misattribution of a terminal row to a different authority/attempt-policy/frozen selection document. CodeRabbit Major (r3725429328) lets a no-charge terminal record retain broker-receipt history carrying billing evidence, defeating the exact accounting invariant this PR exists to protect. Copilot (r3725397033) leaves the new terminal_unavailable_path unvalidated in recovered-public verification. CodeRabbit Major (r3725429347) identifies leaked SQLite ledger connections plus a lost-commit hazard that would silently stop the new tests from exercising tampered history. Two remaining data-validation blockers (r3725429319 non-ASCII queue_id; r3725429338 lexical timestamp compare) are also fixable. None of the changed paths touch secure-gate/sudo/auth-policy/CI-workflow/deploy or judge/rubric/schema-enforcement crown-jewel code, and no token-scope broadening, new secret reads, or secret-emitting logs are present, so this is a fixable block for a capable agent rather than a human handoff. r3725397054 and r3725429281 plus the informational nitpicks are genuinely deferrable.
Head SHA: 064742d
Model: opus

Codex action items

Action Items for Codex

Required CI is green, but the findings below are correctness/security/data-integrity defects raised at the current head by Codex, CodeRabbit, and Copilot. Fix every Critical and Security/Integrity item and re-run quality gates before re-requesting merge. Grouped by file, most-blocking first.

legalforecast/ingestion/recap_fetch_quarantine_recovery.py

Critical — functional regressions (fix first)

  1. [Codex P1 r3725405430] Stop requiring the recovery-era full-ledger purchase-state hash to match. After resolve-post-recovery-documents, clear_unknown_material legitimately advances resolved operations to cleared_public, so the snapshot passed by materialize-cohort-documents necessarily diverges from the recovery-era hash — even when the terminal partition is empty. As written, every successfully resolved unknown-origin document fails downstream materialization with a partition error. Fix: validate the individual terminal operation records instead of comparing the entire mutable ledger against its recovery-era snapshot. Success: resolved unknown-origin docs materialize cleanly while terminal records stay validated.
  2. [Codex P2 r3725405436] Preserve replay for existing v1 run cards. Completed recoveries created before this change use legalforecast.acquisition_run_card.v1 with six output paths and no terminal_unavailable_operations commitment. Unconditionally reading the new commitment makes every such immutable card fail materializer/clearance replay, so interrupted/downstream runs cannot resume after upgrade. Fix: version the run-card contract or accept the legacy six-output form as an empty terminal partition.

Security / data integrity (must fix)

  1. [Codex P2 r3725405432 + CodeRabbit Major r3725429311] Re-validate operation authority during terminal-row replay. Replay passes record["attempt_document_sha256"] as selection_document_sha256, which _terminal_unavailable_record copies verbatim and never checks against operation["attempt_document_sha256"]; the caller comparison is therefore trivially self-consistent and validate_terminal_unavailable_records only checks 64-hex shape. A crafted/edited artifact can misattribute a terminal row to a different authority, attempt policy, or frozen selection document. Fix: derive selection_document_sha256 from the authenticated purchase snapshot and invoke the same _validate_operation_authority binding the live write path uses before reproducing the terminal row.
  2. [CodeRabbit Major r3725429328] Reject billing evidence in retained terminal broker-receipt history. _validate_terminal_broker_receipts checks only identity/ordering, so a no-charge terminal row can retain confirmed/unreconciled history carrying authoritative_fee_usd or billing_evidence — a failed operation that looks charged, defeating the accounting invariant this PR targets. Fix: fail closed when authoritative_fee_usd not in {None, "0.00"} or billing_evidence is non-empty; do not alter validate_broker_receipt.
  3. [Copilot r3725397033, also at line 45299] _verified_recovered_public_clearance_capability validates manifest/restriction/case-relevance/review-request paths but not the new terminal_unavailable_path from _verify_materializer_quarantine_recovery. Fix: validate the terminal-unavailable artifact location so recovered-public verification rejects a card that commits it to an unexpected path.
  4. [CodeRabbit r3725429319] Replace queue_id.isdigit() with a canonical ASCII decimal pattern (e.g. _CANONICAL_QUEUE_ID = re.compile(r"[1-9][0-9]*")). isdigit() accepts non-ASCII/superscript digits the broker's _POSITIVE_DECIMAL rejects, letting a non-canonical queue_id persist in a terminal record with null receipt linkage.
  5. [CodeRabbit r3725429338] Replace the lexical updated_at < previous_updated_at ordering with exact string inequality (or a verified true-chronological compare) so valid non-equal canonical timestamps are not rejected on sort order.

tests/test_resolved_post_recovery.py and tests/test_unknown_public_recovery.py

  1. [CodeRabbit Major r3725429347] Both files open the ledger via with sqlite3.connect(...), which manages only the transaction and leaves the connection open; the still-open handle can recreate -wal/-shm sidecars and break the subsequent CaseDevPurchaseJournal lock/filesystem-state assertion. Fix: wrap in contextlib.closing, keeping commit() and PRAGMA wal_checkpoint(TRUNCATE) inside the block before cli.main/read_case_dev_purchase_snapshot reuse the ledger. In test_unknown_public_recovery.py, add an explicit connection.commit() after the purchase_operations UPDATE — otherwise closing discards the mutation and each parameterized case silently stops exercising the tampered history.

Optional — defer to follow-up (not merge-blocking)

Expected Outcome

  • Resolved unknown-origin documents materialize without partition errors; terminal records validated individually.
  • Pre-existing v1 run cards replay successfully (versioned schema or empty-terminal fallback).
  • Terminal-row replay enforces operation authority and binds the top-level selection-document hash to the authenticated snapshot.
  • No-charge terminal records cannot retain billing evidence.
  • Recovered-public verification enforces terminal_unavailable_path.
  • queue_id accepts only canonical ASCII decimals; timestamp comparison rejects only true mismatches.
  • Test ledger connections close deterministically and preserve tampered-history mutations.
  • Re-run Ruff/Pyright, the Python quality gates, and the focused recovery suite covering queue statuses 3/6/7.

Copilot AI review requested due to automatic review settings August 6, 2026 02:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Suppressed comments (1)

legalforecast/ingestion/recap_fetch_quarantine_recovery.py:258

  • The docstring says this publishes an immutable canonical JSONL manifest, but the implementation relies on json.dumps(...) defaults (ensure_ascii=True, allow_nan=True). To make the emitted bytes unambiguously canonical (and consistent with legalforecast.ingestion.canonical_json), it’s safer to pin ensure_ascii=False and allow_nan=False for each record serialization.
) -> None:
    """Atomically publish an immutable canonical JSONL manifest."""

Copilot AI review requested due to automatic review settings August 6, 2026 02:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Suppressed comments (1)

legalforecast/ingestion/recap_fetch_quarantine_recovery.py:280

  • write_recap_fetch_quarantine_manifest now supports a label for clearer conflict errors, but write_recap_fetch_restriction_evidence still calls it without a label. If the restriction-evidence file already exists with different bytes, the error will misleadingly say it’s an “existing quarantine manifest” conflict. Pass an explicit label here (and similarly for other non-manifest uses) so troubleshooting points at the correct artifact.
def write_recap_fetch_restriction_evidence(
    path: Path, records: Sequence[Mapping[str, Any]]
) -> None:
    """Publish immutable URL-free fresh-detail public restriction evidence."""

    write_recap_fetch_quarantine_manifest(path, records)

Copilot AI review requested due to automatic review settings August 6, 2026 03:07
@ai-agent-on-behalf-of-john-hughes

Copy link
Copy Markdown
Contributor

[awt-judge] Opus decision

Decision: block_for_fixes
Reasoning: The required check 'Python quality gates' is RED at head 9f55348 (F2, failureKind=actionable): 5 tests fail with AttributeError 'object has no attribute committed_amount_usd' (6122 passed, 33 skipped). Direct inspection of the diff (base de02b82..head) confirms this PR's own production changes introduced reads of purchase_snapshot.committed_amount_usd and thread purchase_committed_amount_usd through legalforecast/cli.py and legalforecast/ingestion/case_dev_purchase.py, while the SimpleNamespace/argparse.Namespace test doubles in test_cohort_document_materializer.py, test_provenance_clearance_cli.py, and test_replacement_recovery_consolidation.py were not updated to carry the new field. This is a real regression that breaks existing tests, so the PR ships a failing required check and cannot be approved. No blocking review comments and no Codex review are present; CodeQL (python/actions), Community Multi-Harness, Copilot review, and Code Quality all passed. No must-escalate category applies: changed paths are product-domain acquisition/ingestion/CLI/docs/tests (coordinatorProvenMatches empty; confirmed on inspection), and I found no token-scope broadening, new secret reads, or secret-emitting logs. The fix is mechanical fixture drift a capable agent can safely repair and re-verify, so block_for_fixes rather than handoff.
Head SHA: 9f55348
Model: opus

Codex action items

Action Items for Codex

Required check Python quality gates is RED at head 9f55348 — 5 failing tests, 6122 passed, 33 skipped. All 5 share one root cause: this PR added production reads of committed_amount_usd on purchase-snapshot/args objects, but three existing test files still build types.SimpleNamespace / argparse.Namespace doubles that lack the field, raising AttributeError. Merge is blocked until CI is green at the new head.

Critical Blockers (must fix)

  1. tests/test_cohort_document_materializer.py — add committed_amount_usd=<money string> to the SimpleNamespace purchase-snapshot double(s) exercised by:

    • test_paid_materializer_authenticates_decision_omission_before_recovery
    • test_downstream_replay_reauthenticates_bound_decision_omission
    • Why: the materializer path now reads snapshot.committed_amount_usd (added in legalforecast/cli.py).
  2. tests/test_provenance_clearance_cli.py — add committed_amount_usd to the argparse.Namespace args double used by:

    • test_recovered_public_capability_flows_through_planner_and_finalizer
  3. tests/test_replacement_recovery_consolidation.py — add committed_amount_usd to the SimpleNamespace double(s) used by:

    • test_multi_tranche_consolidation_materializes_promoted_purchased_documents
    • test_initial_only_consolidation_is_supported

Note: the working tree already has uncommitted edits to exactly these three files (likely an in-progress fix). Those edits are NOT part of head 9f55348, which is why CI ran red. Finish/verify them, then commit and push so the fix actually lands on the PR head.

Fix approach

  • Prefer updating the test doubles to mirror the real model: case_dev_purchase.py exposes committed_amount_usd as a USD money string, so give each double a value coherent with that test's cap/committed/reservation invariants (so reconciliation and run-card assertions still hold).
  • Do NOT paper over this with getattr(obj, 'committed_amount_usd', None) in production — the real snapshot/journal objects always provide the field; this is fixture drift, not an optional-attribute case.

Expected outcome

  • All 5 named tests pass locally.
  • Python quality gates (full pytest) is green at the new head SHA.
  • No production behavior change required beyond keeping the newly-added committed_amount_usd field.

Optional / non-blocking

  • No blocking review comments and no Codex review were present; CodeQL (python + actions), Community Multi-Harness Validation, Copilot review, and Code Quality all passed. Once fixtures are fixed and CI is green, this PR is otherwise mergeable.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated no new comments.

@ai-agent-on-behalf-of-john-hughes

Copy link
Copy Markdown
Contributor

[awt-judge] Opus decision

Decision: approve_as_is
Reasoning: The single known blocker (comment 5199926494) is a prior [awt-judge] Opus block_for_fixes filed against the earlier head 9f55348, where the required 'Python quality gates' check was RED (5 tests failing with AttributeError on missing committed_amount_usd due to fixture drift). Per E5 I re-evaluated against the current head 86ca12b rather than inheriting that conclusion. The current head adds exactly the prescribed fix: commit 86ca12b 'test: model authenticated purchase snapshots' adds committed_amount_usd to the SimpleNamespace/argparse.Namespace test doubles in precisely the three named files (tests/test_cohort_document_materializer.py, tests/test_provenance_clearance_cli.py, tests/test_replacement_recovery_consolidation.py) and the exact named tests. Coordinator facts F2 confirm the required check is now success at the exact current head 86ca12b (allRequiredGreen=true, 6/6 CI tasks passed: Python quality gates, CodeQL python+actions, Community Multi-Harness, Copilot review, Code Quality). I independently ran the 5 previously-failing tests at head and all pass. The production diff (cli.py, case_dev_purchase.py, recap_fetch_quarantine_recovery.py) is coherent with the stated intent (terminal recovery operation accounting) with no out-of-scope changes, and semantic security review (E8) found no new secret reads, token-scope broadening, credential persistence, or secret-emitting logs — 'auth' matches are domain terminology (authenticated/authority/authorized purchase accounting). No must-escalate category applies: coordinatorProvenMatches empty, integrityAnomalies empty, head SHA consistent across F1/F2/F4, and the blocking comment is a bot-authored judge report treated as data (E6). No material findings remain.
Head SHA: 86ca12b
Model: opus

@ai-agent-on-behalf-of-john-hughes
ai-agent-on-behalf-of-john-hughes Bot merged commit b620e70 into main Aug 6, 2026
12 checks passed
@ai-agent-on-behalf-of-john-hughes
ai-agent-on-behalf-of-john-hughes Bot deleted the fix/recovery-terminal-operation-accounting branch August 6, 2026 03:22
ai-agent-on-behalf-of-john-hughes Bot pushed a commit that referenced this pull request Aug 6, 2026
## Summary

- allow the exact-100 successor's authenticated empty pre-recovery purchased manifest to derive paid scope from final-selection gap identities
- subtract only verifier-owned terminal decision omissions and bind their source artifacts and partition into the consolidation run card
- filter historical closed-tranche material to the final active scope while rejecting missing, duplicate/rebound, uncleared, unledgered, or inconsistent records
- route terminal-authority journal replay through the byte-preserving read-only API landed in #459 and integrate the terminal-recovery contract landed in #460

## Validation

- `uv run pytest -q tests/test_replacement_recovery_consolidation.py tests/test_cohort_document_materializer.py tests/test_unknown_public_recovery.py` — 95 passed
- `uv run ruff format --check legalforecast/cli.py tests/test_replacement_recovery_consolidation.py`
- `uv run ruff check legalforecast/cli.py tests/test_replacement_recovery_consolidation.py`
- `uv run pyright` — 0 errors
- `uv run pytest -q` — 6,157 passed, 13 skipped

No live purchase ledger, provider, paid acquisition, evaluation, freeze, or dispatch operation was performed.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **New Features**
  * Added support for authenticated terminal omission inputs during replacement recovery.
  * Recovery now records verified source snapshots and input locations.
  * Added replay handling for terminal omission metadata.

* **Bug Fixes**
  * Strengthened validation of paid-document coverage, ledger consistency, clearance status, and recovery gaps.
  * Duplicate recovery records are now rejected consistently.
  * Recovery now detects missing or inconsistent authority inputs and coverage data.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants