@sabaiway/agent-workflow-kit 5.4.0 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -278,7 +278,7 @@ DEFAULT_CODEX_EFFORT="xhigh"
278
278
  # Review-receipt identity (AD-038). AW_BRIDGE_VERSION mirrors this bridge's SKILL.md/capability.json
279
279
  # version (drift-guarded by codex-review.test.mjs against capability.json).
280
280
  AW_RECEIPT_BACKEND="codex"
281
- AW_BRIDGE_VERSION="3.4.1"
281
+ AW_BRIDGE_VERSION="3.5.0" # aw-version-anchor
282
282
  CODEX_MODEL="${CODEX_MODEL:-$DEFAULT_CODEX_MODEL}"
283
283
  CODEX_EFFORT="${CODEX_EFFORT:-$DEFAULT_CODEX_EFFORT}"
284
284
  # Generous hard cap for a slow xhigh review (subscription latency varies).
@@ -3,7 +3,7 @@
3
3
  "schema": 1,
4
4
  "name": "codex-cli-bridge",
5
5
  "kind": "execution-backend",
6
- "version": "3.4.1",
6
+ "version": "3.5.0",
7
7
  "posture": { "model": "gpt-5.6-sol", "effort": "xhigh", "tier": null },
8
8
  "provides": ["execute", "review"],
9
9
  "roles": {
@@ -13,14 +13,15 @@
13
13
  "output": "diff",
14
14
  "contract": {
15
15
  "invocations": [
16
- "codex-exec <plan-file|->",
17
- "codex-exec <plan-file|-> -- <extra codex flags...>"
16
+ "codex-exec [--nonce <n>] <plan-file|->",
17
+ "codex-exec [--nonce <n>] <plan-file|-> -- <extra codex flags...>"
18
18
  ],
19
19
  "grounding": "automatic — the root AGENTS.md (Hard Constraints) is auto-merged into codex's context and the wrapper prepends the orchestrator execution contract; no grounding flags",
20
20
  "continue": [
21
- "codex-exec --resume-last <plan-file|->",
22
- "codex-exec --resume <session-id> <plan-file|->"
21
+ "codex-exec --resume-last [--nonce <n>] <plan-file|->",
22
+ "codex-exec --resume <session-id> [--nonce <n>] <plan-file|->"
23
23
  ],
24
+ "receipt": "side effect — a NONCED run mints ONE exec receipt beside the delegation store: the dispatch nonce seam is the AW_DISPATCH_NONCE environment value or its plain-argument equivalent --nonce <n>, recognised ONLY before the prompt operand (after the operand or a literal '--' it is passthrough payload, never a flag), under the safe grammar [A-Za-z0-9._-]{1,64} — anything else, a duplicate, or a flag disagreeing with a non-empty env value refuses PRE-SPEND. The store directory resolves exactly as the kit's delegation store does: the dirname of an ABSOLUTE AW_DELEGATION_STORE (a relative one, or one ending in a path separator, refuses), else the git common dir. The artifact is agent-workflow-exec-receipt-<backendLength>-<backend>-<nonce>.json in two states: 'reserved' is written atomically and NO-CLOBBER immediately before the CLI runs — that write IS the nonce reservation, so a second dispatch on the same nonce, or an already-taken report name, refuses BEFORE any spend — and 'terminal' replaces it in place at exit. A nonced run also refuses pre-spend when no timeout/gtimeout binary can cap it (an accounted dispatch that cannot be capped can never honour the terminal-exit rule; a nonce-LESS run still warns and runs uncapped), when node is missing (the mint core), and when the prompt rides on stdin instead of a contract FILE — contractDigest is computed BY THIS WRAPPER from the dispatch file it was actually handed, so the kit can refuse a run that executed a different contract than the one it opened. That digest is taken from the SAME bytes already read as the prompt, never a second open of the path: two reads leave a window in which the file can be swapped, and the run would then execute one contract while its receipt claimed the digest of another. The header's own nonce must EQUAL the dispatch nonce — 'dispatch open' copies the nonce FROM the header, so a disagreeing --nonce could only reserve an identity no return would ever absorb, and it refuses pre-spend. A contract file edited BETWEEN 'dispatch open' and the run is caught at ABSORB by the contractDigest comparison, not pre-spend: the wrapper never reads the ledger, and that boundary is what the whole lane rests on. At exit the wrapper FIRST re-reads its reservation and verifies its own opaque owner token — a foreign owner refuses having published NOTHING, neither report nor receipt — THEN writes the delegate's final message atomically to agent-workflow-exec-report-<backendLength>-<backend>-<nonce>.txt, THEN re-verifies the owner and REPLACES the reservation with the terminal receipt {schema, kind, state, backend, nonce, owner, contractDigest, wrapperVersion, posture {model, effort, tier}, capS, killGraceS, sessionId, exitStatus, outcome, reportDigest, reportLength, timestamp}: the report is complete on disk before any artifact says the run arrived. capS and killGraceS are the cap the run ACTUALLY applied. outcome is the wrapper's own SUBSET of the ledger's vocabulary — exit 0 with a session id -> success, exit 0 without one -> missing-identity, ANY nonzero exit including the timeout codes 124 and 137 -> transport-failure; every orchestrator judgment is recorded at absorb time, never claimed here. The session id is captured BEFORE outcome branching, so a FAILED run records one too; in resume mode it is the validated resume id. FAIL-CLOSED, deliberately NOT the review lane's warn-only receipt: a publication that cannot complete exits nonzero with a DISTINCT status, and the message states only what the run can still prove. 70: the reservation could not be verified BEFORE any publication — NOTHING was published, not the report and not the receipt, and because the artifact found there belongs to another run it is never a '--no-receipt' source. 71: a publication stopped after that point — either the report write failed (nothing beyond the reservation was published; the '--no-receipt' absorb then records reportLength 0, ineligible by the name empty-report) or the report IS on disk and the terminal receipt was not completed (the absorb reads it, report-if-present). The post-report lane never claims the reservation still stands, because after that point its fate is no longer something this run observed. Every lane names the tree as partial/dirtied rather than untouched. A nonce-LESS invocation is byte-unchanged: no reservation, no receipt, no artifact, no node.",
24
25
  "passthrough": {
25
26
  "policy": "guarded",
26
27
  "blocked": ["-c*", "--config*", "-s*", "--sandbox*", "--dangerously-bypass-approvals-and-sandbox", "--dangerously-bypass-hook-trust", "--full-auto", "--oss", "--local-provider*", "-p*", "--profile*", "-m*", "--model*", "-o*", "--output-last-message*", "--json*", "--color*", "--output-schema*", "--ephemeral*"],
@@ -75,14 +76,16 @@
75
76
  { "contractField": "invocations", "index": 1 }
76
77
  ],
77
78
  "operands": [
78
- { "slot": "<plan-file|->", "required": true, "description": "the plan or instruction file, or - to read it from stdin" },
79
+ { "slot": "[--nonce <n>]", "required": false, "description": "the delegation dispatch nonce the plain-argument lane onto the AW_DISPATCH_NONCE seam (dispatch open records it; flag and a non-empty env must agree), recognised only before the prompt operand" },
80
+ { "slot": "<plan-file|->", "required": true, "description": "the plan or instruction file, or - to read it from stdin (a nonced dispatch requires the FILE — contractDigest binds it)" },
79
81
  { "slot": "<extra codex flags...>", "required": false, "description": "extra codex flags, filtered by the guarded passthrough tiers" }
80
82
  ],
81
83
  "guardrails": [
82
84
  { "value": "runs under codex's OWN OS sandbox (workspace-write)", "enforcement": "enforced", "condition": "it cannot nest inside another sandbox — the FS turns read-only; route it outside on the OBSERVED failure", "source": "capability.json roles.execute.contract.notes" },
83
85
  { "value": "the guarded passthrough blocks model / sandbox / approval overrides", "enforcement": "enforced", "source": "bin/codex-exec.sh" },
84
- { "value": "hard wall-clock cap CODEX_HARD_TIMEOUT (built-in default 3600s)", "enforcement": "enforced", "condition": "only while timeout(1)/gtimeout is on PATH — otherwise the wrapper warns and runs uncapped", "source": "capability.json settings.CODEX_HARD_TIMEOUT" },
85
- { "value": "ONE exec posture stderr banner line states the ACTUAL run posture before dispatch (session=fresh|resume:<id>; timeout is banner-only)", "enforcement": "enforced", "source": "bin/codex-exec.sh" }
86
+ { "value": "hard wall-clock cap CODEX_HARD_TIMEOUT (built-in default 3600s)", "enforcement": "enforced", "condition": "only while timeout(1)/gtimeout is on PATH — otherwise the wrapper warns and runs uncapped, and a NONCED run refuses pre-spend", "source": "capability.json settings.CODEX_HARD_TIMEOUT" },
87
+ { "value": "ONE exec posture stderr banner line states the ACTUAL run posture before dispatch (session=fresh|resume:<id>; timeout is banner-only)", "enforcement": "enforced", "source": "bin/codex-exec.sh" },
88
+ { "value": "a NONCED run reserves its nonce before any spend and publishes a fail-closed terminal receipt at exit", "enforcement": "enforced", "condition": "nonce-supplied runs only — a nonce-less invocation writes no artifact at all", "source": "capability.json roles.execute.contract.receipt" }
86
89
  ],
87
90
  "customHooks": ["CODEX_PROBE"]
88
91
  },
@@ -95,7 +98,8 @@
95
98
  "whenNotTo": ["a fresh task — a stale session carries stale assumptions"],
96
99
  "invocationRefs": [{ "contractField": "continue", "index": 0 }],
97
100
  "operands": [
98
- { "slot": "<plan-file|->", "required": true, "description": "the follow-up instruction file, or - to read it from stdin" }
101
+ { "slot": "[--nonce <n>]", "required": false, "description": "the delegation dispatch nonce — the plain-argument lane onto the AW_DISPATCH_NONCE seam, recognised only before the prompt operand" },
102
+ { "slot": "<plan-file|->", "required": true, "description": "the follow-up instruction file, or - to read it from stdin (a nonced dispatch requires the FILE — contractDigest binds it)" }
99
103
  ],
100
104
  "guardrails": [
101
105
  { "value": "ONE exec posture stderr banner line states the ACTUAL run posture before dispatch (session=resume:<id> only AFTER the sidecar id is resolved and validated; timeout is banner-only)", "enforcement": "enforced", "source": "bin/codex-exec.sh" }
@@ -111,7 +115,8 @@
111
115
  "invocationRefs": [{ "contractField": "continue", "index": 1 }],
112
116
  "operands": [
113
117
  { "slot": "<session-id>", "required": true, "description": "the session id the original run printed on stderr" },
114
- { "slot": "<plan-file|->", "required": true, "description": "the follow-up instruction file, or - to read it from stdin" }
118
+ { "slot": "[--nonce <n>]", "required": false, "description": "the delegation dispatch nonce — the plain-argument lane onto the AW_DISPATCH_NONCE seam, recognised only before the prompt operand" },
119
+ { "slot": "<plan-file|->", "required": true, "description": "the follow-up instruction file, or - to read it from stdin (a nonced dispatch requires the FILE — contractDigest binds it)" }
115
120
  ],
116
121
  "guardrails": [
117
122
  { "value": "ONE exec posture stderr banner line states the ACTUAL run posture before dispatch (session=resume:<id> only AFTER the explicit id is validated against the session-id grammar; timeout is banner-only)", "enforcement": "enforced", "source": "bin/codex-exec.sh" }
package/capability.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "schema": 1,
4
4
  "name": "agent-workflow-kit",
5
5
  "kind": "composition-root",
6
- "version": "5.4.0",
6
+ "version": "5.5.0",
7
7
  "provides": [],
8
8
  "roles": {},
9
9
  "detect": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sabaiway/agent-workflow-kit",
3
- "version": "5.4.0",
3
+ "version": "5.5.0",
4
4
  "description": "Portable, cross-agent memory & workflow for AI coding agents — Claude Code, Codex, Cursor, Devin Desktop. One command deploys an AGENTS.md entry point + docs/ai context with cap/archive/index enforcement into any repo.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -0,0 +1,29 @@
1
+ ### Mode: dispatch
2
+
3
+ <!-- opt-in-capability: none — the engine is invoked explicitly per sub-task or per report; it guards no repeatable surface and there is nothing a deployment could be missing without knowing it -->
4
+
5
+ The **delegation engine** (delegation Plans 1–2): the sub-task **contract check**, the records a human hand-writes into the delegation ledger, the four **writer verbs** that put a delegated thread on the record, the **arrival waiter**, and the L0 **acceptance report** over one wave. It exists because nothing today records `{dispatched → returned → folded}` for EXEC work, so "how much does delegating actually buy?" is unanswerable. The ledger answers it; this mode is the surface a number enters it through, and the surface it is read back from.
6
+
7
+ Run **`node ${CLAUDE_SKILL_DIR}/tools/dispatch.mjs <check|register|observe|open|await|return|fold|degrade|aggregate> …`**:
8
+
9
+ 1. **`check <dispatch-file>`** — validates the ONE ```` ```aw-dispatch-contract ```` fenced block the dispatch file carries: exit `0` when its form is valid, `1` naming the FIRST violated field. The contract of the whole mode, in one sentence: the contract check is FORM-only — fields present, grammars respected, never boundedness, design-decidedness or acceptance adequacy — and `aggregate` REFUSES instead of computing acceptance for a wave with no pre-registration record, over an OPEN thread in scope, over a PRE-DISPATCH degrade that opens no thread, or across several waves with no `--wave`; the writer verbs add NO second legality door — the store's preflight is the only one, and its refusals travel verbatim — while `open` copies every mint-time field from the contract header and refuses a deadline below the wrapper cap plus the kill grace, `return` absorbs only a TERMINAL exec receipt whose backend, nonce and independently computed contractDigest match the dispatch it answers, and `fold` binds the CURRENT tree to the folded return's postTreeDigest, so a tree that moved between the two never folds, and `await` is satisfied ONLY by the TERMINAL exec receipt of its own dispatch's {backend, nonce} — never by a review receipt, a ledger line or a finding manifest — while an expiry names a supervision question and releases NO writer slot. Whether the sub-task is genuinely bounded, whether its design is decided, and whether its acceptance criteria are adequate stay **explicit orchestrator judgment** — a well-formed absurdity passes here by construction.
10
+ 2. **`register`** — appends the wave's **pre-registration** record (`--wave --step-classes --pairing-key --min-per-class --mean-l-threshold --first-pass-num --first-pass-den`). Acceptance is pre-registered so thresholds can never be chosen after the observations that will be judged against them; a registration is **immutable per wave** and a second one refuses. `--pairing-key` accepts only a key the aggregator actually implements (today `stepClass`) — a wave registered under any other key would record a contract the computation never follows, and the aggregator re-checks the registration independently, since a store can be hand-written.
11
+ 3. **`observe`** — appends ONE **observation** (`--wave --step-class --scope --plan --phase --provenance [--denominator-bytes]`). Provenance is `solo-construction` or `self-reported` **only**: delegated accounting is DERIVED from nonce threads and is never hand-appended. The numerator is the post-image bytes of the objects the scope names; a **solo** observation takes that same number as its denominator — `L = 1` by construction, so `--denominator-bytes` is refused — while a self-reported one states its own. One stated exception: a solo scope that measures **zero** bytes has no ratio at all, and is recorded INELIGIBLE by name (`zero-denominator`) rather than as `L = 1` — `0/0` is undefined before it is one, and a named ineligibility is what this family records instead of a silent number. **`--scope` is repeatable**, one repo-relative path per occurrence: no separator is safe inside a POSIX path, so a split list could not express `docs/my file.md` and would silently measure the wrong set wherever its fragments happened to exist. Paths are anchored at the **git top-level**, never at the caller's cwd, and the record carries a **JSON-encoded array of canonical repo-relative paths, carried in the schema's string field** (the record's `scope` is a string by D3, so the array travels encoded). An object's identity IS its canonical path, so two files with identical bytes are two objects and count twice, while one path reached twice (a repeated listing, an in-repo ancestor symlink) is one object and counts once. The run refuses by name outside a git work tree, for an absent or non-regular path, and for a path whose **real** location leaves the repository (an ancestor symlink is not a way out).
12
+ 4. **`open`** — appends the **dispatch** record that opens a thread (`--contract <dispatch-file> --wave --backend --rationale --wrapper-cap-s --kill-grace-s [--retry-of <nonce>]`). Every mint-time field — `nonce`, `stepClass`, `vehicle`, `deadlineS`, `retry.index`, `retry.cap` — is **COPIED from the contract header** and bound by `contractDigest`, so a dispatch can never disagree with the header it claims to carry. It fingerprints the tree (`preTreeDigest`) and probes it for a CLEAN baseline; a **dirty** one is recorded as such, and the eventual return is then metric-INELIGIBLE by the name `dirty-baseline` — the uncommitted-state fingerprint is blind to the index↔worktree split, so a dirty baseline cannot attribute bytes to a dispatch. It **refuses a `deadlineS` below `--wrapper-cap-s` + `--kill-grace-s`** (both explicit operands: the kit never reads another package's default), because a dispatch that cannot be capped inside its own deadline could never honour the terminal-exit rule. Every retry rule — one successor per thread, the cap read off the chain's ORIGIN, wave and class equality, a changed contract after a `contract-refusal` — is the **store's**, and `open` surfaces its refusals verbatim.
13
+ 5. **`await`** — watches for ONE dispatch to **answer**, and writes nothing at all (`--nonce [--timeout <s>]`). Satisfaction is the arrival of the **TERMINAL** exec receipt for the dispatch's own `{backend, nonce}`: a `reserved` artifact means the run holds the nonce and has published nothing about how it ended, so the wait continues — the same artifact that is a **supervision refusal** at `return`, because absorbing it would record an outcome nobody observed. Satisfaction is decided **positively**, by the exec-receipt reader itself, so an artifact of a neighbouring family standing at that path — a review receipt, a delegation ledger line, a finding manifest — **refuses** rather than satisfies: only an exec receipt answers an exec dispatch, which is the D10 rule the review waiter states from its own side. The bound is the **ABSOLUTE** deadline, measured from the DISPATCH record's timestamp rather than from whenever this wait began: `--timeout` defaults to the time **remaining**, and one reaching past the deadline is **refused** rather than silently clamped (a shortened wait would let a caller believe they waited longer than they did). An already-expired dispatch is **answered immediately**, never waited on. **Arrival is read first, before EVERY bound** — the two wait bounds and the `--timeout` admissibility check alike, so a receipt already on disk answers `ARRIVED` even when the supplied `--timeout` is inadmissible, and the check runs only once there genuinely IS a wait to bound. A receipt already on disk is a fact this verb reports; whether a *late* one may be absorbed is the absorb door's question, refused there by name — **lateness has exactly one decision site, and it is `return`**. The review waiter checks its deadline first for the opposite reason: a receipt *line* carries no dispatch-bound timestamp at all, so there the clock is the only evidence there is. Each pass consults the clock over the poll that has already happened, then sleeps and polls again, so a receipt landing during the last sleep is reported rather than lost to a cutoff a moment later. An unanswered wait exits **`3`** — its own status so a caller that **branches** on the code can tell it from a refusal, never a defence against one that discards failure wholesale — names whether the **deadline** or the `--timeout` ended it, and states that **no writer slot was released**: a wait that ended without an answer never authorizes the next dispatch. Every artifact read rides the store's own no-follow reader, so a symlinked, FIFO, unreadable or malformed artifact refuses **by class** instead of standing in for an answer.
14
+ 6. **`return`** — absorbs the wrapper's exec receipt and appends the **return** record (`--nonce [--outcome <o>] [--no-receipt --exit-status <n>]`). The receipt and its report live **beside the ledger**, in the store's own directory. It reads the **TERMINAL** receipt — a `reserved` one is a **supervision question**, not a timeout, and refuses by saying so — then checks the receipt's independently computed `contractDigest` against the dispatch's (a run that executed a *different* contract would otherwise correlate cleanly), its `{backend, nonce}` body against the dispatch it answers (the filename alone is composed by this side), its `capS + killGraceS` against the recorded `deadlineS`, and its timestamp against the **ABSOLUTE** deadline (`dispatch.timestamp + deadlineS`) — a late return refuses by name. It re-verifies the report's digest and length against the receipt, refuses a change set **hidden from the plain `git diff`** the metric is computed over (assume-unchanged, skip-worktree, `diff.ignoreSubmodules` — the numerator and the denominator would both be blind to it), and then enumerates the returned objects and frames the canonical integration bundle, **bracketed by the tree fingerprint**: a tree that moves mid-computation refuses rather than mixing two change sets. `--outcome` records an orchestrator judgment under the **closed override form** — a wrapper outcome either stays itself or moves to `contract-refusal | partial-edit | acceptance-failure | stale-return | store-failure`, and `success` is recordable only from a receipt that already says success; the record vocabulary's own cross-field rules cut the remaining incompatible pairs. **`--no-receipt`** means "no TERMINAL receipt", never "no evidence": it absorbs a **reservation ONLY** — over a `terminal` artifact it refuses, because a hand-stated outcome there would discard an exit status, a session id and a report digest the run actually proved, and skip the report check that guards them. It builds the return from that reservation (`wrapperVersion` and `posture`, never hand-typed), requires `--exit-status` and an `--outcome` from `transport-failure | contract-refusal | store-failure | missing-identity` (so `partial-edit` and `stale-return` are inexpressible there by name), and reads the report **if present** — an absent one records length 0 and the metric is then ineligible **by name**: `empty-report` where there IS a diff, and `no-op-diff` where the tree is unchanged too, since the eligibility rule names the diff first. That keeps a failed REPORT write and a failed TERMINAL write two separately named lanes. With **no artifact at all** there is no honest return — `--no-receipt` reads the same path and would refuse the same way — so close the thread with `degrade`.
15
+ 7. **`fold`** — appends the integration re-confirmation and CLOSES the thread (`--nonce --verdict <text>`). It runs the **same hidden-path guard** as `return`, and here for a sharper reason: `treeDigestAtFold` is computed from the very payload that is blind to those paths, so a change made behind an `assume-unchanged` / `skip-worktree` bit between the return and the fold leaves the digest **equal** and the fold would accept bytes nobody returned — the digest cannot catch what the payload cannot see. It then computes the **current** tree digest, which the store binds to the folded return's `postTreeDigest`: a tree that moved between the return and the fold refuses in the store's own words, and the recovery it names is a fresh dispatch, never a fold. Staging usually moves the tree too — every change set carrying an untracked path does — so the order is `return` → review (own eyes + the configured review recipe) → a plain gate run → `fold` → `git add`. **The one shape staging does not move** is a tracked-only change passing from the worktree into a **clean** index: the payload concatenates the staged and unstaged diffs, so those bytes are identical either side of `git add` (the uncommitted-state fingerprint's stated blindness to the index↔worktree split). The fold is still honest there — identical payload bytes mean the folded content IS the returned content — so the rule is "**the fold precedes staging**", never "staging refuses the fold by construction".
16
+ 8. **`degrade`** — appends the recorded no-fold closure (`--wave --step-class --rationale [--nonce]`), **threaded** (with `--nonce`) or **pre-dispatch** (without). The pre-dispatch form opens no nonce thread, so once one is recorded `aggregate` **REFUSES the whole wave by name** until the wave's counting unit is decided — said here and on the line that writes it, because it is a live cost of the record, not a surprise to discover later.
17
+ 9. **`aggregate [--wave <id>]`** — reports one wave: the registered thresholds, every observation (context — never part of the acceptance number), and per registered step class the delegated threads with the **D7 inclusion table** applied. A folded **success** with an eligible `wrapper-git` metric contributes its `L`; a folded success whose metric is ineligible or self-reported is EXCLUDED from the mean and from `n` while still counting in the first-pass rate; a failure-terminal thread, a degrade-closed one, and a folded **acceptance-failure** (the §7 fold-fix — the orchestrator paid for the same work twice, so its bytes are not leverage) are each in `n` at `L = 0`. The first-pass rate is per retry CHAIN: its denominator is the `retryIndex`-0 threads, so a retry never reports one piece of work twice. Below the registered minimum acceptance is **NOT computed** (insufficient); at or above it is computed and labeled **PILOT** evidence. The primary statistic is the per-class MEAN of `L`; the byte-weighted aggregate prints as SECONDARY only.
18
+
19
+ **Exit codes:** `0` success (a computed report and an insufficient one both exit 0 — go/no-go is judgment, never an exit code); `1` a refusal (a form violation, an unreadable dispatch file, a supervision question, any store STOP verbatim); `2` usage; `3` an `await` that ended with no terminal receipt — its own status so a caller that **branches** on the exit code can tell an unanswered wait from a refusal; a caller that discards every failure discards this one too.
20
+
21
+ **A concealing tree is refused at every door that measures or binds one** (`open`, `return`, `fold`) — the recorded baseline, the counted bytes and the folded identity are all claims about a tree that is telling the truth. Two arms, and the first refuses a **bit**, not its effect: any index entry whose `ls-files` tag is not `H` (assume-unchanged, skip-worktree, an unmerged index) stops the verb, because deleting a *materialized* skip-worktree file is invisible to `computeWorkingState`, to the plain diff, to the tree fingerprint **and** to the producer's enumeration — there is no pair of views whose difference could reveal it, and at `open` it is worse than invisible: the baseline records CLEAN and the delegate is later credited with a deletion it never made. The second arm compares the forced diff against the plain one **per side and on raw bytes** (a union lets a path visible on one side mask its hidden state on the other; decoded names let two distinct paths collapse into one), refusing what an `ignore-submodules` setting hides. Both name the path; the recovery is `git update-index --no-assume-unchanged / --no-skip-worktree`, or clearing the diff config.
22
+
23
+ **Honest limits of the exec lane, v1:** **gate output is never accounted** — the wrapper's EXIT trap removes its trace, so no `gate-output` component is emitted and the metric counts the returned change set only; a change set carrying a **binary, non-regular or submodule** object is **REFUSED** at `return` and `fold`. For the first two the payload carries no content at all — a binary by name alone (`untracked-binary:<path>`, and a tracked binary's diff is the line "Binary files … differ"), a non-regular path by name alone. The **submodule** arm is *conservative* rather than a claim about every submodule: a clean staged gitlink replacement does carry exact OIDs in the payload, but once a submodule is **dirty** nothing further about its nested state is recorded (probed: a second, different nested edit and a whole new nested file both leave the digest unchanged), and no cheap probe separates the two at this door. A **symlink whose target is not valid UTF-8** is refused for a narrower reason on the same principle: the payload reads a target as a *string*, so those bytes are folded to `U+FFFD` before the digest is taken (probed: targets `0xff` and `0xfe` produce one payload and one enumeration), and a later change of target would move nothing. Reading that target fails closed on every errno but `ENOENT` — a deleted link has nothing to lose, its bytes riding the diff, while `EINVAL` means the object is **no longer a symlink at all**: the producer labels an object `symlink` when *any* layer carries mode `120000`, so a committed symlink replaced by a binary regular file is a `symlink` entry that the content-blind refusal never sees either, and the type change would fall between the two guards.
24
+
25
+ **Which ledger the tree-binding verbs use.** For `open`, `return` and `fold` the resolved store must be the **canonical** one, exactly — `<git common dir>/agent-workflow-delegation.jsonl` — and every other store refuses. Three failures collapse into that one equality: a store placed inside the work tree is carried by the payload *and enumerated as an object*, so the metric would count its own bookkeeping as delegated work while the append that follows `postTreeDigest` moves the tree every later fold binds; a store belonging to another repository would measure this tree against a foreign thread, the frozen record schema binding no worktree identity; and a **second** ledger in the same git dir would share artifact names with the first, since those are a function of `{backend, nonce}` alone — so a return could absorb the neighbouring ledger's receipt. One repository, one delegation ledger, which is what resolving to the git *common* dir already meant. `register`, `observe`, `await` and `aggregate` keep the unrestricted override — they bind no tree. Path comparison canonicalizes through symlinks and **fails closed** on any error but `ENOENT` (a path this kit cannot resolve is not one it may approve); the check is repeated immediately before every append, which *narrows* the replacement window rather than closing it — a check-then-write pair is not atomic, and this module defends against a buggy or interrupted producer, never a racing adversary. `open` additionally refuses **pre-spend** when either exec artifact name for its `{backend, nonce}` is already taken: one nonce, one artifact pair. **Stated residual:** two *linked worktrees* of one repository share a common dir by design (the ledger is worktree-shared), so this rule cannot separate them — opening in one linked worktree and returning or folding in another measures the wrong tree, is **forbidden** under the D10 one-writer bar, and is named here rather than mechanized, because the frozen record schema carries no worktree identity to bind it with. Their bytes can therefore move under a digest that cannot follow them, while the numerator counts a size re-read at return time; documenting that would leave a promise knowingly false, so the capability is **subtracted** until the frozen shared payload (which the review lane binds too) can carry their content — queued as its own item.
26
+
27
+ **What the fold's binding IS, and its stated residuals.** The binding is **equality of the visible unframed payload** between the return and the fold — never an identity of the tree, and the difference is not academic. The payload is an unframed concatenation, so **two different trees can produce the same bytes**: probed live, a single file containing the line `untracked:two.txt` yields the same fingerprint as a tree of two files, and a symlink's target can imitate the marker that opens the next untracked entry. It also carries **no git-relevant mode**, so `chmod 0644 → 0755` on an untracked file moves neither the fingerprint nor the enumeration — a blind attribute, not a fourth unsupported content class. Regular-file content and symlink targets are deliberately **not** subtracted: they are present in the payload, only ambiguously framed, and refusing them would refuse `new` and `symlink`, which is every delegated change set there is. The real fix — a framed payload carrying mode bits — is queued against that frozen surface, since it revalues every stored fingerprint in the family. A **receipt is forgeable**, exactly like every record in this family, so what the absorb door defends against is a buggy or interrupted producer, not a hostile one; the numerator **over-counts a rename** (a delegate's rename arrives as a deletion plus a creation, and no identity matching is done — that machinery was built and deliberately subtracted), so the metric reads optimistic, except in the rename-then-recreate case, which is indeterminate and named in the producer's own header; and **D10 is a bar, not a mechanism** — at most ONE in-tree exec dispatch runs at a time, and nothing here refuses a second one.
28
+
29
+ **Invariants:** writer — appends only to the delegation ledger, `<git common dir>/agent-workflow-delegation.jsonl` (`AW_DELEGATION_STORE` overrides, absolute only), through the store's own lock-serialized append; the store's preflight is the single legality door and this mode adds no second validator · every verb that DERIVES anything from the ledger (`return`, `fold`, `degrade`, `aggregate`) replays the store's preflight over the whole file first and refuses at the first record the append path would have refused, naming its physical line — a verb never computes over a "legal prefix" · **never commits**, never runs a subscription CLI, spawns nothing but git READS · the ledger is separate from the review receipts and the flow store, and a line of one family never satisfies a reader of another.
@@ -2,13 +2,13 @@
2
2
 
3
3
  <!-- opt-in-capability: none — a read-only per-dispatch waiter; it guards no repeatable surface (the review obligations gate is review-state, already covered) -->
4
4
 
5
- The **per-dispatch receipt-ARRIVAL deadline runner** (flow-orchestration #41/#50): it waits for ONE dispatched review to **answer**, never for the review obligations to be satisfied — satisfaction is receipt ARRIVAL past the watermark — a strictly-newer parseable receipt line from the dispatched backend (or its nonce-matched finding manifest, preferred when present) — never obligation satisfaction. For "block until `--check` would PASS" use `review-state --await` instead; this runner answers the narrower per-dispatch question "did THIS dispatch come back at all?", which is what the round dispatch ledger's deadline discipline needs.
5
+ The **per-dispatch receipt-ARRIVAL deadline runner** (flow-orchestration #41/#50): it waits for ONE dispatched review to **answer**, never for the review obligations to be satisfied — satisfaction is receipt ARRIVAL past the watermark — a strictly-newer parseable REVIEW receipt line from the dispatched backend (or its nonce-matched finding manifest, preferred when present), never a delegation-ledger line that merely names the same backend — never obligation satisfaction. For "block until `--check` would PASS" use `review-state --await` instead; this runner answers the narrower per-dispatch question "did THIS dispatch come back at all?", which is what the round dispatch ledger's deadline discipline needs.
6
6
 
7
7
  Run **`node ${CLAUDE_SKILL_DIR}/tools/receipt-deadline.mjs --backend <name> --watermark <bytes> [--nonce <nonce>] [--timeout <s>]`**:
8
8
 
9
9
  1. **`--watermark`** is the receipts-file **byte length minted BEFORE the dispatch** (the round dispatch-ledger `receiptWatermark`). The runner additionally binds the file **prefix below that offset IN-PROCESS at start**: a shrunken file or a rewritten prefix refuses **loudly for the lifetime of the run** — a truncate-and-rewrite can never masquerade as arrival — and the watermark must sit **on a line boundary** (a positive offset whose preceding byte is not a newline refuses loudly at start: the pre-dispatch tail was unterminated, so an appended receipt would physically continue that malformed line). Honest limit: the prefix binding is a **runtime guard, never a persisted proof** (the persisted ledger watermark stays the plain integer).
10
- 2. **Arrival** = a newline-terminated, parseable receipt line **from that backend** starting at/after the watermark offset. A malformed line never satisfies (and never masks a later valid one); a foreign backend's line never satisfies; a partial (unterminated) append is not a receipt yet.
11
- 3. **`--nonce`** (the dispatch nonce under the safe grammar `[A-Za-z0-9._-]{1,64}`): when the `{backend, nonce}`-named finding manifest exists beside the receipts file, the runner **prefers that correlation** — the manifest is minted atomically BEFORE the receipt append, and it carries the dispatch identity, so it can never be another dispatch's receipt. A malformed or foreign-identity manifest refuses loudly.
10
+ 2. **Arrival** = a newline-terminated, parseable **review receipt** line **from that backend** starting at/after the watermark offset. A malformed line never satisfies (and never masks a later valid one); a foreign backend's line never satisfies; a partial (unterminated) append is not a receipt yet. A line of the **delegation family** (the exec ledger — it carries a `backend` field too) is skipped on the same terms: a review waiter waits for a REVIEW answer, and an exec return is not one. An **exec receipt** placed in the receipts store is skipped on exactly the same terms: despite carrying both `backend` and `nonce`, it fails the review-receipt minimal core, so it never satisfies either a nonce-less or nonce-bound review wait. The two families are mutually inert **by construction**: this review waiter admits only a review receipt, while the exec waiter, `dispatch await`, states the mirror rule from its own side.
11
+ 3. **`--nonce`** (the dispatch nonce under the safe grammar `[A-Za-z0-9._-]{1,64}`): when the `{backend, nonce}`-named finding manifest exists beside the receipts file, the runner **prefers that correlation** — the manifest is minted atomically BEFORE the receipt append, and it carries the dispatch identity, so it can never be another dispatch's receipt. A malformed or foreign-identity manifest refuses loudly. An exec receipt standing where the `{backend, nonce}`-named finding manifest belongs is **refused as malformed**, never read as arrival: the manifest grammar is the door, and a sibling family's artifact does not pass it.
12
12
  4. **Timeout** (default 900s) fires ONLY when no receipt landed, and its wording **names the watermark**. An authoritative NEGATIVE verdict is not this tool's business — arrival is arrival, whatever the verdict says; the obligations verdict lives in `review-state`.
13
13
 
14
14
  **Exit codes:** `0` arrived; `1` timeout or a loud refusal (shrunken/rewritten store, malformed manifest, no git tree); `2` usage (including an unsafe nonce).
@@ -259,6 +259,13 @@ const CATALOG = [
259
259
  kind: READ_ONLY,
260
260
  oneLine: 'Check that the documented contract tokens still match the live code constants they describe — a read-only lint that fails closed on drift; --check turns it into a gate exit code.',
261
261
  },
262
+ {
263
+ key: 'dispatch',
264
+ invocation: invocationOf('dispatch'),
265
+ group: 'Orchestrate',
266
+ kind: WRITER,
267
+ oneLine: 'Measure delegation: check a sub-task brief’s contract block (form only — never whether the task is genuinely bounded), pre-register an acceptance wave with its thresholds, record one observation, open a delegated thread from that brief, wait for that one dispatch to answer — a wait that ends without an answer says so and authorizes nothing — absorb the wrapper’s receipt back into the ledger, fold the returned work or close the thread with a recorded degrade, and print the per-class report of how much a delegated sub-task actually bought, derived from what was dispatched, returned and folded. Writes only its own ledger file beside the repo; never commits.',
268
+ },
262
269
  {
263
270
  // NEVER `guarded` — that kind promises dry-run-first, which these writers do not have; the
264
271
  // honest strongest caution is `writer` with the destructive arm named in the line itself.
@@ -56,7 +56,9 @@ const GIT_MAX_BUFFER = 256 * 1024 * 1024; // a full-tree diff / TAP stream can b
56
56
 
57
57
  const gitRaw = (args, cwd) => spawnSync('git', args, { cwd, maxBuffer: GIT_MAX_BUFFER, windowsHide: true });
58
58
 
59
- const gitBuf = (args, cwd) => {
59
+ // Exported so a consumer computing over the SAME domain (the exec metric producer) reads git through
60
+ // this one runner instead of growing a second spawnSync wrapper with its own buffer bound.
61
+ export const gitBuf = (args, cwd) => {
60
62
  const r = gitRaw(args, cwd);
61
63
  if (r.error || r.status !== 0) return null;
62
64
  return r.stdout;
@@ -1012,6 +1014,26 @@ export const runDegrade = ({ cwd = process.cwd(), env = process.env, backend, re
1012
1014
 
1013
1015
  // ── the summary verb (D6): ONE stateless render — receipts + evidence store, no ledger ────────────
1014
1016
 
1017
+ // A red-proof binds the BYTES of the test file it was observed on, and the final-run checker refuses
1018
+ // when those bytes have moved (coverage-check.mjs, the hash-mismatch arm). This render used to print a
1019
+ // stale record byte-identically to a live one, so the only place staleness surfaced was that gate —
1020
+ // after both council dispatches, at the commit boundary, which is the most expensive discovery point
1021
+ // in the loop. The currency is therefore computed HERE, from the SAME two helpers the checker uses, so
1022
+ // the render and the gate can never disagree about what stale means.
1023
+ //
1024
+ // An unresolvable or unreadable file is STALE, not unknown: a proof nobody can re-check is not a proof
1025
+ // anyone should read as current.
1026
+ export const redProofCurrency = (rootTop, record) => {
1027
+ if (rootTop == null) return { state: 'stale', detail: 'the work-tree root is not resolvable' };
1028
+ const resolved = resolveTestFile(rootTop, record.file);
1029
+ if (!resolved.ok) return { state: 'stale', detail: resolved.reason };
1030
+ const current = hashFileBytes(resolved.abs);
1031
+ if (current == null) return { state: 'stale', detail: `cannot read "${record.file}"` };
1032
+ return current === record.fileHash
1033
+ ? { state: 'current' }
1034
+ : { state: 'stale', detail: 'the bound test file changed after the mint' };
1035
+ };
1036
+
1015
1037
  export const buildSummaryState = ({ cwd = process.cwd(), env = process.env } = {}) => {
1016
1038
  const fingerprint = computeTreeFingerprint(cwd);
1017
1039
  const base = resolveBase(cwd);
@@ -1026,7 +1048,10 @@ export const buildSummaryState = ({ cwd = process.cwd(), env = process.env } = {
1026
1048
  backend: b,
1027
1049
  summary: summarizeReviewReceiptsForTree(receipts.filter((r) => r.backend === b), fingerprint),
1028
1050
  }));
1029
- const redProofs = authoritativeOfKind(store.records, 'red-proof').filter((r) => r.base === base);
1051
+ const rootTop = gitLine(['rev-parse', '--show-toplevel'], cwd);
1052
+ const redProofs = authoritativeOfKind(store.records, 'red-proof')
1053
+ .filter((r) => r.base === base)
1054
+ .map((r) => ({ ...r, currency: redProofCurrency(rootTop, r) }));
1030
1055
  const degrades = authoritativeOfKind(store.records, 'degrade').filter((r) => r.fingerprint === fingerprint);
1031
1056
  const finalRun = authoritativeOfKind(store.records, 'final').find((r) => r.fingerprintBefore === fingerprint) ?? null;
1032
1057
  // A malformed/unreadable store makes the AUTHORITATIVE selection untrustworthy (a dropped later
@@ -1076,7 +1101,16 @@ export const renderSummary = (s) => {
1076
1101
  ? [` evidence sections WITHHELD — the store is unavailable (${s.storeMalformed} malformed line(s)${s.storeReadError ? `, read error: ${s.storeReadError}` : ''}); a dropped line could resurrect a superseded record — inspect ${s.storePath}`]
1077
1102
  : [
1078
1103
  ` red-proof records (current base): ${s.redProofs.length ? '' : '(none)'}`,
1079
- ...s.redProofs.map((r) => ` ${r.testId} — ${r.reds}/${r.runs} red, hash ${short(r.fileHash)}, pre-fix fingerprint ${short(r.fingerprint)}`),
1104
+ ...s.redProofs.map((r) => {
1105
+ const stale = r.currency?.state === 'stale';
1106
+ const mark = stale ? `STALE (${r.currency.detail})` : 'CURRENT';
1107
+ return ` ${r.testId} — ${mark} · ${r.reds}/${r.runs} red, hash ${short(r.fileHash)}, pre-fix fingerprint ${short(r.fingerprint)}`;
1108
+ }),
1109
+ // The recovery, printed once and only when it is needed — it was written down nowhere, and
1110
+ // the loop paid for that three times.
1111
+ ...(s.redProofs.some((r) => r.currency?.state === 'stale')
1112
+ ? [' ↳ a STALE proof is refused by run-gates --final: park the fix so the bound test fails again, re-observe it with `core-evidence red-proof "<testId>"`, then restore the fix. Edit test files FIRST and re-observe ONCE per park — every proof bound to an edited file goes stale together.']
1113
+ : []),
1080
1114
  ` degrade records (current tree): ${s.degrades.length ? '' : '(none)'}`,
1081
1115
  ...s.degrades.map((d) => ` ${d.backend} — ${d.reason} (${d.timestamp})`),
1082
1116
  ];
@@ -67,14 +67,15 @@ const RAW_BACKENDS = [
67
67
  roleContracts: {
68
68
  execute: {
69
69
  invocations: [
70
- 'codex-exec <plan-file|->',
71
- 'codex-exec <plan-file|-> -- <extra codex flags...>',
70
+ 'codex-exec [--nonce <n>] <plan-file|->',
71
+ 'codex-exec [--nonce <n>] <plan-file|-> -- <extra codex flags...>',
72
72
  ],
73
73
  grounding: "automatic — the root AGENTS.md (Hard Constraints) is auto-merged into codex's context and the wrapper prepends the orchestrator execution contract; no grounding flags",
74
74
  continue: [
75
- 'codex-exec --resume-last <plan-file|->',
76
- 'codex-exec --resume <session-id> <plan-file|->',
75
+ 'codex-exec --resume-last [--nonce <n>] <plan-file|->',
76
+ 'codex-exec --resume <session-id> [--nonce <n>] <plan-file|->',
77
77
  ],
78
+ receipt: "side effect — a NONCED run mints ONE exec receipt beside the delegation store: the dispatch nonce seam is the AW_DISPATCH_NONCE environment value or its plain-argument equivalent --nonce <n>, recognised ONLY before the prompt operand (after the operand or a literal '--' it is passthrough payload, never a flag), under the safe grammar [A-Za-z0-9._-]{1,64} — anything else, a duplicate, or a flag disagreeing with a non-empty env value refuses PRE-SPEND. The store directory resolves exactly as the kit's delegation store does: the dirname of an ABSOLUTE AW_DELEGATION_STORE (a relative one, or one ending in a path separator, refuses), else the git common dir. The artifact is agent-workflow-exec-receipt-<backendLength>-<backend>-<nonce>.json in two states: 'reserved' is written atomically and NO-CLOBBER immediately before the CLI runs — that write IS the nonce reservation, so a second dispatch on the same nonce, or an already-taken report name, refuses BEFORE any spend — and 'terminal' replaces it in place at exit. A nonced run also refuses pre-spend when no timeout/gtimeout binary can cap it (an accounted dispatch that cannot be capped can never honour the terminal-exit rule; a nonce-LESS run still warns and runs uncapped), when node is missing (the mint core), and when the prompt rides on stdin instead of a contract FILE — contractDigest is computed BY THIS WRAPPER from the dispatch file it was actually handed, so the kit can refuse a run that executed a different contract than the one it opened. That digest is taken from the SAME bytes already read as the prompt, never a second open of the path: two reads leave a window in which the file can be swapped, and the run would then execute one contract while its receipt claimed the digest of another. The header's own nonce must EQUAL the dispatch nonce — 'dispatch open' copies the nonce FROM the header, so a disagreeing --nonce could only reserve an identity no return would ever absorb, and it refuses pre-spend. A contract file edited BETWEEN 'dispatch open' and the run is caught at ABSORB by the contractDigest comparison, not pre-spend: the wrapper never reads the ledger, and that boundary is what the whole lane rests on. At exit the wrapper FIRST re-reads its reservation and verifies its own opaque owner token — a foreign owner refuses having published NOTHING, neither report nor receipt — THEN writes the delegate's final message atomically to agent-workflow-exec-report-<backendLength>-<backend>-<nonce>.txt, THEN re-verifies the owner and REPLACES the reservation with the terminal receipt {schema, kind, state, backend, nonce, owner, contractDigest, wrapperVersion, posture {model, effort, tier}, capS, killGraceS, sessionId, exitStatus, outcome, reportDigest, reportLength, timestamp}: the report is complete on disk before any artifact says the run arrived. capS and killGraceS are the cap the run ACTUALLY applied. outcome is the wrapper's own SUBSET of the ledger's vocabulary — exit 0 with a session id -> success, exit 0 without one -> missing-identity, ANY nonzero exit including the timeout codes 124 and 137 -> transport-failure; every orchestrator judgment is recorded at absorb time, never claimed here. The session id is captured BEFORE outcome branching, so a FAILED run records one too; in resume mode it is the validated resume id. FAIL-CLOSED, deliberately NOT the review lane's warn-only receipt: a publication that cannot complete exits nonzero with a DISTINCT status, and the message states only what the run can still prove. 70: the reservation could not be verified BEFORE any publication — NOTHING was published, not the report and not the receipt, and because the artifact found there belongs to another run it is never a '--no-receipt' source. 71: a publication stopped after that point — either the report write failed (nothing beyond the reservation was published; the '--no-receipt' absorb then records reportLength 0, ineligible by the name empty-report) or the report IS on disk and the terminal receipt was not completed (the absorb reads it, report-if-present). The post-report lane never claims the reservation still stands, because after that point its fate is no longer something this run observed. Every lane names the tree as partial/dirtied rather than untouched. A nonce-LESS invocation is byte-unchanged: no reservation, no receipt, no artifact, no node.",
78
79
  passthrough: {
79
80
  policy: 'guarded',
80
81
  blocked: ['-c*', '--config*', '-s*', '--sandbox*', '--dangerously-bypass-approvals-and-sandbox', '--dangerously-bypass-hook-trust', '--full-auto', '--oss', '--local-provider*', '-p*', '--profile*', '-m*', '--model*', '-o*', '--output-last-message*', '--json*', '--color*', '--output-schema*', '--ephemeral*'],
@@ -324,7 +324,11 @@ const FIELD_CHECKS = {
324
324
  firstPassNum: { ok: isByteCount, want: 'a non-negative integer first-pass numerator' },
325
325
  firstPassDen: { ok: (v) => Number.isSafeInteger(v) && v >= 1, want: 'a positive integer first-pass denominator' },
326
326
  vehicle: { ok: isPlainObject, want: 'the closed vehicle pair {requested, selected}' },
327
- backend: { ok: isNonEmptyString, want: 'a non-empty backend name' },
327
+ // The safe token grammar, not merely non-empty: the backend NAMES an artifact beside the store
328
+ // (the exec receipt and its report), so a name outside this grammar records a dispatch whose own
329
+ // receipt could never be written. One grammar on both sides, or the ledger accepts what the
330
+ // wrapper cannot mint.
331
+ backend: { ok: isSafeName, want: 'a backend name in the safe token grammar ([A-Za-z0-9._-]{1,64})' },
328
332
  contractDigest: { ok: isHex64, want: 'the 64-hex digest of the canonical contract header' },
329
333
  preTreeDigest: { ok: isHex64, want: 'the 64-hex uncommitted-state fingerprint at dispatch' },
330
334
  postTreeDigest: { ok: isHex64, want: 'the 64-hex uncommitted-state fingerprint after the run' },
@@ -427,8 +431,11 @@ const validateReturnCrossFields = (r) => {
427
431
  if (r.metric.eligible) {
428
432
  return refuse(`return: this return's own fields make the metric INELIGIBLE (${local.ineligibleReason}) — an eligible metric is never claimed over them`);
429
433
  }
430
- if (r.metric.ineligibleReason !== local.ineligibleReason && r.metric.ineligibleReason !== 'dirty-baseline') {
431
- return refuse(`return: metric.ineligibleReason "${r.metric.ineligibleReason}" contradicts this return's own fields (locally "${local.ineligibleReason}") only "dirty-baseline" may override`);
434
+ // The locally PROVABLE name wins outright: "dirty-baseline" is admissible only where the return's
435
+ // own fields leave the metric eligible, so it can never stand in for a reason a reader could
436
+ // have checked. (The store then binds that ONE override to the dispatch's recorded baseline.)
437
+ if (r.metric.ineligibleReason !== local.ineligibleReason) {
438
+ return refuse(`return: metric.ineligibleReason "${r.metric.ineligibleReason}" contradicts this return's own fields (locally "${local.ineligibleReason}") — the provable reason is the one recorded, and "dirty-baseline" never replaces it`);
432
439
  }
433
440
  } else if (!r.metric.eligible && r.metric.ineligibleReason !== 'dirty-baseline') {
434
441
  return refuse(`return: metric.ineligibleReason "${r.metric.ineligibleReason}" is not substantiated by this return's own fields — when the local evaluation finds the metric eligible, only "dirty-baseline" (the store-verified override) may be recorded`);