Skip to content

Receipts

Receipts are JSON artifacts that gate state transitions.

Implementation review receipts include:

  • Backend mode: which reviewer produced it — rp, codex, copilot, cursor, or host (or triage_skip for the trivial-diff fast path)
  • Verdict: SHIP, NEEDS_WORK, MAJOR_RETHINK, or NEEDS_HUMAN
  • Confidence anchors
  • Introduced vs pre-existing findings
  • Suppressed finding counts
  • Deferred finding counts
  • Validation state
  • An optional versioned findings projection

Ralph reads receipts before moving forward. This prevents fake “done” status and open-loop drift.

Terminal window
.flow/review-receipts/<branch>.json

Receipts are not decoration. They are the machine-checkable handover between work and next action.

Plan, implementation, completion, and QA receipts may contain an additive findings object. It preserves the finding stream as a stable, product-neutral handover while the original reviewer prose remains available.

{
"findings": {
"schemaVersion": 1,
"sourceReceiptId": "review-…",
"reviewKind": "implementation",
"backend": "rp",
"round": 2,
"baseSha": "optional-reviewed-base",
"headSha": "reviewed-head",
"supersedesReceiptId": "optional-parent-generation",
"items": [
{
"id": "finding-…",
"priorFindingId": "optional-explicit-lineage-edge",
"ordinal": 1,
"severity": "P1",
"confidence": 100,
"classification": "introduced",
"status": "fixed",
"anchor": {
"path": "src/example.py",
"originalPath": "optional/base-side-name.py",
"side": "head",
"startLine": 42,
"endLine": 45,
"baseSha": "reviewed-base",
"headSha": "reviewed-head",
"blobOid": "optional-git-object-id"
},
"title": "Short finding title",
"body": "Grounded explanation",
"suggestion": "Optional remediation",
"rIds": ["R3"],
"firstSeenReceiptId": "review-…",
"lastSeenReceiptId": "review-…"
}
]
}
}

The required container fields are schemaVersion, sourceReceiptId, reviewKind, backend, round, headSha, and items. baseSha and supersedesReceiptId are optional. Each item requires id, ordinal, severity, confidence, classification, status, title, body, rIds, firstSeenReceiptId, and lastSeenReceiptId. priorFindingId, anchor, and suggestion are optional.

Unknown fields make a v1 container unsupported. A consumer must not reinterpret them as a compatible extension.

The receipt envelope binds the projection to its workflow:

Receipt typeRequired findings.reviewKind
plan_reviewplan
impl_reviewimplementation
completion_reviewcompletion
qa_verdictqa

The receipt mode must exactly equal findings.backend. A type/kind or mode/backend mismatch invalidates the structured projection, so consumers fall back to the receipt and original prose.

FieldStored valuesAccepted reviewer aliases
reviewKindplan, implementation, completion, qanone
severityP0, P1, P2, P3CriticalP0; MajorP1; MinorP2; NitpickP3
confidence0, 25, 50, 75, 100none
classificationintroduced, pre_existingpre-existing / pre existingpre_existing
statusopen, fixed, not_fixed, withdrawnfixed in review / resolvedfixed; not fixed / remains open / unresolvednot_fixed
anchor.sidebase, headnone

Aliases apply only while Flow-Next parses reviewer prose. Stored objects always use canonical values. Unknown enum values make the structured projection unsupported; they are never coerced.

Items are stored in deterministic order: severity P0 → P3, confidence descending, then ordinal ascending.

sourceReceiptId identifies one findings generation. Whenever a finding is first seen, its ID is exactly finding- plus the first 32 lowercase hexadecimal characters of SHA-256 over the UTF-8 bytes flow-next-finding-v1\0<firstSeenReceiptId>\0<ordinal> (the \0 separators are single NUL bytes). Every valid successor carries the complete prior snapshot forward: each carried item keeps its ID and firstSeenReceiptId, while lastSeenReceiptId advances. A Prior finding N ratchet record updates the carried item’s status; it is not required for carry-forward.

Fully restated finding prose is not semantic identity. Without an explicit lineage edge, it creates an additional finding and ID even when its content or ordinal resembles an older item.

priorFindingId records an explicit edge only when the parser cannot preserve an older ID byte-for-byte. The new item keeps its new ID and names the older item. Only stored id and priorFindingId fields establish identity; consumers never match findings by title, body, anchor, ordinal, or semantic similarity. Every non-root generation is a complete snapshot: silently omitting a previously known finding invalidates the chain.

Before a latest receipt pointer advances, Flow-Next preserves its valid prior generation beside it:

<receipt-path>
<receipt-path>.history/<sha256(sourceReceiptId)>.json

Committed receipts conventionally live in .flow/review-receipts/; interactive workflows may use an explicit or temporary receipt path. Consumers start from the receipt paths handed over as evidence. They do not call Flow-Next parser helpers or depend on skill internals.

An anchor exists only when the reviewer supplied a safe repository-relative path and a positive line or range. Absent location evidence produces no anchor. A valid primary location without enough snapshot binding omits the entire anchor candidate before supplemental originalPath or blobOid metadata is or range ordering is interpreted. Thus an inverted range on an unbound anchor candidate is omitted with that candidate; Flow-Next never guesses the missing binding. Malformed or conflicting primary locations, unsafe primary paths, and invalid sides reject the entire structured generation. Once the primary location is snapshot-bound, an inverted range (endLine < startLine) rejects the structured generation. Once the primary location is snapshot-bound, invalid supplemental paths or blob OIDs also reject the generation. Consumers then fall back to the receipt and prose; they do not repair or truncate invalid anchor evidence.

side identifies the snapshot that owns the line. baseSha and headSha bind the anchor to the reviewed comparison. originalPath preserves the base-side name across a rename, and optional blobOid binds the location to a Git object. When a finding carries into a new snapshot without fresh location evidence, the finding stays but the stale anchor is removed.

No anchor does not mean no finding.

Current status comes from one unambiguous receipt lineage tip:

  1. Validate each candidate and filter to the requested reviewKind and backend.
  2. Require unique sourceReceiptId values.
  3. Require every supersedesReceiptId parent to exist in the same lineage with a round exactly one lower.
  4. Select unsuperseded tips whose headSha equals the current review head. Exactly one must remain.
  5. Walk that tip’s ancestor chain and reject cycles, incomplete snapshots, duplicate replacement ownership, duplicate finding identities, or invalid first-seen references.

Only the selected chain receives the step 5 finding-lineage checks; a semantically incomplete stale sibling does not invalidate it. That tip’s item status is current. A receipt bound to another head remains stale evidence, but cannot supply current resolution, approval, or ship state. Zero or multiple head-matching tips means “no unambiguous current structured findings,” not “no findings.”

The receipt verdict remains the workflow gate. Structured findings explain the finding stream; they do not grant SHIP independently.

SurfaceLimit
Reviewer source input1 MiB UTF-8
Encoded findings object256 KiB UTF-8
Items200
rIds per item32, unique
IDs, backend, and review-kind strings160 characters
baseSha and headSha160 characters
Anchor paths1,024 characters
Title240 characters
Body4,000 characters
Suggestion4,000 characters

R-IDs use R<digits>. IDs and ordinals are unique. round and ordinal are positive JSON integers—booleans do not qualify—and a root generation without supersedesReceiptId must use round 1. Anchor paths are normalized, repository-relative, and cannot traverse with ... Line ranges use positive integers. On a snapshot-bound anchor, endLine must be greater than or equal to startLine; an unbound anchor candidate is omitted before that ordering validation. Optional blobOid is 7–64 lowercase hexadecimal characters.

These are rejection boundaries, not truncation targets.

  • No findings field: use the receipt verdict and original prose.
  • Valid, supported, head-current v1: structured rendering is safe.
  • Valid but stale: label and retain it as stale evidence.
  • Unsupported version, invalid data, or ambiguous lineage: ignore the structured projection and keep the receipt plus prose.
  • Explicit empty items: the parser recognized a no-findings SHIP response for that generation. This is different from missing or rejected data.

Consumers never merge a stale or invalid object with prose or another generation to manufacture one apparently current record. Unsupported structured data is never a pass.

Receipt lineage owns finding currentness. Bug memory owns reusable lessons. After a non-trivial NEEDS_WORKSHIP fix cycle, Work may create a memory entry from the finding and fix. That entry does not supersede the receipt and its memory status (active, stale, or hardened) says nothing about whether the finding is open or fixed.

Keep both records: receipts for review evidence, memory for recurrence prevention.

Completion-review receipts (type: completion_review only) may carry a second additive field beside findings: per-criterion compliance with the project’s standing criteria in .flow/criteria.md (see Standing Criteria).

"criteria": [
{"id": "G1", "status": "met"},
{"id": "G3", "status": "violated", "note": "route added without contract regen"},
{"id": "G4", "status": "n/a"}
]
  • id is a G-ID (G<digits>, unique within the array), status is exactly one of met / violated / n/a, and note is an optional one-liner up to 400 characters.
  • The field is projected deterministically from the reviewer’s ## Global criteria output section, at the same public boundary as the findings parser: unparseable, duplicate-id, or oversized content degrades to absent, never to an error or a wrong verdict. Recorded ids must match the configured criteria before the array attaches. Legacy receipts without the field stay valid.
  • The criteria array is authoritative for compliance status; findings carry the detail, since every violated criterion is also reported as a normal finding at reviewer-judged severity. No cross-validation links the two, so a consumer renders compliance from criteria and detail from findings independently.
  • When .flow/criteria.md is absent, the review prompt contains no criteria content and receipts carry no criteria field.

Agentic work needs evidence that survives the chat. A receipt records the verdict, what was reviewed, which findings were introduced by the diff, and what remains unresolved.

flowchart LR
  Diff["Diff"] --> Review["Review backend"]
  Review --> Receipt["Receipt JSON"]
  Receipt --> Ralph["Optional Ralph gate"]
  Receipt --> PR["PR cognitive aid"]
VerdictMeaning
SHIPNo blocking introduced findings remain.
NEEDS_WORKFixable issues block handoff.
MAJOR_RETHINKThe plan or approach is wrong enough to revisit the spec or architecture.
NEEDS_HUMANA human must adjudicate - a judgment call, not another edit. The receipt and a needs_human status persist before the workflow exits with ESCALATE:.

Only introduced findings should block the current change. Pre-existing issues should be reported, but not silently converted into scope.

NEEDS_WORK is not a terminal state. It sends the work back through the fix loop, then review runs again. The receipt chain is the evidence that the loop kept pressure on the change until the introduced issues were resolved or explicitly deferred.

  • Keep receipts in repo-local .flow/ state.
  • Do not hide suppressed findings; record suppression count and reason.
  • Defer findings only when the PR body names them.
  • Re-run review after meaningful fixes.
  • Treat missing receipts as an unknown state, not a pass.

When a receipt exists, read:

  1. Verdict.
  2. Introduced findings.
  3. Suppressed or deferred findings.
  4. Validation state.
  5. Confidence anchors.

That order prevents reviewers from over-weighting low-confidence commentary while still seeing real risk.