Receipts
Receipts are JSON artifacts that gate state transitions.
Implementation review receipts include:
- Backend
mode: which reviewer produced it —rp,codex,copilot,cursor, orhost(ortriage_skipfor the trivial-diff fast path) - Verdict:
SHIP,NEEDS_WORK,MAJOR_RETHINK, orNEEDS_HUMAN - Confidence anchors
- Introduced vs pre-existing findings
- Suppressed finding counts
- Deferred finding counts
- Validation state
- An optional versioned
findingsprojection
Ralph reads receipts before moving forward. This prevents fake “done” status and open-loop drift.
.flow/review-receipts/<branch>.jsonReceipts are not decoration. They are the machine-checkable handover between work and next action.
Structured findings
Section titled “Structured findings”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 type | Required findings.reviewKind |
|---|---|
plan_review | plan |
impl_review | implementation |
completion_review | completion |
qa_verdict | qa |
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.
Canonical values
Section titled “Canonical values”| Field | Stored values | Accepted reviewer aliases |
|---|---|---|
reviewKind | plan, implementation, completion, qa | none |
severity | P0, P1, P2, P3 | Critical → P0; Major → P1; Minor → P2; Nitpick → P3 |
confidence | 0, 25, 50, 75, 100 | none |
classification | introduced, pre_existing | pre-existing / pre existing → pre_existing |
status | open, fixed, not_fixed, withdrawn | fixed in review / resolved → fixed; not fixed / remains open / unresolved → not_fixed |
anchor.side | base, head | none |
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.
Finding identity and review lineage
Section titled “Finding identity and review lineage”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)>.jsonCommitted 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.
Anchors
Section titled “Anchors”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.
Currentness
Section titled “Currentness”Current status comes from one unambiguous receipt lineage tip:
- Validate each candidate and filter to the requested
reviewKindandbackend. - Require unique
sourceReceiptIdvalues. - Require every
supersedesReceiptIdparent to exist in the same lineage with a round exactly one lower. - Select unsuperseded tips whose
headShaequals the current review head. Exactly one must remain. - 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.
Bounds
Section titled “Bounds”| Surface | Limit |
|---|---|
| Reviewer source input | 1 MiB UTF-8 |
Encoded findings object | 256 KiB UTF-8 |
| Items | 200 |
rIds per item | 32, unique |
| IDs, backend, and review-kind strings | 160 characters |
baseSha and headSha | 160 characters |
| Anchor paths | 1,024 characters |
| Title | 240 characters |
| Body | 4,000 characters |
| Suggestion | 4,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.
Fallback contract
Section titled “Fallback contract”- No
findingsfield: 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-findingsSHIPresponse 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.
Memory is a different record
Section titled “Memory is a different record”Receipt lineage owns finding currentness. Bug memory owns reusable lessons.
After a non-trivial NEEDS_WORK → SHIP 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.
Standing-criteria compliance
Section titled “Standing-criteria compliance”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"}]idis a G-ID (G<digits>, unique within the array),statusis exactly one ofmet/violated/n/a, andnoteis an optional one-liner up to 400 characters.- The field is projected deterministically from the reviewer’s
## Global criteriaoutput 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
criteriaarray is authoritative for compliance status; findings carry the detail, since everyviolatedcriterion is also reported as a normal finding at reviewer-judged severity. No cross-validation links the two, so a consumer renders compliance fromcriteriaand detail fromfindingsindependently. - When
.flow/criteria.mdis absent, the review prompt contains no criteria content and receipts carry nocriteriafield.
Why receipts matter
Section titled “Why receipts matter”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"]
Verdicts
Section titled “Verdicts”| Verdict | Meaning |
|---|---|
SHIP | No blocking introduced findings remain. |
NEEDS_WORK | Fixable issues block handoff. |
MAJOR_RETHINK | The plan or approach is wrong enough to revisit the spec or architecture. |
NEEDS_HUMAN | A 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.
Receipt hygiene
Section titled “Receipt hygiene”- 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.
Human reading order
Section titled “Human reading order”When a receipt exists, read:
- Verdict.
- Introduced findings.
- Suppressed or deferred findings.
- Validation state.
- Confidence anchors.
That order prevents reviewers from over-weighting low-confidence commentary while still seeing real risk.