Phase 1 progress
v0.0.2, "it commits". The content-addressed object store and the commit graph.
Decisions (all Accepted)
- ADR-0008 object encoding and the store engine:
redb, CBOR restricted to RFC 8949 §4.2 plus our 64-bit-float rule,ciboriumplus a thin pass. - ADR-0009 the ref model: branches only, flat namespace, one-line text
HEAD, compare-and-swap ref moves, reflog reserved.
mnem-core, built
| Ticket | Module | What |
|---|---|---|
| #22 | object, id | Object (kind-tagged), MemoryNode, State, Commit, Provenance; ObjectId (BLAKE3, hex, CBOR byte string) |
| #23 | codec, objects, error | canonical CBOR encode/decode; put/get/has over a redb txn; MnemError |
| #24 | store, config | Store::init/open (upward discovery, nested-store guard), config parsing with the format_version gate |
| #25 | refs, head | the refs table with compare-and-swap; Head::{Attached,Detached}; Store::head/set_head/resolve_head |
| #26 | commit, staging | persistent staging; Store::stage/staged/unstage/commit (one write txn per commit) |
| #27 | log | Store::log/log_head, first-parent and full-graph walks |
ADR-0004 tracer-bullet checkpoint
The minimum vertical slice (#23 to #27) is working, ~43 tests, no walls hit.
Friction: a redb borrow-lifetime quirk (fixed by inlining a helper) and a
clippy version drift (fixed by pinning CI's rust job to 1.93.0). The Rust
core continues; the Python fallback is not needed.
mnem CLI, built (#28)
init, add, commit, log, each a thin wrapper over the core. Verified end
to end:
$ mnem init
$ mnem add customer-4821 "the customer is on the Enterprise plan" --source ticket-4821
$ mnem commit -m "learn the plan tier"
$ mnem add customer-4821 "the customer downgraded to Pro"
$ mnem commit -m "record the downgrade"
$ mnem log --oneline
ddb2a9ea60a3 record the downgrade
45ee8a6b98b1 learn the plan tier
mnem-py, the Python binding (#29)
A pyo3 cdylib (_mnem) over mnem-core, built by maturin as an abi3
wheel (abi3-py310, one wheel for CPython 3.10 and up). It exposes Store
(init, open, add, commit, staged, unstage, log, head,
format_version, root) and maps every MnemError variant onto a Python
exception hierarchy rooted at mnem.MnemError (ADR-0004). pyproject.toml
moved from hatchling to the maturin backend; the version is read from the Cargo
workspace at build time. python/mnem/__init__.py re-exports the binding; the
ergonomic layer is #30.
The rust CI job excludes mnem-py from cargo test/build (its test binary
would need libpython to link); the python job builds the real extension
through maturin and runs the binding's smoke tests. The behaviour itself stays
tested in the core.
mnem, the Python SDK (#30)
The agent-facing layer, pure ergonomics over the binding (ADR-0004). Store
takes str | PathLike, add takes any JSON-serialisable value and does the
json.dumps, commit defaults the author to $MNEM_AUTHOR then "unknown"
and the time to now (matching the CLI), and log returns Commit dataclasses.
Provenance and MemoryNode are dataclasses; add_node stages a MemoryNode.
Module-level mnem.init / mnem.open mirror the classmethods. The binding's
add grew the remaining three Provenance fields so the SDK carries all of
ADR-0003's provenance. A _mnem.pyi stub types the raw extension. Binding tests
moved to mnem._mnem; test_sdk.py covers the wrapper.
Round-trip test (#31), the definition of done
crates/mnem-core/tests/round_trip.rs: a fixed synthetic run (a support agent
triaging one ticket over three commits, mixing fresh nodes with updates, string
/ nested / unicode content, and provenance from empty to every field set) is
persisted, the Store is dropped so the database file closes, then it is
reopened from disk. The test asserts:
- the whole object table is byte-identical before and after the reload
- every stored id is still the BLAKE3 hash of its stored bytes, and decoding then re-encoding an object reproduces those bytes exactly
- the commit history reloads in the same order with the same parents, messages, authors and times
- HEAD's state holds every node id pointing at its latest value, and an earlier commit's state still holds the pre-update value
Runs in the rust CI job. An agent loop can persist its memory and read it back
byte for byte.
The format spec, frozen (#32)
docs/format/README.md is now the full specification: the store directory,
discovery and atomicity, the config and HEAD grammars, the three redb
tables, object identity, the canonical CBOR profile (RFC 8949 section 4.2 plus
the three Mnemosyne rules), and a field table for each object kind. It is marked
frozen for the 0.0.x line: format_version 1, the same for every 0.0.x
release.
docs/format/golden-vectors.md pins six canonical objects with their exact CBOR
hex and ObjectId. crates/mnem-core/tests/golden_vectors.rs checks both that
the encoder still produces those bytes and that the doc still lists them, so the
format cannot drift without a red test.
The wheel job (#33)
CI now has a wheel job separate from the editable python job: it builds a
release wheel with maturin build --release, checks the archive bundles
_mnem.abi3.so, _mnem.pyi and py.typed, installs it into a clean
environment, and runs the SDK tests from outside the repo so import mnem can
only resolve to the installed wheel. The wheel is uploaded as a build artefact.
Phase 1 complete, tagged v0.0.2
The substrate core, the mnem CLI, the pyo3 binding, the Python SDK, the format
spec and golden vectors, the round-trip test, and the wheel job are all in. The
repo-hardening epic (#99) landed alongside: conventional commits, the
offline-core deny check, issue forms, the justfile, the consistency checks,
and workspace lints. The definition of done is met.
v0.0.2 tagged on main (ADR-0007): workspace version bumped, the changelog
section dated, docs/progress/plain-notes.md written. The pending-release
issues and the v0.0.2 milestone are closed. Next: Phase 2 (v0.0.3), map #10.