feat(kernel): bound, hash-verified evidence and the verified-evidence completion policy (slice 3/5) - #102
Conversation
The content-addressed object store lands a declared objects/ subtree under .loop/artifacts/. Listing files only keeps the anti-litter teeth (a duplicate bundle or an orphan .staged still fails) without pinning the directory shape. Green against the unmodified writer as well as the new one.
…, hash-verified records Adds the second completion mode. Under all_required_verified_evidence a Succeeded terminal may only cite evidence@1 records that are self-consistent (the bundle hashes to the digest the record declares), bound into the event chain at their CURRENT bytes, and in agreement with the live TASKS.json goalpost. Hash-verification alone is forgeable by anything with .loop/ write access, so the two layers that hold a workspace (emit.terminate, doctor) enforce all three sub-checks through ONE definition — contract._strict_evidence_failure — while the two pure layers (the reducer fold and integrations.to_terminal_state) keep only the structural half rather than pretend to verify what they cannot reach. An unreadable event store is a refusal, never a skip. Three changes reach beyond the bar itself and are called out deliberately: * loop/simulate.py — _predict no longer restates the auto-terminal payload literally. That literal was a second definition of the very thing this change edits, and the predicted-equals-real pin (test_loop_simulate_cli.py:101) is what would have caught the drift only after it shipped. It now builds the payload with the runner's own runner._auto_terminal_payload. Same anti-drift rule this slice makes load-bearing for _strict_evidence_failure, applied to the payload. * scripts/test_loop_simulate_cli.py:86 — the auto-terminal snapshot gains the "reason" key the payload genuinely now carries. Whole-dict equality is preserved on purpose: the assertion is not relaxed to a subset check, and the reason is not dropped, because a silently weaker policy with no stated cause is the downgrade this mode exists to forbid. * loop/runner.py — the auto-terminal now adopts the strict mode honestly. It evaluates the same shared predicate against every candidate record BEFORE it chooses the mode and BEFORE it appends terminal_written. Previously it adopted the mode on evidence FILE EXISTENCE and let emit.terminate apply the real bar afterwards, which both overstated the bar and left the event durable before raising — the committed-then- refused seam this slice removes. When the bar is not met the payload stays all_required with evidence ["RUNLOG.md"] and a reason that names why, distinguishing a task that produced no record at all from a record present but failing a sub-check. Suite: 1211 passed / 16 skipped (pyyaml+jsonschema), 1117 passed / 110 skipped (pyyaml only), each with the one governed intermediate red owned by the verifier- identity task. Interpreter matrix 3.12 and 3.13 green. Both dogfood examples stay doctor-clean.
…ure one The workspace-bearing layers checked the record's internal uri for containment but never the cited entry path itself, so a store-less contract could write a Succeeded terminal citing a record outside the workspace -- accepted by emit.terminate, refused by the reducer replaying that same terminal. Reuse the pure layers' own shape predicate so the four layers agree on what Succeeded means.
…ll misses Flips four Slice-2 pins in place (renamed with bodies, residual halves kept, each docstring naming the release that changed the behavior) and adds scripts/test_adversarial_evidence_binding.py - 14 pins stating exactly what chain-bound evidence catches and what it provably does not. Pins 13 and 14 hold decision 14 two residuals. Pin 13 corrects the plan claim that doctor missing_event_store tripwire covers the delete-the-store route: measured on a real dispatch it does not, because that tripwire is gated on leftover -wal/-shm sidecars and sidecar-free reads leave none, so deleting the store makes plain doctor report ok with zero issues. The pin asserts that measured silence and uses --expect-chain-head as its positive control. Also discharges carry-forward 11: the workspace-root symlink-loop variant of the argv[0] pin, green on 3.12 (pathlib RuntimeError) and 3.13 (OSError).
…hat still is not caught
… has it The parenthetical credited _self_verified, which is the independence check behind self_verified_evidence and plays no part in the goalpost comparison. The rename escape is _policy_digest_issues skipping a task_id that TASKS.json no longer declares.
…hentic The strict mode verified everything about a cited evidence record except what it said. A dispatch whose verifier FAILED wrote a genuine, hash-verified, chain-bound record of that failure, and emit.terminate(state="Succeeded", ..., completion_policy="all_required_verified_evidence") accepted it: doctor then reported ok with zero issues and the reducer replayed Succeeded. Every layer proved the evidence was real; none asked whether it said the work passed. _strict_evidence_failure gains a verdict sub-check, judged by the repo's one green-marker rule. That rule lived only in scripts/metrics.py (a bundle is green when outcome == "PASS" or passed is True; a score-only bundle reads RED), so it is hoisted to loop.evidence.verify_bundle_is_green and metrics now imports it — a bundle that reads RED to the FCR gate can no longer read GREEN to the completion gate. Decided explicitly: a record whose kind is not verify-bundle carries no verdict this layer can read, and is REFUSED rather than waved through. Unreadable, digest-drifted or unparseable verdicts are refusals, never skips (R007). The pure layers are untouched by design (decision 14): the reducer and the integrations projection hold no filesystem, so they still check shape only.
…in the walk
Two defects lived in the gap between the write-time and read-time views of "bound".
An APPEND-ONLY forge laundered tampered bytes past emit, with no trigger drop and no
re-chain: bound_artifact_digests built a {path: sha256} comprehension over every
event, so a path bound twice collapsed LAST-WINS. Rewrite a bound record (terminate
correctly refuses — "bound at a different digest"), then append ONE ordinary event
binding that path at the NEW digest, and the writer accepted the forged tree while
_bound_evidence_issues — which checks PER EVENT — still reported
evidence_chain_mismatch on it. That layer disagreement is what ruling R9 called the
defect, and §17 claims the writer can never accept a terminal its own replay would
refuse.
The view now returns every DISTINCT digest a path was bound at, in first-bound order
(the ordinary repeat-binding case stays a one-element tuple), and the strict bar
refuses a path carrying more than one: ambiguous is not proof. Both layers now refuse
the same trees. bound_artifact_digests shipped with no direct test at all (Task-3
carry-forward); it has one now.
The binding walk also read whatever a path declared. event@1 constrains it only to a
non-empty string, so an appended event could make loop doctor open and hash
/etc/hostname; the ledger's justification — that the chain hash covers
artifact_hashes — is wrong, because the chain prevents retroactive edits to a
binding, not a fresh append naming an arbitrary path. Every declared path is now
containment-checked with ZERO filesystem access before anything is opened (absolute,
drive-lettered, backslashed and ..-traversing paths are reported as the new
bound_evidence_escape and never read), symlinked escapes are caught after resolution
exactly as verify_evidence catches them, and the read itself is bounded: non-regular
files are refused rather than streamed forever, and the hash runs in chunks under a
64 MiB cap.
The goalpost sweep dropped every record outside the runner's evidence-iter<N>.json name, on the reasoning that such a record carries no position in the run's order and so can never be shown to be latest. That created a hole: a record with no iteration id is still the LATEST when it is the only one for its task. A store-less contract whose single hand-written record was named evidence-hand.json stayed doctor-clean with zero issues after its goalpost moved, while the identical record renamed evidence-iter9.json fired policy_digest_mismatch. §17 and §22 meanwhile both claim "the latest evidence record for a task" is compared. Latest now RANKS unnumbered records below every numbered one, ties broken by filename — the same deterministic tie-break the numbered path already used — so the only record for a task is always compared. Decision 5 is untouched: still only the latest record per task, and a record naming a task absent from TASKS.json is still never compared.
test_every_new_doctor_code_is_documented_in_section_22 grepped the WHOLE contract document, and every code it names also appears in §17's tier list — so deleting all four §22 rows left the pin green. Verified: with the rows removed, a whole-file backtick search still hits all four. This is the pin a PR body would cite as evidence that the codes are documented, and it documented nothing. It now slices §22's New issue codes table the way the sibling §17 pin already slices its section, and covers bound_evidence_escape as well. Proven to discriminate by deleting the five rows and observing the failure.
… read Two typed-error escapes. doctor's chain-binding walk is a THIRD independent read of the event store, and it sat OUTSIDE the except RuntimeStoreError that its two siblings (status_report, replay_report) share. A store that became unreadable between reads escaped doctor_report as an untyped traceback instead of the typed finding an errored check owes (R007). write_verify_evidence read the existing object from inside its except FileExistsError handler, where the sibling except OSError cannot reach — so an IsADirectoryError or PermissionError at the content-addressed object path escaped the writer untyped. The collision read is now wrapped, and the pre-existing typed refusal for a genuine digest collision is unchanged.
…changes §17 now describes the bar this branch actually ships — four sub-checks, not three: the verdict check and its one green-marker rule (a score-only bundle reads red in both the FCR gate and the completion gate; a non-verify-bundle kind carries no verdict and is refused), and why an ambiguous binding is refused rather than collapsed. The latest-per-task rule is restated to say that unnumbered records are ranked in, not dropped. New "Upgrade notes" block, stated without hedging because each one can bite an existing user: - policy_digest_mismatch is NOT opt-in. A contract clean at v0.10.0 goes red on upgrade after an ordinary goalpost edit, and the documented remedy — re-verify — does not generally help, because done-ness comes from the event log so the runner never re-verifies. Measured, the reliable route back to green is renaming the task, which is the very evasion the tier list admits the check cannot catch. The operator's real options are named, and the absent re-baseline affordance is named as absent. - The completion_policy enum widening is forward-incompatible as a HARD ERROR: an older kernel reports invalid_completion_policy + schema_violation rather than degrading, which matters for the README's pinned @v0.10.0 action and for uvx loop-engineer@0.10.0. - os.link now runs once PER DISPATCH rather than once per run, so a filesystem without hard-link support turns every dispatch into a committed-then-failed iteration. Named as a known limitation; there is deliberately no non-atomic fallback.
…ose four edges
The Check (3) paragraph asserted that refusing an ambiguous binding "keeps the
writer from ever accepting a terminal its own replay would refuse" -- unconditional,
and false. The write-time bar examines the entries a terminal cites and the artifact
each uri names; it does not walk the other paths those events bound. The two layers
agree about every path a terminal rests on, which is the claim; they are not two
spellings of the same sweep.
Also: pin that the canonical green-marker OR rule accepts a self-contradicting
{outcome: PASS, passed: false} bundle, since tightening it here alone would make one
bundle green to the completion bar and red to the FCR gate that shares the object;
record the 64 MiB bound-artifact read cap as a fourth un-remediable upgrade note;
stop claiming a malformed bound path is "outside the workspace" when it is not a
path at all; and close an fd leak when os.fdopen raises after os.dup succeeds.
There was a problem hiding this comment.
Pull request overview
This PR advances the tamper-evident provenance work by making verification evidence content-addressed, binding artifact digests into the Slice-1 event hash chain at append time, and enforcing/validating those guarantees via loop doctor and an opt-in “verified evidence” completion policy.
Changes:
- Add a content-addressed object store for verify bundles and bind
{path, sha256}entries (bundle, evidence record, object) intoiteration_appendedevents. - Extend
loop doctorto hash-verify evidence records’ referenced artifacts, comparepolicy_digestagainst liveTASKS.json, and re-hash chain-bound artifacts (with containment + read caps). - Introduce
all_required_verified_evidencecompletion mode (write-time + read-time enforcement; pure layers enforce shape only) and align the metrics “green bundle” rule with kernel logic; update docs and add extensive tests/pins.
Reviewed changes
Copilot reviewed 32 out of 32 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/test_verify_evidence_writer.py | Adjusts writer test expectations for artifacts directory contents. |
| scripts/test_verify_evidence_objects.py | New tests for purity + object store placement + artifact hash coverage. |
| scripts/test_verify_evidence_object_collision.py | New tests ensuring object-collision handling is typed (no raw OSError). |
| scripts/test_verifier_identity.py | Adds a regression test for workspace-root resolve failures. |
| scripts/test_metrics_verifier_source.py | New tests around excluding injected_callable bundles from metrics. |
| scripts/test_loop_simulate_cli.py | Updates simulate expectations to include auto-terminal reason. |
| scripts/test_evidence_binding_runner.py | New tests asserting digest binding into iteration events and hash coverage. |
| scripts/test_doctor_store_read_guard.py | New test ensuring all store reads are guarded (typed findings). |
| scripts/test_doctor_evidence.py | Updates fixtures for doctor’s new hash-verification behavior. |
| scripts/test_doctor_evidence_verification.py | New tests for record hash verification + policy_digest comparisons and ranking rules. |
| scripts/test_doctor_evidence_binding.py | New tests for doctor’s chain-binding walk and legacy-store behavior. |
| scripts/test_conformance.py | Adds doc pins tying implementation details to contract documentation. |
| scripts/test_completion_verified_evidence.py | New suite covering the strict completion policy behavior across layers. |
| scripts/test_completion_verdict.py | New suite ensuring strict mode requires evidence of PASS (verdict check). |
| scripts/test_bound_artifact_view.py | New tests for conflict-aware bound-digest view + containment guarantees. |
| scripts/test_adversarial_verifier_identity.py | Updates adversarial pins to reflect new doctor checks + chain-bound detection. |
| scripts/test_adversarial_evidence_binding.py | New adversarial suite pinning honest limitations + positive controls. |
| scripts/metrics.py | Factors green-marker rule into kernel helper; excludes injected-callable bundles and reports provenance. |
| schemas/terminal.schema.json | Widens completion policy enum to include all_required_verified_evidence. |
| schemas/evidence.schema.json | Updates schema description to reflect new shipped verification/binding behavior. |
| reference/safety-and-approvals.md | Documents new goalpost check (policy_digest_mismatch) and limits. |
| reference/repo-os-contract.md | Major doc update: bound evidence set, strict completion mode, new issue codes, upgrade notes. |
| README.md | Updates top-level claim to “anchorable chain-bound evidence” (not tamper-proof). |
| loop/simulate.py | Uses runner’s shared auto-terminal payload builder to avoid drift. |
| loop/runtime.py | Adds chain-bound artifact verification + conflict-aware bound_artifact_digests. |
| loop/runner.py | Binds artifact hashes pre-append, writes evidence post-append, adds auto-terminal strict-mode adoption logic. |
| loop/reducer.py | Enforces strict-mode evidence shape at pure replay time. |
| loop/integrations.py | Enforces strict-mode evidence shape in pure projection (fail closed). |
| loop/evidence.py | Adds shared green-marker predicate + bound-artifact hashing helper with containment and caps. |
| loop/emit.py | Splits build vs write for verify evidence; writes content-addressed object; enforces strict evidence bar at terminate. |
| loop/contract.py | Doctor now composes verify_evidence, compares policy digests, enforces strict terminals with shared predicate. |
| loop/completion.py | Adds all_required_verified_evidence policy and shape-only helper predicates. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| try: | ||
| existing = hashlib.sha256(built.object_path.read_bytes()).hexdigest() |
| from .evidence import VERIFY_BUNDLE_KIND, verify_bundle_is_green | ||
|
|
||
| kind = record.get("kind") | ||
| if kind != VERIFY_BUNDLE_KIND: | ||
| return (f"is a {kind!r} record, not a {VERIFY_BUNDLE_KIND}: it carries no verdict, " | ||
| f"so it cannot show that anything passed") | ||
| uri = record["uri"] | ||
| try: | ||
| blob = (paths.workspace / uri).read_bytes() | ||
| except (OSError, ValueError) as exc: | ||
| return f"cites a verify bundle that cannot be read ({exc})" |
| root = Path(workspace_root) | ||
| try: | ||
| resolved = (root / str(rel)).resolve(strict=True) | ||
| except (OSError, ValueError, RuntimeError): | ||
| return "unreadable", "is absent or cannot be resolved" | ||
| try: | ||
| resolved.relative_to(root.resolve()) | ||
| except ValueError: | ||
| return "escape", "resolves outside the workspace (a symlinked component)" |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 42c0931732
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| artifact_hashes = tuple( | ||
| {"path": path.relative_to(paths.workspace).as_posix(), "sha256": digest} | ||
| for path, digest in ((bundle_path, bundle_sha256), (record_path, record_sha256), | ||
| (object_path, bundle_sha256)) |
There was a problem hiding this comment.
Keep bound writer artifacts below the verification cap
When a supported injected verifier returns a summary large enough to make the bundle exceed 64 MiB, this code still binds both the oversized bundle and its content-addressed copy and write_verify_evidence writes them successfully. The next loop doctor run checks these bindings through hash_bound_artifact, whose fixed 64 MiB cap classifies both writer-generated files as missing_bound_evidence; thus a successful dispatch can immediately produce a contract the doctor rejects. Cap or reject the rendered artifacts before committing their bindings.
Useful? React with 👍 / 👎.
| return (isinstance(entry, str) and entry.startswith(_RECORD_PREFIX) | ||
| and entry.endswith(".json") and ".." not in entry) |
There was a problem hiding this comment.
Reject nested paths outside the flat evidence namespace
For an entry such as .loop/evidence/subdir/record.json, this predicate returns true even though evidence discovery in _validate_evidence_records uses the flat .loop/evidence/*.json namespace. Consequently emit.terminate and reducer replay can accept that record under the verified-evidence policy while the normal doctor sweep never discovers it, allowing checks performed only by that sweep—such as self_verified_evidence—to be bypassed. Require a non-empty filename with no additional slash after the prefix.
Useful? React with 👍 / 👎.
… (#104) Committed as an executed artifact, not as current truth. Execution proved four of its statements wrong and they are NOT corrected here, so the record shows what was planned against what shipped: the bound order is bundle -> record -> object (the plan says bundle -> object -> record); doctor's missing_event_store tripwire does NOT catch a store deleted after an ordinary run (the external anchor does); several test counts are stale; and four of its proposed doc assertions were non-discriminating. The shipped reference docs and tests are authoritative wherever they disagree.
Makes the evidence a verified dispatch writes load-bearing: artifacts are content-addressed and their digests are bound into the Slice-1 hash chain at append time;
loop doctorhash-verifies every discovered record and compares itspolicy_digestagainst the liveTASKS.jsonentry; andSucceededgains an opt-in completion mode whose bar is evidence that is hash-verified, chain-bound, goalpost-agreeing and attesting a pass — rather than non-empty path strings.Slice 3 of the tamper-evident-provenance program. Plan:
docs/superpowers/plans/2026-07-25-slice3-evidence-wiring.md(committed separately).The claim, stated exactly
The shipped claim is evidence whose bytes are bound into an anchorable chain. It is not "tamper-proof evidence", and §17 names that as the forbidden claim.
all_required_verified_evidenceis opt-in and never retroactive. Its bar, per cited entry, is four checks at the two layers that hold a workspace (emit.terminateat write time,loop.contractat read time — one shared predicate,_strict_evidence_failure, with three callers):.loop/evidence/*.jsonpath, the same check the pure layers apply, so the writer can never accept an entry its own replay would refuse;uriresolves inside the workspace and hashes to the digest it declares;loop.reducerandloop.integrations.to_terminal_stateare pure, hold no workspace, and enforce the shape half only. Making them appear to verify binding would be pretending.Two residuals, both pinned and both documented. (i) A contract with no event store — the writer-API path — cannot chain-bind anything, so the mode degrades to shape + self-consistency + verdict + goalpost, and a hand-written record satisfies it. (ii) Chain-binding proves bytes, never provenance: a worker who can rewrite
.loop/can rewrite the store, so without--expect-chain-heada full rewrite of artifacts and chain verifies clean.Note on residual (i): the plan asserted doctor's
missing_event_storetripwire catches a store deleted out from under a real run. It does not. Measured three times independently — an ordinary dispatch leaves no-wal/-shmsidecars (sidecar-free reads, v0.10.0), the tripwire is gated on that residue, and deleting.loop/events.dbleaves plainloop doctoratok: truewith zero issues. The compensating control that does hold is the external anchor:doctor --expect-chain-head <head>reportschain_anchor_mismatch. The docs say the measured truth;test_the_strict_mode_accepts_a_hand_written_record_in_a_store_less_contract_pinnedasserts it.Baselines — fresh worktree, dependency set named per number
main@c1d5a93144 = 135 + 9: nine new tests are jsonschema-gatedstricthalves.self_eval13/13,validate_frontmatter9/9. Both dogfood examples stay doctor-clean,loop inspect examples/coverage-repairstill scores 90, andscripts/metrics.pyon both examples is unchanged (FCR 0.0, RP 1.0,evidence_backedtrue,injected_verifier_bundlesempty).Mutation probes — predicted vs observed
Every kill count below was predicted before running, independently by the implementer and by the reviewer, then observed. All three agree.
_bound_evidence_issues→pass_policy_digest_issuesbody →returnverify_evidencecomposition removed_strict_evidence_failure's chain-boundness → always pass_strict_evidence_failure's goalpost → always passThe plan's numbers are stale in three of five cases for two verified reasons: it counted a
parametrize("mode", _MODES)test as one item where the extras leg collects two, and six governor-ruled tests post-date it. Probe 4 is the one that matters — chain-boundness is killed at the write layer, the read layer and a pin control, so it is enforced rather than described. No probe killed zero.Honest limitations: four flipped in place, none deleted
Per the "FLIP, never delete" constraint, each keeps its residual half and its docstring names the release that changed it. The file's collected count is unchanged (13 → 13) and
_pinned(went 10 → 6.test_rewriting_the_evidence_record_to_independent_identities_leaves_doctor_clean_pinnedtest_rewriting_the_evidence_record_is_caught_only_when_the_chain_binds_ittest_deleting_both_bundle_and_record_leaves_doctor_clean_pinnedtest_deleting_both_bundle_and_record_is_caught_only_when_the_chain_binds_themtest_no_automated_digest_comparison_exists_pinnedtest_policy_digest_comparison_fires_only_against_a_live_task_entrytest_doctor_does_not_hash_verify_the_referenced_bundle_pinnedtest_doctor_hash_verifies_the_referenced_bundlescripts/test_adversarial_evidence_binding.pyadds 14 pins stating what binding does and does not catch; pins 13 and 14 are decision 14's two residuals, each with a positive control so they bound a hole rather than merely documenting one.Whole-branch review — one BLOCKER and six MAJORs, found and closed
Seven per-task reviews passed this branch. A three-lens whole-branch review (kernel-correctness, threat-honesty, completeness) then returned REPAIR on all three lenses, because per-task review checks a task against its brief while this one attacked the assembled system. Every finding was reproduced with a probe before being fixed, and each fix carries a mutation negative control.
task_failed, binding a bundle reading{"outcome":"FAIL","passed":false}, was accepted byemit.terminateunder the strict mode; doctor returnedok: trueand the reducer replayedSucceeded. Every layer verified the evidence was authentic; none verified it said the thing passed. Fixed by adding the verdict check, and by hoisting the repo's canonical green-marker rule out ofscripts/metrics.pyintoloop.evidence.verify_bundle_is_greenso the FCR gate and the completion gate share one object — a bundle that reads RED to metrics can no longer read GREEN to completion. Ascore-only bundle reads RED; a non-verify-bundlekindis refused, not skipped.bound_artifact_digestscollapsed duplicate paths last-wins while the doctor walk checks per event, so appending one ordinary event re-binding a path at a new digest laundered a tampered record past the writer while doctor still condemned the same tree — no trigger drop, no re-chain. The view now returns the conflict set and an ambiguous binding is refused. It also gained its first direct tests./etc/hostnameor../../../../etc/hostnamemadeloop doctoropen and hash files outside the workspace, unbounded. Now: lexical containment before any filesystem access, then resolution, thenO_NOFOLLOW+S_ISREG+ a 64 MiB cap. Escapes report the newbound_evidence_escapeand are never read. The reviewer proved zero out-of-workspace opens by instrumentingos.open/io.open/builtins.openacross a whole doctor call.test_every_new_doctor_code_is_documented_in_section_22could not fail (it grepped the whole file, and the codes also appear in §17, so the §22 rows could be deleted with the pin still green). Now scoped to the §22 table.RuntimeStoreErrorguard (R007), and the object-collision handler could raise a rawOSError.Behavioural changes, stated without hedging
reference/repo-os-contract.mdcarries four upgrade notes. Two can turn a previously-clean contract red:policy_digest_mismatchis not opt-in and its remedy is currently unsatisfying. An ordinary goalpost edit after a recorded verification makes the contract red, and because done-ness comes from the event log the runner will not re-verify a task it considers done. A first-class re-baseline affordance does not exist yet.completion_policyenum widening is forward-incompatible as a hard error — an older kernel reportsinvalid_completion_policy+schema_violationrather than degrading. Pin the gate and the writer to the same release.os.linknow runs once per dispatch, not once per run, so a filesystem without hard-link support turns every dispatch into a committed-then-failed iteration.Not claimed
M-1 (
_EVIDENCE_SCHEMA_IDliteral fork), M-7 (§17'sunresolvablerow) and M-12 (the "three honest tiers" heading) were already resolved onorigin/mainbefore this branch and are not fixed here.The plan itself is wrong in three places this branch does not repeat: the bound order is
bundle → record → object(notbundle → object → record); themissing_event_storecompensating-control sentence; and several stale test counts. Four of its own proposed doc assertions were non-discriminating and were strengthened.Test plan
mainself_eval13/13,validate_frontmatter9/9inspect90; metrics unmoved