Skip to content

fix: harden the PCM ledger — structural conflict-freedom, forgery resistance, idempotent import#26

Merged
CodeWithJuber merged 1 commit into
masterfrom
claude/whitepaper-implementation-plan-x8f7bh
Jul 7, 2026
Merged

fix: harden the PCM ledger — structural conflict-freedom, forgery resistance, idempotent import#26
CodeWithJuber merged 1 commit into
masterfrom
claude/whitepaper-implementation-plan-x8f7bh

Conversation

@CodeWithJuber

Copy link
Copy Markdown
Owner

What & why

The production-readiness pass on the P1 ledger (#25). An adversarial 8-angle review (line-scan, removed-behavior, cross-file trace, reuse, simplification, efficiency, altitude, conventions — each finding independently verified) ran against the merged code and confirmed several real defects. All are fixed here, each with a regression test.

The three serious ones:

  1. The conflict-free merge guarantee didn't actually hold. Claim files embedded per-author provenance, so two teammates minting the same claim produced different bytes at the same path — a real git conflict, and a merge-order-dependent mergeStates. Tombstones had the same flaw (single file, first-writer-wins). Now: claim bytes are a pure function of (kind, body, scope) — byte-identical on every replica — and provenance + tombstones move into per-claim append-only logs, hash-deduped and union-merged exactly like evidence. The semilattice property now holds for all state, including concurrent retractions, and the property tests cover it.
  2. Forged evidence could buy confidence. A hand-edited log line claiming w: 50 on the strongest oracle would previously push val to ~0.98 and pass forge ledger verify. Now val() takes weights from the ORACLES table only (stored w is audit metadata), unknown oracles are ignored, and verify recomputes every record's content hash — flagging forgery, ghost oracles, and inflated weights.
  3. forge ledger import wasn't idempotent. Synthetic outcomes were re-hashed on every run (the legacy counters keep moving) and double-counted confirmations already shadow-written live. Now a claim that's already tracked is skipped entirely — import back-fills pre-ledger history only, and re-running is a proven no-op.

Also fixed (each was a verified finding): distillation now supersedes in the ledger (evidence carried to the distilled claim, template tombstoned — previously a lesson's history split across orphaned claims); evidence refs carry the confirmation counter (same-day sessions share episode ids — real confirmations were being silently deduped); regex-detected reverts contradict at the conservative bridge weight instead of full-weight human.revert (a routine git restore could permanently bury a good lesson in the append-only log); fact claims mint one id from both the shadow path and the file-parse path (single CRLF-tolerant parser + trimmed bodies — Windows checkouts forked ids before); same-name fact updates supersede the stale claim and recall consolidate reconciles deletions into tombstones (no phantom memories at the P2 read-flip); putClaim repairs corrupt/truncated files instead of trusting existsSync; log reads return records in (t, h) order so views never depend on post-merge line order; forge ledger show resolves via its shard instead of scanning the ledger; forge ledger --personal reaches the personal ledger (it was write-only); forge init emits the union-merge .gitattributes rule into consumer repos (it only existed in forgekit's own); SCOPE_WEIGHT single-sourced in the ledger core; the CLI's shadow-write imports are inside try/catch so a broken bridge can't fail an already-persisted write.

Checklist

  • npm test passes (270/270 — 17 new/extended tests incl. concurrent-retraction convergence, forged-evidence rejection, corrupt-file repair, import idempotence under live shadow-writes, CRLF parity, supersede-on-distill)
  • npm run check passes on changed files (remaining repo diagnostics pre-date this PR)
  • New public functions have a test (readFact, shadowFact, reconcileFacts, supersedeLessonClaim, getClaimByPrefix, sealRecord, isDormant, shingles, claimText — closing the review's coverage finding)
  • Conventional commit message (fix:)
  • CHANGELOG.md updated under ## [Unreleased]
  • No new runtime dependency
  • Substrate/docs updated — behavior now matches docs/plans/substrate-v2/01 and 02 more faithfully than the P1 merge did

Risk & rollback

  • Risk level: low-medium — ledger format changes (provenance out of claim bytes, tombstones as logs) land minutes after P1 merged, before any real ledger adoption; loadState still accepts P1-format claim files (the id never covered provenance, so addresses are unchanged)
  • Rollback plan: revert the commit; .forge/ledger/ remains inert data

Extra checks (tick if applicable)

  • npm run typecheck passes
  • Input validated at boundaries (store now rejects invalid outcomes too, not just the record constructor)
  • Logs contain no secrets/PII (verify re-scans all three log types now)
  • If AI-assisted: I understand it, verified the package APIs, and it has tests

🤖 Generated with Claude Code

https://claude.ai/code/session_01Fc2MvWJbQ1cyNQ76ymv4hs


Generated by Claude Code

…istance, idempotent import

Findings from an 8-angle adversarial review of the P1 merge, all fixed:

- claim files now hold pure content (kind/body/scope/v) so the same id is
  byte-identical on every replica; provenance and tombstones move into
  per-claim append-only logs (hash-deduped, union-merged like evidence) —
  the semilattice merge property now holds for ALL state, including
  concurrent mints and concurrent retractions (property-tested)
- val() takes oracle weights from the ORACLES table, never the stored
  record, and ignores unknown oracles; forge ledger verify recomputes
  every record's content hash and flags forgery/ghost oracles/inflated
  weights (previously a hand-edited log line could buy val ~0.98)
- forge ledger import: claims already tracked live are skipped entirely —
  no re-synthesized evidence, no double counting, true idempotence
- cortex bridge: applyDistillation supersedes (evidence carried to the
  distilled claim, template tombstoned); evidence refs carry the
  confirmation counter (same-day session episode-id collisions no longer
  dedupe real confirmations); regex-detected reverts contradict at the
  conservative bridge weight, not full-weight human.revert
- facts: single CRLF-tolerant parser (recall.readFact) + trimmed bodies so
  the shadow path and import path mint one id; same-name updates supersede
  the old claim; recall consolidate reconciles deletions into tombstones
- putClaim repairs corrupt/truncated claim files instead of trusting
  existsSync; forge ledger show resolves via its shard; --personal reaches
  the personal ledger; forge init emits the union-merge .gitattributes
  rule into consumer repos; SCOPE_WEIGHT single-sourced in the ledger core
- readLog returns records in (t, h) order so views never depend on on-disk
  line order after a union merge

270/270 tests (17 new/extended), biome + tsc clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fc2MvWJbQ1cyNQ76ymv4hs
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