Skip to content

feat(core): add the change ledger - #240

Merged
oratis merged 1 commit into
mainfrom
feat/change-ledger
Aug 8, 2026
Merged

feat(core): add the change ledger#240
oratis merged 1 commit into
mainfrom
feat/change-ledger

Conversation

@oratis

@oratis oratis commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Summary

PR 3 of docs/FLOATBOAT_ADOPTION_PLAN.md §2.B — the plan's largest single capability gap. Independent of the file-contract PRs; nothing in the permission path reads it.

Sessions store a message stream and snapshots capture file state, but neither answers what people ask after a run: what did it change, why, and how do I undo that one thing? The ledger is the index over both, pairing each mutation with the request that motivated it and the checkpoint that reverses it.

deepcode ledger list
deepcode ledger show chg-lz4k2p-01
deepcode ledger export --out audit.md

Design decisions worth reviewing

Two timelines, not one file with a type column. changes is high-frequency edits; governance is rare and high-impact (contract edits, plugin installs, trust grants). Interleaved, the second is never seen again.

Stored outside the repo (~/.deepcode/projects/<key>/ledger/). Selfware keeps its log inside the instance, which suits a document workspace — here it would append to a tracked file on every edit and fill git status with noise during the exact activity being reviewed. export covers wanting it committed.

One write point in the loop. Per-tool writes are the shape AGENTS.md rules out: a new mutating tool would silently go unrecorded.

rollbackHint is absent when no checkpoint was taken, rather than pointing somewhere plausible.

Deliberately not recorded

Why
Reads Nothing to say about them, and the traffic buries the mutations
Failed / blocked calls A ledger of things that did not happen is worse than no ledger
File contents Only paths and summaries, so it never becomes a second copy of your secrets

Bash is recorded, with empty paths — its effects can't be declared ahead of time, but "the agent ran rm -rf build" is exactly what an audit needs. The pre-Bash git checkpoint already taken is what makes it reversible.

Two properties that are tested, not just intended

Bookkeeping can never cost a completed edit. FileLedger.append returns null instead of throwing, and the loop additionally catches a host-supplied sink that throws. Tested with a sink that throws disk full — the write still executes and the run still ends end_turn.

Retention ships with the writer, not as a TODO. A log that only grows is one somebody eventually deletes wholesale, losing the recent records too. Newest 5000, nothing past 90 days, trimmed as records accumulate.

Test plan

  • pnpm test1317 passed, 16 skipped (+28: 883 core, 227 cli)
  • pnpm typecheck · pnpm lint · pnpm format:check · pnpm build · node scripts/check-docs.mjs
Area Cases
Writer Append/read; separate timelines; writes outside the repo; unique ids under rapid append
Failure Unwritable path → null, not a throw; oversized summary truncated rather than dropped; corrupt lines skipped
Retention maxRecords trim; maxAgeDays trim with an injected clock; no-op when in window
Mapping Workspace-relative paths; notebook_path; Bash with no paths; rollback hint present/absent
End-to-end in the loop Records a real write with intent; records nothing for failed, blocked, or read calls; a throwing sink does not fail the call; no sink → no change
CLI 11 cases incl. the hidden-record count and "none recorded" for a missing rollback

Documentation

  • New docs/change-ledger.md — why it's separate from sessions, storage location and the reasoning, record shape, what is not recorded, failure behaviour, retention, and the not-an-authority rule

Release notes label

  • release-notes:feature

Related

Plan §2.B (PR 3). Research: docs/research/floatboat.md §4.4(2). ledger rollback lands in PR 4 with the No Silent Apply ceremony.

🤖 Generated with Claude Code

Sessions store a message stream and snapshots capture file state, but neither
answers what people ask after a run: what did it change, why, and how do I undo
that one thing? A message log means reading a conversation; snapshots are
addressable but carry no intent. The ledger is the index over both.

Two timelines, not one file with a type column: `changes` is high-frequency
workspace edits, `governance` is rare and high-impact. Interleaved, the second
would never be seen again.

Stored under ~/.deepcode/projects/<key>/ledger/, not in the repo. Selfware keeps
its log inside the instance, which suits a document workspace; here it would
append to a tracked file on every edit and fill `git status` with noise during
the exact activity being reviewed. `deepcode ledger export` covers wanting it
committed.

One write point in the loop, after a tool succeeds. Per-tool writes are the
shape AGENTS.md rules out — a new mutating tool would silently go unrecorded.

Deliberately not recorded: reads (nothing to say about them, and the traffic
buries mutations), failed and blocked calls (a ledger of things that did not
happen is worse than none), and file contents (so it never becomes a second copy
of a secret). Bash is recorded with no paths, since "the agent ran this" is what
an audit needs even when the effects cannot be declared.

Writes never fail a tool call — a completed edit is worth more than its
bookkeeping. Retention ships with the writer rather than as a follow-up; a log
that only grows is one somebody eventually deletes wholesale.

rollbackHint is absent when no checkpoint was taken, rather than guessing.

Adds `deepcode ledger <list|show|export>`. `list` reports how many records a
--limit hid: a truncated list that reads as complete is how someone concludes
the agent changed less than it did.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@oratis
oratis merged commit 4c7001d into main Aug 8, 2026
5 checks passed
@oratis
oratis deleted the feat/change-ledger branch August 8, 2026 10:05
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