Skip to content

Port @relayburn/ledger to Rust (#243) - #253

Closed
willwashburn wants to merge 2 commits into
mainfrom
claude/fix-issue-242-1SZ66
Closed

Port @relayburn/ledger to Rust (#243)#253
willwashburn wants to merge 2 commits into
mainfrom
claude/fix-issue-242-1SZ66

Conversation

@willwashburn

@willwashburnwillwashburn commented May 3, 2026

Copy link
Copy Markdown
Member

Closing in favor of #259, which redesigns the Rust ledger as a SQLite-only store (no JSONL, no hash sidecars, no file-lock dance) instead of porting the TS tri-store layout literally. The 2.0 cutover makes Rust the source of truth, so the cross-language compat constraint that motivated #243's design no longer applies.

The work in this branch (claude/fix-issue-242-1SZ66) stays on the remote as a reference for the lock-protocol port and schema-compat code, but the next implementation will land against #259.

Initial port of the append-only JSONL ledger, content sidecar, lock
protocol, and SQLite archive into the `relayburn-ledger` crate. Mirrors
the TS source under `packages/ledger/src/` so a Rust holder and a TS
holder share an `archive.sqlite` and a `*.lock` file without rebuilds.
- schema: `LedgerLine` round-trips every kind (turn, stamp, compaction,
relationship, tool_result_event, user_turn). Records stay
`serde_json::Value` until #242 lands the typed `TurnRecord`. Hash
helpers (turn / compaction / relationship / tool-result-event /
user-turn id; turn content fingerprint) match the TS sha256[..16]
inputs byte-for-byte.
- lock: replicates the exclusive-create + two-phase retry + orphan
recovery protocol from `adapters/file-lock.ts` rather than reaching
for `flock(2)`/`fs2` (which has different cross-process semantics on
macOS / Windows). Re-entrancy uses `tokio::task_local!` as the analog
of TS `AsyncLocalStorage`. The retry-budget-vs-STALE_MS invariant is a
`const _` compile-time assertion. Property test serializes 100
concurrent callers; orphan-recovery and timeout paths are covered.
- sidecar: load / append / rebuild for `ledger.idx` +
`ledger.content.idx`, including the rolling `CONTENT_WINDOW=10_000`
tail rewrite. `rebuild_index` uses
`serde_json::Deserializer::from_reader().into_iter()` (the shape the
issue calls out for the archive's hot loop).
- file_adapter: JSONL append + stream parse. `append_turns` keeps the
TS batch-snapshot dedup semantics — id hashes dedupe within a batch,
content fingerprints only against historical disk state.
- archive: `ARCHIVE_VERSION = 3` matches TS in lockstep. Schema
statements are byte-identical with `archive.ts:SCHEMA_SQL` so a
TS-built archive opens here without a forced rebuild
(`existing_archive_opens_without_rebuild` covers it). WAL +
synchronous=NORMAL pragmas applied on every open. Additive
migrations (idempotent `ALTER TABLE ADD COLUMN` guarded by
`PRAGMA table_info`) match `applyAdditiveMigrations`. Schema-mismatch
drop-and-recreate matches TS too.
Out of scope until #242 / #244 land the strongly-typed records:
- buildArchive's tail-ingest transaction (the multi-row VALUES path
the issue suggests).
- the spawn-env stamp -> synthesized relationship line in `appendStamp`.
- per-session content sidecar pruning + `pruneContent`.
- archive query helpers.
These are flagged inline in the touched modules.
23 new unit tests; `cargo test --workspace` green.
Closes#243https://claude.ai/code/session_01DaRFCBEvxCFnYTH8BqQr6k
devin-ai-integration[bot]

This comment was marked as resolved.

…tions
- schema: add #[serde(rename_all = "camelCase")] to MessageIdRange.
Without it, TS-written stamps with fromTs/toTs deserialize to all-None
and silently bypass stamp_matches range filtering; Rust-written stamps
produce snake_case JSON TS can't read back. Round-trip test pins the
wire format.
- archive: add tool_calls.replaced_tools (TEXT) and collapsed_calls
(INTEGER) to both SCHEMA_SQL and apply_additive_migrations to mirror
packages/ledger/src/archive.ts:380-381. Counterfactual annotations
written by relaywash-style replacement tools.
https://claude.ai/code/session_01DaRFCBEvxCFnYTH8BqQr6k
Sign up for freeto 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

@willwashburn@claude