Writer-observed execution

Decision. When record-gate-claim executes a declared evidence command itself (runObservedCommand — a real process it spawned, with a real exit code, output hash, and, for tests, a local-process-exit execution proof), it appends that observation to the same hash-chained command-log.jsonl the PostToolUse capture hook writes, under the same lockfile protocol, visibly attributed via source: "canonical-writer-execution". The capture fold’s precedence is outcome-ranked, source-blind: observed fail > observed pass > ambiguous, with the exit code always traveling with the winning status. The properties that matter follow: a writer pass can lift ambiguity but can never bury an independently captured failure (fail is sticky from either source), and a writer-observed failure likewise defeats any pass. Passes from the two sources rank equally — the distinction between them lives in the permanent source attribution, not in the fold. The chain fork classifier tolerates shared-parent siblings only when every sibling’s source is postToolUse-capture or canonical-writer-execution; any other source on a shared parent remains tamper.

Context. ADR 0017/0020 make the independent capture log the truth source for command outcomes, and #470 hardened capture to never record pass without positive evidence. On hosts whose runtime hook payloads carry no exit code (Claude Code’s PostToolUse tool_response today), every captured entry is honestly ambiguous — and #634 showed the consequence: builder.verify.* claims can never derive verified, while the sanctioned accepted-gap waiver produces assumed, which no gate’s accepted_statuses admits. The verify gate was structurally unclosable on such hosts regardless of evidence quality.

Why this preserves the anti-gaming posture. A writer observation is not an inference or a self-report of intent — it is the exit code of a process the canonical writer itself spawned and waited on. It enters the record distinguishable forever (the source field), tamper-evident (chain-hashed), serialized against the hook (shared lock), and subordinate on conflict (an observed fail from either source wins). The waiver path is untouched: accepted gaps remain disclosure that derives assumed, never gate satisfaction.

Residual risk, accepted. A compromised writer process could self-attest a pass the hook never saw. Mitigations: the attribution is permanent and auditable; the chain makes retroactive editing detectable; a hook-observed failure always prevails; and PR CI Trust Reconcile re-executes manifest commands independently of any local observation. Follow-up (#634 option c): when runtime harnesses surface exit codes in hook payloads, hook capture naturally resumes confirming passes first-hand; writer observations then serve as corroboration rather than the sole deterministic signal.

Revision-bound observations (#1081)

Decision. A command result is observed only when its result and its repository state have both settled. The canonical writer captures its Git-worktree snapshot after the child process’s close event; the PostToolUse capture records the host result first, then captures Git state before it appends and chain-hashes the command-log record. Each captured command therefore carries its own observed_at_commit and worktree_clean, as well as the observation-time verification_workspace_snapshot where the writer can provide it. The fields are copied without re-stamping through command-log.jsonl, metadata.observed_commands, and the canonical trust.bundle rebuild.

worktree_clean: false, missing provenance, a non-Git root, shallow or otherwise unresolved Git history, and trusted-Git capture failure are all non-confirming. A successful exit remains useful diagnostic evidence, but it is not_verified rather than a verified command observation and cannot satisfy a gate. Version 1 records are rejected and must be re-recorded with capture-time provenance.

Why both revision and snapshot are required. For each item that would contribute to a passing gate, trusted Git must resolve observed_at_commit and prove it is an ancestor of the trusted current HEAD; the item’s captured snapshot must also exactly match the current canonical Git-worktree snapshot. Ancestry makes history substitution visible, while the exact snapshot catches changed tracked or untracked bytes. Ancestry alone does not prove byte identity, and a cleanliness flag alone does not establish currentness. The trust.bundle is the runtime authority for this decision; workflow-evidence v2 is its consumer-facing projection contract, not a second authority.