Skip to content

Receipts, statuses, and staleness

A receipt is Change Loop’s record that a specific provider observed a specific result against specific content. Everything about it is designed so that a receipt cannot outlive the thing it described.

A provider returns one of four statuses, and three of them block.

Status Meaning Lands?
pass The provider ran and the claim held Yes
fail The provider ran and the claim did not hold No
error The provider could not run — crash, timeout, spawn failure No
inconclusive The provider ran but produced no verdict for the claim No

inconclusive is the one that surprises people, and it is the most useful of the four. A Playwright suite that runs green but carries no claim annotations is inconclusive, not passing: it demonstrated that some tests ran, not that your claim holds. Declared readiness that was never observed is an error regardless of the command’s exit code, because a suite that ran before its dependency was up proved nothing.

Each receipt records what ran, what it saw, and what it saw it against:

{
"provider": "test",
"execution": "harness",
"status": "pass",
"workspaceHash": "eb7f67b3…",
"contractFingerprint": "9a456f30…",
"providerFingerprint": "084664fa…",
"inputIdentity": { "mode": "global" },
"claims": ["profile-update"],
"artifacts": [{ "name": "command-log", "sha256": "", "size": 4210 }]
}

The fingerprints are what make reuse safe. contractFingerprint covers the intent, impact, review policy, acceptance, and claims. providerFingerprint covers the adapter, command, capability, environment, lockfile digests, timeouts, and readiness identity. Change any of them and the receipt no longer describes the current world.

workspaceHash binds the receipt to the content it was produced against. It is derived from Git index entries plus the identity of dirty files, skipping regenerable output directories and the change’s own execution.yaml — so rewiring a provider does not by itself expire evidence, but editing source does.

Change Loop distinguishes evidence it produced from evidence a person handed it, and it will not let the two be confused.

A receipt marked execution: "harness" can only be written by a call site that actually ran a command, and a passing one must carry a command log as an artifact.

A receipt marked execution: "manual" must carry an observation, a provenance source, and at least one artifact or a reference that resolves — a URI or a real path. Free text is refused, because “I ran it and it worked” is not evidence.

By default a receipt is bound to the whole workspace, so any edit expires it. That is correct but blunt: a documentation typo should not invalidate a security scan.

A provider can narrow that by declaring the inputs it actually depends on:

{
"security-static": {
"adapter": "command",
"command": ["npm", "audit", "--audit-level=high"],
"inputs": ["package.json", "package-lock.json"]
}
}

With declared inputs, Change Loop records the sorted digest of exactly those files. When the workspace changes but those files do not, the receipt is rebound to the new workspace hash rather than re-run, and the rebinding is written to an audit log. Narrow the inputs honestly — a provider that reads more than it declares will reuse a receipt it should have re-earned.

review, acceptance, and semantic-acceptance may not declare inputs. Their verdict is about the whole change.

A semantic-acceptance provider verifies an external Ed25519-signed envelope without exposing hidden inputs or oracle code to the implementation agent. The envelope binds stable case IDs, claims, input partitions, statuses, and observation digests to the exact workspace. A case can additionally bind a critical-case observation from a deterministic provider and a signed before-fail/after-pass reproduction.

Missing, skipped, duplicate, wrong-provider, stale, tampered, or failing required cases block Proof and Land. A passing review cannot override them. In an unambiguous single npm repository, Change Loop also activates its built-in package.json/package-lock.json consistency check without extra wiring.

A provider that executed and failed leaves three exits, and the fail verdict prints all of them: fix the code, rewire the provider, or withdraw that one capability’s enforcement on an explicit recorded decision:

Terminal window
claude-foundation change waive <change> --capability <c> --reason <why> --decision-ref <ref>

The waiver travels as a user-waived advisory into readiness, the proof record, the archive, and the LAND READY line; --revoke restores the requirement. It is deliberately subtractive: it cannot change what any other provider attested, receipts already earned stay valid, and there is no route that lands a failing proof. review and acceptance cannot be waived here — they have their own declared waiver routes in foundation.json.

prove refuses unless every required provider is currently valid. The common verdicts, in the order they are checked:

Verdict What happened
missing The provider never produced a receipt
contract-stale The claims or the contract changed after the receipt
provider-fingerprint-stale The command, environment, or lockfiles changed
stale The workspace changed and inputs were not declared
provider-inputs-stale The declared inputs themselves changed
review-not-independent The reviewer was also an implementer and the policy does not waive independence
review-not-diverse The AI reviewer shared a model family with the implementer
acceptance-invalid The scope, hash, or stated reason drifted after acceptance
semantic-acceptance-invalid A signed case, source observation, transition, issuer, or workspace binding is invalid
external-observation-missing A manual receipt lacks its observation or provenance

A receipt that goes stale after execution during the same proof run usually means a provider wrote inside the hashed surface — a report emitted into the working tree rather than an ignored directory. Point the report somewhere regenerable, or declare it as an artifact.

Land re-checks all of this a second time. The proof’s workspace hash must still match, and every live receipt’s digest must equal the digest recorded in the proof manifest, so a receipt cannot be swapped between proving and landing.