Skip to content

Ticket 0009: anchor reconcile, the first consumer of anchors (+ P2 remediation from #318/#319) - #320

Merged
noctem-o merged 3 commits into
mainfrom
seam/0009-anchor-reconcile
Jul 5, 2026
Merged

noctem-o merged 3 commits into
mainfrom
seam/0009-anchor-reconcile

Conversation

@noctem-o

@noctem-o noctem-o commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Ticket 0009: cog anchor reconcile

Scans a directory tree for sealed bundles, folds the anchor log into an AnchorSet projection, and anchors exactly the sealed-but-unanchored remainder. Idempotent by construction: the second run anchors nothing. Deliberately the first consumer of anchors — it validates the seam by using it.

  • AnchorSet (cog-anchor): BTreeSet fold over LogReader::replay; exhaustive Payload match (no _ => {}); replay verifies the chain first, so a tampered log refuses to reconcile.
  • read_verifying_key (cog-anchor): read-side companion to generate_key_file, same 64-lowercase-hex rules.
  • Scan detects bundles by the four witness manifest filenames and reuses the emitters'"'"' per-kind AnchorRequest builders (visibility bump only) — single source of truth for manifest→request. Roots are re-validated reconcile-side (64 lowercase hex) because reconcile anchors bundles it did not just verify.
  • Deterministic: candidates sorted by path before acting. Exit nonzero only for anchoring failures; unparseable/foreign bundles are reported and skipped.
  • Reviewer fix on the Codex implementation: kernel-arm profile gate (the kernel request builder debug-asserts a profile that untrusted on-disk manifests can violate; hostile input must skip, not panic).

P2 remediation (codex bot review of #318/#319)

  1. Flags beat config (Ticket 0007b: anchor emission at the kernel witness seal point #318): complete --anchor-key/--anchor-log no longer consult deadbolt-anchor.toml, so a malformed config cannot veto explicit paths.
  2. Init preflights the log before minting a key (Ticket 0007b: anchor emission at the kernel witness seal point #318): missing log parent / existing non-empty log are refused before key creation; if the open still fails, the freshly minted key is removed. No more stranded keys blocking re-init.
  3. Kernel anchor gate runs the CLI verifier'"'"'s source preflight (Ticket 0007b: anchor emission at the kernel witness seal point #318): symlinked/reparse-point source artifacts are refused before the library verifier (which follows them) — the gate is no longer weaker than cog verify kernel-witness.
  4. Pending markers moved beside the run dir (Ticket 0008: fan anchor emission out to the remaining seal points #319): <run>.pending-anchor.json. Anchoring debt is operational state, not evidence; the observation verifier'"'"'s strict uncommitted_artifact_present no longer fails a sealed bundle whose anchor attempt failed. Kernel stale-artifact clearing follows the marker.

Validation (run on Windows by the reviewer)

  • cargo test -p cog-anchor: 9/9 pass (incl. tamper-refusal)
  • cargo test -p cog-cli: 545 pass / 7 fail — the 7 are the known pre-existing style_plan snapshot failures on native Windows, identical on clean main
  • New tests: reconcile idempotence / mixed-state / tamper / uppercase-root / determinism / foreign-kernel-profile; flags-over-malformed-config; init-preflight-no-stranded-key; kernel-gate symlink refusal (symlinks supported on the runner — executed, not skipped); observation bundle with anchoring debt still verifies Pass
  • cargo tree -i magpie-log: cog-anchor remains the sole direct dependent (pin 7c7f043 unchanged)

Codex (gpt-5.5) implemented ticket 0009 (.agent-runs/20260705T093018Z-1991177716); Claude reviewed, applied the reviewer fix and P2 remediations, and ran validation. Merge is George'"'"'s call, as always.

🤖 Generated with Claude Code

noctem-o and others added 2 commits July 5, 2026 10:04
cog anchor reconcile: scan a tree for sealed bundles, fold the anchor log
into an AnchorSet projection (BTreeSet, exhaustive Payload match), and
anchor exactly the sealed-but-unanchored remainder. Idempotent by
construction; a tampered log refuses reconciliation. Detection keys on the
four witness manifest filenames and reuses the emitters' per-kind
AnchorRequest builders; roots are re-validated (64 lowercase hex) because
reconcile, unlike the live emitters, anchors bundles it did not verify.

Reviewer fix: reconcile-side kernel profile gate — the kernel request
builder debug-asserts a profile that untrusted on-disk manifests can
violate.

P2 remediation (codex bot review of #318/#319):
- complete --anchor-key/--anchor-log flags no longer consult
  deadbolt-anchor.toml, so a malformed config cannot veto explicit paths
- anchor init preflights the log path before minting a key, and removes
  the freshly minted key if the log open still fails - no stranded keys
- the kernel anchor gate runs the CLI verifier's source-artifact
  preflight, refusing symlinked sources the library verifier would follow
- pending-anchor markers moved beside the run directory
  (<run>.pending-anchor.json): anchoring debt is operational state, not
  evidence, and the observation verifier rightly refuses foreign files
  inside sealed bundles

Implemented by Codex (gpt-5.5) from the 0009 ticket; review, P2 fixes,
and validation by Claude.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@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: 4dcca9793c

ℹ️ 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 on lines +23 to +25
pub(crate) fn pending_anchor_marker_path(run: &Path) -> PathBuf {
match (run.parent(), run.file_name().and_then(|name| name.to_str())) {
(Some(parent), Some(name)) => parent.join(format!("{name}.{PENDING_ANCHOR_FILE}")),

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 Clear sibling pending markers after successful seals

When the marker is moved beside the run directory, rerunning a previously failed observation/codelet/transaction seal with a valid anchor configuration no longer overwrites or removes the old <run>.pending-anchor.json; those success paths return Anchored without calling delete_pending_anchor_marker_if_present (kernel has a separate stale-marker cleanup). This leaves operational debt visible even though the bundle has just been anchored, so the non-kernel seal paths should clear the sibling marker on successful anchoring as reconcile does.

Useful? React with 👍 / 👎.

}
};

if !is_lower_hex_64(&request.witness_root) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Verify bundles before anchoring them

For a scanned bundle whose evidence was tampered after sealing, this path builds an AnchorRequest and only checks that the requested root is lowercase hex before appending it. For example, changing witness_root.txt in an observation/codelet/transaction bundle to any other 64-character lowercase hex value is caught by the normal bundle verifiers as witness_root_mismatch, but reconcile will still anchor that false root because it never runs those verifiers before this format check. Reconcile should gate each candidate with the same per-kind verification used by the live seal paths before writing an anchor.

Useful? React with 👍 / 👎.

P1 (codex bot, PR #320 review): reconcile built an AnchorRequest from what
the bundle claimed and only shape-checked the root, so evidence tampered
after sealing - e.g. witness_root.txt rewritten to a different 64-hex
value - would be anchored even though every live seal path re-verifies
before anchoring. Reconcile now gates each candidate with the same
per-kind verifier the emitters use (kernel via a shared
verify_kernel_witness_for_anchor so the two gates cannot drift). New
verify_failed summary count; verification failures force nonzero exit -
tampered evidence must be loud. Already-anchored roots skip verification:
their anchor was recorded at a verified moment.

Reconcile tests now seal REAL observation/kernel bundles instead of
hand-built manifests, plus a tamper test proving a re-rooted bundle is
refused while valid bundles still anchor.

P2 (codex bot): a successful anchor now pays off the run's stale sibling
pending-anchor marker in all four emitters (shared anchored_outcome), not
just via kernel's build-time stale cleanup.

Also drops the per-anchor full-log replay: same-root dedupe within a run
now uses a local BTreeSet of newly anchored roots.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@noctem-o
noctem-o merged commit e5bba7e into main Jul 5, 2026
1 check passed
@noctem-o
noctem-o deleted the seam/0009-anchor-reconcile branch July 5, 2026 10:39
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.

1 participant