26382a7ac6
CI / Clippy (push) Failing after 15m13s
CI / Test (ubuntu-latest) (push) Failing after 16m1s
CI / Test (macos-latest) (push) Has been cancelled
CI / Test (windows-latest) (push) Has been cancelled
CI / Build (no embeddings / no ORT) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / Cookbook (Node) (push) Has been cancelled
CI / Pi Extension (Node) (push) Has been cancelled
CI / Rust SDK (lean-ctx-client) (push) Has been cancelled
CI / Embed SDK (lean-ctx-sdk) (push) Has been cancelled
CI / Python SDK (leanctx) (push) Has been cancelled
CI / Hermes Plugin (Python) (push) Has been cancelled
CI / SDK Conformance Matrix (push) Has been cancelled
CI / Coverage (push) Has been cancelled
CI / cargo-deny (push) Has been cancelled
CI / Adversarial Safety (push) Has been cancelled
CI / Benchmarks (push) Has been cancelled
CI / Output-Quality Gate (eval A/B) (push) Has been cancelled
CI / Documentation (push) Has been cancelled
CI / CI Green (push) Has been cancelled
JetBrains Plugin / Actionlint (push) Has been cancelled
CodeQL / Analyze (actions) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
CodeQL / Analyze (rust) (push) Has been cancelled
JetBrains Plugin / Validation (push) Has been cancelled
JetBrains Plugin / Build (push) Has been cancelled
JetBrains Plugin / Test (push) Has been cancelled
Security Check / Security Scan (push) Has been cancelled
115 lines
5.7 KiB
Markdown
115 lines
5.7 KiB
Markdown
# Journey 21 — Context Time Machine (Snapshots)
|
|
|
|
> You want a time axis on the context layer: capture what the model saw at a
|
|
> commit, replay *why* it acted, then reproduce, resume or share that exact
|
|
> state. This journey covers `lean-ctx snapshot` — git-anchored,
|
|
> content-addressed, Ed25519-signed snapshots that verify **offline**, the same
|
|
> trust model as the savings ledger, applied to temporal state.
|
|
|
|
Source files referenced here:
|
|
- `rust/src/cli/snapshot_cmd.rs` — `cmd_create`, `cmd_list`, `cmd_show`, `cmd_verify`, `cmd_restore`, `cmd_publish`, `cmd_import`
|
|
- `rust/src/core/context_snapshot/mod.rs` — module root + re-exports
|
|
- `rust/src/core/context_snapshot/types.rs` — `CONTEXT_SNAPSHOT_V1` structs (git anchor, slices, signature)
|
|
- `rust/src/core/context_snapshot/builder.rs` — `SnapshotOptions`, `build`, `create`
|
|
- `rust/src/core/context_snapshot/digest.rs` — `canonical_body`, `compute_id`, `finalize_id` (BLAKE3 content id)
|
|
- `rust/src/core/context_snapshot/signing.rs` — `sign_snapshot`, `verify_snapshot` (Ed25519)
|
|
- `rust/src/core/context_snapshot/timeline.rs` — append-only timeline index
|
|
- `rust/src/core/context_snapshot/restore.rs` — `RestoreOptions`, `GitRestore`, `SessionMerge`, `restore`
|
|
- `rust/src/core/context_snapshot/publish.rs` — `PublishOptions`, `PublishOutcome`, `ImportOutcome`, `publish`, `import`
|
|
- `rust/src/core/agent_identity.rs` — persistent per-machine Ed25519 keypair (shared with the savings ledger)
|
|
|
|
Contract: [`docs/contracts/context-snapshot-v1.md`](../contracts/context-snapshot-v1.md).
|
|
|
|
---
|
|
|
|
## 0. The principle
|
|
|
|
> A snapshot is a *distilled, signed* view of the context layer at one git
|
|
> commit — not a copy of your repo. It bundles slices of the IR, the decisions
|
|
> and knowledge in play, the savings-ledger slice and the live session. The
|
|
> `snapshot_id` is a BLAKE3 hash of the canonical body, so the id **is** the
|
|
> content; an Ed25519 signature binds origin. Nothing leaves your machine unless
|
|
> you explicitly `publish`.
|
|
|
|
---
|
|
|
|
## 1. The snapshot model — `CONTEXT_SNAPSHOT_V1`
|
|
|
|
Defined in `types.rs`. A snapshot bundles:
|
|
|
|
| Slice | Holds | Source |
|
|
|-------|-------|--------|
|
|
| Git anchor | Commit SHA + dirty flag at capture | `builder.rs` (via `core::git`) |
|
|
| IR digest | The distilled view the model saw | `builder.rs` |
|
|
| Decisions & knowledge | Session decisions + project facts | `builder.rs` (session + knowledge) |
|
|
| Ledger / ROI slice | Token savings booked to that point | `builder.rs` (savings_ledger) |
|
|
| Session state | Task, touched files, findings | `core::session` |
|
|
| Signature | Ed25519 over the content id | `signing.rs` |
|
|
|
|
The canonical body and id are computed in `digest.rs` (`canonical_body` → `compute_id`/`finalize_id`, BLAKE3). Editing any byte of the body changes the id and breaks `verify_snapshot`.
|
|
|
|
---
|
|
|
|
## 2. The `snapshot` command surface
|
|
|
|
`snapshot_cmd.rs` dispatches on the first argument.
|
|
|
|
| Command | Code path | Leaves machine? |
|
|
|---------|-----------|-----------------|
|
|
| `snapshot create` | `cmd_create` → `context_snapshot::create` (`builder.rs`) → sign → append timeline | No |
|
|
| `snapshot list [--json]` | `cmd_list` → `timeline::*` (append-only index) | No |
|
|
| `snapshot show <id>` | `cmd_show` → load snapshot by id | No |
|
|
| `snapshot verify <id>` | `cmd_verify` → `signing::verify_snapshot` + `digest` recompute | No |
|
|
| `snapshot restore <id> [--git]` | `cmd_restore` → `restore::restore` (`SessionMerge`, optional `GitRestore`) | No |
|
|
| `snapshot publish <id> [--out FILE]` | `cmd_publish` → `publish::publish` (signs if needed) | Only the file you share |
|
|
| `snapshot import <file>` | `cmd_import` → `publish::import` (verifies, then appends) | No (any machine) |
|
|
|
|
---
|
|
|
|
## 3. Capture & timeline
|
|
|
|
`create` (`builder.rs`) reads the live context layer, builds the body, computes the BLAKE3 id (`digest.rs`), signs it (`signing.rs`) and appends one JSON artifact to the **append-only timeline** (`timeline.rs`). The timeline is the spine for replay (`list`/`show`) and for the cockpit Replay view.
|
|
|
|
```bash
|
|
lean-ctx snapshot create # context_snapshot::create → sign → timeline append
|
|
lean-ctx snapshot list --json # timeline index, newest first
|
|
lean-ctx snapshot show <id> # full distilled state behind one snapshot
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Restore & resume — `restore.rs`
|
|
|
|
`restore` merges the snapshot's session back into the live session (`SessionMerge`); with `--git` it also checks out the anchored commit (`GitRestore`).
|
|
|
|
```bash
|
|
lean-ctx snapshot restore <id> # session slice only
|
|
lean-ctx snapshot restore <id> --git # also check out the anchored commit
|
|
```
|
|
|
|
`--git` **refuses a dirty tree** — it never silently discards uncommitted work (`RestoreOptions`/`GitRestore` guard). Replay to understand; restore to continue.
|
|
|
|
---
|
|
|
|
## 5. Share & publish — `publish.rs`
|
|
|
|
`publish` writes a single signed, portable file (`PublishOutcome`); `import` verifies it and appends it to the local timeline (`ImportOutcome`), idempotently. A tampered file is refused.
|
|
|
|
```bash
|
|
lean-ctx snapshot publish <id> --out ./review.ctxsnapshot.json # PublishOptions
|
|
lean-ctx snapshot import ./review.ctxsnapshot.json # verify → timeline
|
|
```
|
|
|
|
Only the snapshot body travels — never your repo, prompts or code.
|
|
|
|
---
|
|
|
|
## 6. The trust model
|
|
|
|
A verified snapshot answers two questions, both offline (`signing::verify_snapshot`):
|
|
|
|
- **Integrity** — the body is unchanged: the BLAKE3 `snapshot_id` (`digest.rs`) and the Ed25519 signature both cover the canonical payload.
|
|
- **Origin** — produced by the holder of a specific keypair (`agent_identity.rs`, the same per-machine key the savings ledger uses).
|
|
|
|
This is the [`CONTEXT_SNAPSHOT_V1`](../contracts/context-snapshot-v1.md) contract: trust by construction, not by claim — extended from "what you saved" to "what the model saw, when."
|