Human approval
Change Loop has four distinct places where a human can enter the loop. They are routinely confused with one another, and they do different jobs.
| Boundary | Question it answers | Blocks? |
|---|---|---|
| Acceptance | Is this the outcome we wanted? | Yes — an undecided standard change fails validation |
| Review | Is this implementation sound, judged independently? | Yes, when policy triggers fire |
| Authority bridge | How does a verdict become a receipt? | It is the mechanism, not a gate |
| Host attestation | Is it safe to run unattended? | Only for unattended execution |
Acceptance
Section titled “Acceptance”Acceptance is a named person saying the outcome is the one they wanted. It is the only boundary about the product rather than the work.
A standard change starts undecided, and undecided blocks. change validate
fails until somebody decides, which is deliberate: silence is not consent, and
a change cannot drift into being accepted because nobody objected.
You decide it explicitly, one way or the other:
# Nothing subjective to sign off — the deterministic checks are the whole storyclaude-foundation change resolve <change> --acceptance-not-required
# Somebody has to look at it and say yesclaude-foundation change resolve <change> \ --acceptance-required --acceptance-reason "<why a person must judge this>"A rapid change starts at not-required instead, because rapid is reserved for
low-impact isolated work. Declaring a claim with the acceptance capability
also makes acceptance required, and that declaration outranks
--acceptance-not-required.
A passing acceptance receipt needs a named human, an explicit accept
decision, at least one distinct acceptance criterion, and a recorded
observation. It is re-validated on every read against the workspace hash, the
claims currently in scope, and the stated reason — so if any of those move
afterward, the acceptance goes invalid rather than silently carrying over.
Independent review
Section titled “Independent review”Review asks whether the implementation is sound. The reviewer may be a human or a different AI — what matters is independence, not species.
With workflow.reviewPolicy: "risk-tiered", every change is reviewed and risk
controls the bounded route:
- low: one full AI review; a material correction promotes to medium
- medium: one full AI review, one correction batch, then at most one fresh-session delta that closes the original finding IDs
- high: material risks are decided in the initial Decision Sheet; one full AI review and at most one post-correction delta; no mandatory human final
Authorization/secrets, public or cross-repository contracts, migrations or destructive state, money, concurrency, replay/idempotency, brokers/real wire, and activation of legacy behavior are high-risk signals.
Two properties govern who may review. Both are waivable, and each waiver is
declared the same way: a key in the committed foundation.json, never a
command flag — an exemption the reviewed party can write at the moment it is
caught is not an exemption.
Independence. The shipped policy uses
"review": { "independence": "self" }, allowing the reviewer to share an
implementer’s identity and session. It applies at every impact and stamps an
independence-waived-self-review trigger on the policy. The receipt records
what happened: review.policy.independent stays false, with
independenceWaived: true beside it explaining why it passed. Projects that
require separation of duties can strengthen the value to required; reviewer
identity must then differ from every implementation subject and an AI reviewer
must use a different session.
Diversity. The shipped policy uses
"review": { "diversity": "single-model" }, so diversity is preferred and a
Claude-Code-only installation can review in a fresh Claude session without
requiring Codex. This stamps a diversity-waived-single-model trigger on the
policy. Teams with both providers can strengthen the value to required; the
AI reviewer must then come from a different provider and model family than the
implementer.
The shipped default selects claude-opus; select codex-sol instead for a
Codex-only team. Configured AI review still runs read-only and ephemeral even
though the default policy does not require a distinct identity or session.
Each waiver relaxes only its own axis. A same-model self-review of critical work needs both declared; declaring one leaves the other enforced. Withdrawing either key invalidates the receipts it allowed, because the review policy is part of the contract fingerprint.
The authority bridge
Section titled “The authority bridge”The normal entry point is one resumable command:
claude-foundation advance <change> --through provenThe coordinator invokes the compatible proof primitive, creates or reuses the
request, and never polls an unchanged external wait.
When the packet is actually handed off, configured Codex or Claude Code review
uses authority run. A named-human review must use authority dispatch before authority record. Human acceptance uses request/status/record without a review dispatch.
authority request refuses to open unless the implementation tasks are
complete and that authority is genuinely required. The request is bound to the
workspace hash, expires after 24 hours, and is single-use. Dispatch records the
exact full or delta packet and consumes an attempt even if the reviewer crashes;
only a completed response can unlock the next route.
authority record validates the response against the request — version,
request ID, change ID, type, and workspace hash must all match — and then runs
the ordinary receipt validator. If the response claims a pass but the resulting
receipt would not be valid, the previous receipt is restored and the operation
fails. A completed request cannot be replayed.
If the workspace changes after a request is opened, the request goes stale and must be reopened. A verdict is bound to the code it was given, not to the change in general.
Host attestation
Section titled “Host attestation”This one is not human approval, despite living near it.
sandbox challenge plus doctor --unattended --attestation <file> is a signed
statement from a trusted host that the sandbox boundary is safe enough to run
without a person watching. It is Ed25519-signed, the nonce expires in ten
minutes, and a consumed nonce is recorded so it cannot be replayed.
Trust roots are root-owned system files only, and Change Loop independently refuses unattended execution when it finds a writable container socket, a mounted Kubernetes service-account token, or a mounted SSH agent socket — the things that would let “sandboxed” work reach outside the sandbox.
Detection is not authorization. Finding that unattended execution is possible never implies permission to use it.
Where decisions get recorded
Section titled “Where decisions get recorded”Decisions belong in the change packet, not in a chat transcript. An acceptance
reason lives in the contract, a review verdict lives in a receipt, and a
continuation decision is named by its --decision-ref. The rule underneath all
four boundaries is the same one: approval is never inferred from silence, and
a command being available is never approval to run it.