Skip to content

feat(kernel): bound, hash-verified evidence and the verified-evidence completion policy (slice 3/5) - #102

Merged
SollanSystems merged 22 commits into
mainfrom
feat/evidence-wiring
Jul 26, 2026
Merged

feat(kernel): bound, hash-verified evidence and the verified-evidence completion policy (slice 3/5)#102
SollanSystems merged 22 commits into
mainfrom
feat/evidence-wiring

Conversation

@SollanSystems

Copy link
Copy Markdown
Owner

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 doctor hash-verifies every discovered record and compares its policy_digest against the live TASKS.json entry; and Succeeded gains 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_evidence is opt-in and never retroactive. Its bar, per cited entry, is four checks at the two layers that hold a workspace (emit.terminate at write time, loop.contract at read time — one shared predicate, _strict_evidence_failure, with three callers):

  1. shape — a workspace-relative .loop/evidence/*.json path, the same check the pure layers apply, so the writer can never accept an entry its own replay would refuse;
  2. self-consistency — a readable evidence@1 record whose uri resolves inside the workspace and hashes to the digest it declares;
  3. verdict — the bundle it cites must attest a pass;
  4. chain-boundness (only where a store exists) and goalpost agreement (where the record names a live task).

loop.reducer and loop.integrations.to_terminal_state are 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-head a full rewrite of artifacts and chain verifies clean.

Note on residual (i): the plan asserted doctor's missing_event_store tripwire catches a store deleted out from under a real run. It does not. Measured three times independently — an ordinary dispatch leaves no -wal/-shm sidecars (sidecar-free reads, v0.10.0), the tripwire is gated on that residue, and deleting .loop/events.db leaves plain loop doctor at ok: true with zero issues. The compensating control that does hold is the external anchor: doctor --expect-chain-head <head> reports chain_anchor_mismatch. The docs say the measured truth; test_the_strict_mode_accepts_a_hand_written_record_in_a_store_less_contract_pinned asserts it.

Baselines — fresh worktree, dependency set named per number

main @ c1d5a93 this branch delta
pyyaml + jsonschema + pytest 1161 passed / 18 skipped 1305 passed / 18 skipped +144 passed
pyyaml + pytest (structural fallback) 1076 passed / 103 skipped 1211 passed / 112 skipped +135 passed, +9 skipped
Python 3.10, extras 1305 passed / 18 skipped

144 = 135 + 9: nine new tests are jsonschema-gated strict halves. self_eval 13/13, validate_frontmatter 9/9. Both dogfood examples stay doctor-clean, loop inspect examples/coverage-repair still scores 90, and scripts/metrics.py on both examples is unchanged (FCR 0.0, RP 1.0, evidence_backed true, injected_verifier_bundles empty).

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.

# Stub Plan Predicted Observed
1 _bound_evidence_issuespass 7 9 9
2 _policy_digest_issues body → return 3 6 6
3 the verify_evidence composition removed 5 6 6
4 _strict_evidence_failure's chain-boundness → always pass 3 3 3
5 _strict_evidence_failure's goalpost → always pass 1 2 2

The 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.

was is now
test_rewriting_the_evidence_record_to_independent_identities_leaves_doctor_clean_pinned test_rewriting_the_evidence_record_is_caught_only_when_the_chain_binds_it
test_deleting_both_bundle_and_record_leaves_doctor_clean_pinned test_deleting_both_bundle_and_record_is_caught_only_when_the_chain_binds_them
test_no_automated_digest_comparison_exists_pinned test_policy_digest_comparison_fires_only_against_a_live_task_entry
test_doctor_does_not_hash_verify_the_referenced_bundle_pinned test_doctor_hash_verifies_the_referenced_bundle

scripts/test_adversarial_evidence_binding.py adds 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.

  • BLOCKER — the strict mode accepted evidence of FAILURE. A dispatch recording task_failed, binding a bundle reading {"outcome":"FAIL","passed":false}, was accepted by emit.terminate under the strict mode; doctor returned ok: true and the reducer replayed Succeeded. 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 of scripts/metrics.py into loop.evidence.verify_bundle_is_green so the FCR gate and the completion gate share one object — a bundle that reads RED to metrics can no longer read GREEN to completion. A score-only bundle reads RED; a non-verify-bundle kind is refused, not skipped.
  • MAJOR — write-time and read-time views of "bound" disagreed. bound_artifact_digests collapsed 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.
  • MAJOR — the binding walk had no workspace containment. An event binding /etc/hostname or ../../../../etc/hostname made loop doctor open and hash files outside the workspace, unbounded. Now: lexical containment before any filesystem access, then resolution, then O_NOFOLLOW + S_ISREG + a 64 MiB cap. Escapes report the new bound_evidence_escape and are never read. The reviewer proved zero out-of-workspace opens by instrumenting os.open/io.open/builtins.open across a whole doctor call.
  • MAJOR — the goalpost sweep skipped unnumbered records, so the only record for a live task could carry a stale goalpost with doctor clean while the docs claimed "the latest evidence record for a task" was compared. Unnumbered records are now ranked below numbered ones instead of skipped.
  • MAJOR — test_every_new_doctor_code_is_documented_in_section_22 could 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.
  • Two MINORs: the third store read sat outside the RuntimeStoreError guard (R007), and the object-collision handler could raise a raw OSError.

Behavioural changes, stated without hedging

reference/repo-os-contract.md carries four upgrade notes. Two can turn a previously-clean contract red:

  1. policy_digest_mismatch is 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.
  2. The completion_policy enum widening is forward-incompatible as a hard error — an older kernel reports invalid_completion_policy + schema_violation rather than degrading. Pin the gate and the writer to the same release.
  3. os.link now runs once per dispatch, not once per run, so a filesystem without hard-link support turns every dispatch into a committed-then-failed iteration.
  4. A bound artifact above the 64 MiB read cap fails doctor with no configuration knob.

Not claimed

M-1 (_EVIDENCE_SCHEMA_ID literal fork), M-7 (§17's unresolvable row) and M-12 (the "three honest tiers" heading) were already resolved on origin/main before 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 (not bundle → object → record); the missing_event_store compensating-control sentence; and several stale test counts. Four of its own proposed doc assertions were non-discriminating and were strengthened.

Test plan

  • extras and structural-fallback suites, fresh worktree, both reconciled against a freshly-measured main
  • Python 3.10 and 3.13
  • self_eval 13/13, validate_frontmatter 9/9
  • both dogfood examples doctor-clean; inspect 90; metrics unmoved
  • five mutation probes, predicted and observed
  • every whole-branch-review finding re-attacked from its original description after repair

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).
… 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.
Copilot AI review requested due to automatic review settings July 26, 2026 01:56
@SollanSystems
SollanSystems merged commit 82be934 into main Jul 26, 2026
14 checks passed
@SollanSystems
SollanSystems deleted the feat/evidence-wiring branch July 26, 2026 01:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) into iteration_appended events.
  • Extend loop doctor to hash-verify evidence records’ referenced artifacts, compare policy_digest against live TASKS.json, and re-hash chain-bound artifacts (with containment + read caps).
  • Introduce all_required_verified_evidence completion 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.

Comment thread loop/emit.py
Comment on lines +574 to +575
try:
existing = hashlib.sha256(built.object_path.read_bytes()).hexdigest()
Comment thread loop/contract.py
Comment on lines +817 to +827
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})"
Comment thread loop/evidence.py
Comment on lines +292 to +300
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)"

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread loop/emit.py
Comment on lines +523 to +526
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))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Comment thread loop/completion.py
Comment on lines +86 to +87
return (isinstance(entry, str) and entry.startswith(_RECORD_PREFIX)
and entry.endswith(".json") and ".." not in entry)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

SollanSystems added a commit that referenced this pull request Jul 26, 2026
… (#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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants