fix(qa): the preview receipt is the settled pre-commitment card, not an intermediate render (closes #663) - #696
Merged
Conversation
…an intermediate render (closes #663) When a first_review/weekly_review resolves an absent cash anchor at the card beat, `add-cash` recomputes the review and a second preview renders before the commitment is written (flows/first-review.md step 6). `verify`'s exactly-once stage=preview pair had no rule distinguishing the pre-cash card that only asked the question from the settled, post-recompute card the user actually used to choose or skip the rule -- so the only way a `provided` trace could satisfy the old "cash_anchor_checked after the card" ordering was to receipt the superseded card as the decision artifact. Maintainer disposition (issue #663, comment 2026-07-31): a review trace has exactly one stage=preview pair, the last pre-commitment card; an earlier preview shown only to unlock a cash anchor is an intermediate interaction, not the accepted artifact. The receipt EVENT is deferred until the recompute settles; the presentation itself is not deferred. `verify_rows` now splits the two outcomes that recompute differently: `declined` keeps the existing "after the card" ordering, since nothing recomputes and the card that asked stands as settled. `provided` inverts it -- the recorded stage=preview artifact/card pair must postdate `cash_anchor_checked`, because providing an anchor always triggers `add-cash` and a recompute, so the exactly-once pair can only honestly be the card rendered afterward. A `provided` trace whose pair sits at or before that row now fails, naming the superseded-card problem. Trace id stability across add-cash's engine session replacement was already covered by the pre-existing "all events must use the declared session_id" check (references/ux-receipt.md's "keep the original session id for the whole trace"); this adds a dedicated assertion tying that coverage to #663's own scenario rather than reimplementing it. Docs updated in the same change (mirrored surface): references/ux-receipt.md (primary contract), docs/qa-runbook.md gate 3, references/data-contract.md, and qa/SKILL.md's replayed `weekly_review` qa-trace walkthrough, which was recording the pre-cash card and is now the reference example for the correct deferred ordering. references/card-delivery.md and references/interaction-delivery.md were checked and need no change: both describe presentation mechanics that stay true regardless of which card the receipt records. Full offline suite green: `python3 tests/run_all.py` -> PASS: all 49 suites passed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When a
first_review/weekly_reviewresolves an absent cash anchor at the card beat,add-cashrecomputes the review and a second preview renders before the commitment is written (flows/first-review.mdstep 6).verify's exactly-oncestage=previewpair had no rule distinguishing the pre-cash card that only prompted the question from the settled, post-recompute card the user actually used to choose or skip the rule — so the only way aprovidedtrace could satisfy the old "cash_anchor_checkedafter the card" ordering was to receipt the superseded card as the decision artifact. That is issue #663.Maintainer disposition (comment, 2026-07-31): a review trace has exactly one
stage=previewartifact/presentation pair — the last pre-commitment card the user actually used. An earlier preview shown only to unlock a cash anchor is an intermediate interaction, not the accepted artifact. The receipt event is deferred until the recompute settles; the presentation itself is not deferred. Trace id stability acrossadd-cash's engine session replacement was already required. Receipt semantics only — no change to card generation oradd-cash.What changed
verify_rowsinskills/fomo-kernel/tools/ux_receipt.pynow splits the two cash-anchor outcomes that recompute differently, where it used to treat them identically:declined— unchanged: nothing recomputes, so the card that asked the question is also the settled one, and the row must come after it.provided— inverted (new): providing an anchor always triggersadd-cashand a recompute, so the exactly-oncestage=previewartifact/card pair can only honestly be the card rendered afterward. The row must now come before that pair. Aprovidedtrace whose recorded pair sits at or before the row fails, naming the superseded-card problem.Docs updated in the same change (mirrored surface, per
docs/maintainer-guide.md's mirrored-surfaces table):skills/fomo-kernel/references/ux-receipt.md— the primary contract (required).docs/qa-runbook.mdgate 3 — was stating the old unconditional "after the card" claim.skills/fomo-kernel/references/data-contract.md— same stale claim, fixed to avoid a fresh cross-doc contradiction.qa/SKILL.md— the replayed, mechanically-checkedweekly_review# qa-trace:walkthrough was recording the pre-cash card; now shows the correct deferred order and is the reference example.Checked for staleness, no change needed:
references/card-delivery.mdandreferences/interaction-delivery.mddescribe presentation mechanics (which channel, same-message timing) that stay true regardless of which card the receipt records;flows/first-review.md/flows/weekly-review.mdcross-referenceux-receipt.mdfor the recording mechanic without asserting the ordering themselves.Required proof (from the disposition)
prepare → first preview shown → cash answer → add-cash → second preview shown → commitmentverifies, single preview artifact points at the second card. Built and driven with the actualux_receipt.pyCLI (subprocess) as a scratch probe, and as a production-shaped fixture intests/test_interaction_trajectory.py::test_end_to_end_provided_trace_pointing_at_the_settled_card_verifies. Both pass ({"status":"pass",...}/verify_rows(rows) == []).verifynow exits 1 withFAIL: cash_anchor_checked recorded 'provided' but the recorded preview artifact/card precedes it; ... recording it before this event keeps the superseded pre-cash card as the decision artifact .... Unit-level:test_a_provided_cash_answer_after_the_settled_preview_failsandtest_end_to_end_provided_trace_pointing_at_the_superseded_card_fails.session_id" invariant (test_session_id_must_be_consistent), which is unaffected by which engine sessionadd-cashreturns — the receipt's own session id is the conversation id declared once atstart(references/ux-receipt.md). Addedtest_trace_id_stays_the_original_conversation_id_across_add_cash, which ties that existing coverage to [process·qa] Cash-anchor recompute mid-first_review has no documented receipt slot for its second preview card #663's scenario directly (and cross-referencestest_review_v2.py::test_add_cash_recomputes_the_same_review_without_re_asking_anything, which proves for real thatadd-cashreturns a session id different from the one it was called with) rather than reimplementing anything.if stale:→if False:) with acpbackup/restore (nevergit checkout), cleared__pycache__, import-checked the mutated tree. Both proof-2 tests reddened with a clean semantic failure (not a crash):cpfrom the backup (byte-identical diff confirmed), cleared__pycache__again, reran — both green.python3 tests/run_all.py→PASS: all 49 suites passed.(run once before the mutation, once after restore — both green; this also caught and fixedqa/tests/test_skill_commands.py's replay of the now-staleqa/SKILL.mdwalkthrough before this PR, which is the "adjacent finding" below).Adjacent findings (not folded in)
qa/tests/test_skill_commands.pymechanically replays every# qa-trace:fence inqa/SKILL.mdthrough the real CLI andverify— it caught the staleweekly_reviewwalkthrough the moment the ordering check landed, which is exactly the drift-detection it exists for. No further action needed; noting it because it's a good example of the harness doing its job.providedcash-outcome receipt shape (checked viagrep -rl "cash_outcome.*provided"andgrep -rl "cash_anchor_checked"across.py/.md/.json) beyond the six files this PR touches, so there is no further mirrored-surface drift to fix.Assumptions
rule_choice_presented) continues to be recorded in the same message as whichever card is recorded — now the settled one on aprovidedtrace — consistent withflows/first-review.mdstep 6's "show that card before the commitment is written." This PR does not change when the commitment itself is written, only which card's presentation events the receipt records.🤖 Generated with Claude Code