Skip to content

fix(qa): the receipt's cash-anchor obligation derives from the executed plan, and a fabricated decline fails (closes #677) - #694

Merged
atomchung merged 1 commit into
mainfrom
claude/677-receipt-plan-derived
Jul 31, 2026
Merged

fix(qa): the receipt's cash-anchor obligation derives from the executed plan, and a fabricated decline fails (closes #677)#694
atomchung merged 1 commit into
mainfrom
claude/677-receipt-plan-derived

Conversation

@atomchung

Copy link
Copy Markdown
Owner

Summary

A light-tier weekly_review's executed plan legitimately carries input.cash_anchor.status == "not_applicable" (reason light_tier), but ROUTE_CONTRACTS["weekly_review"]["cash_anchor"] demanded exactly one cash_anchor_checked event unconditionally, with no tier branch. The only way such a session could pass verify was a fabricated declined event asserting a question that was never asked — the exact dishonesty this tool exists to prevent (#230).

Per the maintainer disposition on #677:

  • cash_anchor_checked is required only when the executed Review Plan says the route had a cash-anchor opportunity.
  • input.cash_anchor.status == "not_applicable" means zero cash-anchor events is the honest trace.
  • The verifier inspects the archived plan fact rather than applying one unconditional route-level boolean.
  • A trace with not_applicable plus a provided/declined event is contradictory and must fail.

What changed

  • skills/fomo-kernel/tools/ux_receipt.py: start gains an optional --cash-anchor-status (choices: anchored/partial/absent/not_applicable), transcribed from the plan's input.cash_anchor.status the same way route itself already is — a declared, archived fact on the trace, not something this tool re-derives. verify_rows reads it from the trace's own declaration row instead of branching on the route alone: not_applicable requires zero cash_anchor_checked events and fails closed, naming a contradiction, if one exists anyway. Omitting the flag preserves the original unconditional reading (exactly one event required), so every receipt written before this field existed still verifies unchanged. A new CASH_ANCHOR_STATUSES constant mirrors the engine's real enum, and the value is validated unconditionally (like every other declared enum) so a hand-built or hand-edited trace with a garbage value fails the same way an unsupported route does.
  • skills/fomo-kernel/references/ux-receipt.md: documents the new flag on start, expands the cash-anchor section to state the full obligation model (structural False on snapshot_review/test_drive vs. the new per-trace narrowing on first_review/weekly_review), and updates the verify summary paragraph accordingly.
  • tests/test_interaction_trajectory.py: adds a light_tier_weekly_rows() fixture and eight new tests (see Proofs below), inserted alongside the existing "Cash anchor ([feat·design] Pre-review data-sanity check: confirm cash anchor (and flag known-incomplete data) before the motive questions #357)" test section.

Scope is deliberately narrow: verifier/receipt semantics only. No change to engine/review.py's _cash_anchor_status, card rendering, or any runtime/product behavior — a light-tier weekly review still asks nothing and shows nothing different than before. references/data-contract.md, references/interaction-delivery.md, and docs/qa-runbook.md gate 3 were all checked against the mirrored-surfaces row and found already accurate for this fact (see report in the issue thread / session notes); none needed edits.

Proofs (from the disposition)

  1. A complete synthetic light-tier weekly_review trace verifies with no cash eventtest_light_tier_weekly_review_verifies_with_no_cash_event, exercising the exact shape the issue reproduced with (opener, question, both card stages, rule choice, findings, owner verdict) under --require-owner-verdict --require-findings. A companion test (test_first_review_with_declared_not_applicable_status_also_verifies_with_no_cash_event) confirms the mechanism isn't special-cased to one route.
  2. A non-light plan that owed the check still fails when the event is absenttest_non_light_plan_still_requires_the_cash_anchor_event_when_absent, declaring anchored/partial/absent and confirming the original "exactly one" requirement is untouched.
  3. A light plan carrying a fabricated decline fails, contradiction namedtest_light_tier_weekly_review_with_fabricated_decline_fails.
  4. Mutation proof (cp backup/restore, __pycache__ cleared, mutated tree import-checked before each run):
    • Restoring the unconditional route-level requirement (deleting the not_applicable branch entirely) reddens 3 tests, including proof 1's own test:
      errors returned by verify_rows under MUTATION 1:
       - weekly_review must record exactly one cash_anchor_checked event
      
    • Dropping only the contradiction check (keeping the zero-events branch as a no-op pass) reddens exactly proof 3's test, with zero collateral:
      1 of 137 tests failed under MUTATION 2:
       - test_light_tier_weekly_review_with_fabricated_decline_fails :: []
      
      (verify_rows returns [] for the fabricated-decline trace — it would have verified.)
  5. Full offline suite green: PASS: all 49 suites passed. (python3 tests/run_all.py), both before and after a clean rebase onto latest origin/main.

Test plan

🤖 Generated with Claude Code

…ed plan, and a fabricated decline fails (closes #677)

A light-tier weekly_review's plan legitimately carries
input.cash_anchor.status == "not_applicable" (reason light_tier), but
ROUTE_CONTRACTS["weekly_review"]["cash_anchor"] demanded exactly one
cash_anchor_checked event unconditionally. The only way such a session
could pass verify was a fabricated declined event asserting a question
that was never asked -- the exact dishonesty this tool exists to
prevent (#230).

Per the maintainer disposition: cash_anchor_checked is owed only when
the executed plan says the route had a cash-anchor opportunity; zero
events is the honest trace for a not_applicable plan, and an event
recorded anyway is a named contradiction rather than accepted evidence.

- tools/ux_receipt.py: `start` gains an optional --cash-anchor-status,
  transcribed from input.cash_anchor.status the same way route already
  is. verify_rows reads it from the trace's own declaration instead of
  branching on the route alone: not_applicable requires zero
  cash_anchor_checked events and fails closed on any that exist;
  omitting the flag preserves the original unconditional reading, so
  every receipt written before this field existed still verifies
  unchanged.
- references/ux-receipt.md: documents the new flag and the narrowed
  verify semantics for first_review/weekly_review.
- tests/test_interaction_trajectory.py: covers a complete light-tier
  weekly_review trace verifying with no cash event, a non-light plan
  still failing on a missing event, a fabricated decline against a
  not_applicable plan failing as a named contradiction, the CLI
  round-trip for the new flag (present and omitted), and an unsupported
  status value failing closed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@atomchung
atomchung marked this pull request as ready for review July 31, 2026 18:29
@atomchung
atomchung merged commit 5fbb9b9 into main Jul 31, 2026
3 checks passed
@atomchung
atomchung deleted the claude/677-receipt-plan-derived branch July 31, 2026 20:08
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.

1 participant