Skip to content

feat(cli): import --from <store|bundle> --about into the live store (#903) - #907

Merged
tgarciai merged 3 commits into
integration/903-workspace-memoryfrom
feat/903-s4-import
Oct 4, 2026
Merged

tgarciai merged 3 commits into
integration/903-workspace-memoryfrom
feat/903-s4-import

Conversation

@tgarciai

@tgarciai tgarciai commented Oct 4, 2026

Copy link
Copy Markdown
Member

Slice S4 of #903 (decision D4, option A): use memory from another workspace by incorporating exact abouts into the live store.

Stacked on S1 (#904, fix/903-store-precedence). Until #904 lands, the diff also shows S1; the S4 work is the three commits ending (#903) on top of c53299aa.

Behaviour

kmp-mcp import --from <store-dir|bundle-file> --about <about> [--about <about>]...

Into the store kmp-mcp resolves here (KMP_MCP_DATA_DIR / project / saved / user), opened exactly as plain import opens it.

  • --about is required with --from and vice versa; abouts are opaque and matched exactly. A requested about the source lacks is refused before anything is written.
  • Store source: opened with EmbeddedKernelStore::open_read_only — read-only SQLite connections (SQLITE_OPEN_READ_ONLY + query_only), no create_dir, no FORMAT_VERSION stamp/upgrade, no card adoption, no journal-mode switch, no writer lock; schema and quick_check still verified. Exported through the existing filtered export path (export_bundle_for_abouts). A workspace path is accepted for its .kernel/. A source whose legacy cards were never adopted into its event log is refused (exporting it would drop them).
  • Bundle source: verified like any import (format, range, abouts, digest, card contiguity), then narrowed to the requested abouts.
  • Per (about, role) event stream (the store's aggregate key):
    • absent here → imported (replay all);
    • same revisions and content hashes → unchanged (no-op);
    • destination is an exact prefix → extended (only the missing tail is appended, on the revisions the source recorded);
    • differing revision/hash, or destination ahead (including a role stream the source lacks) → the whole import is refused, naming the about, role and first diverging revision.
  • Also refused: an appended event whose idempotency key already answers for another write here (appending would overwrite that replay answer), and a relation that would point at a node neither the imported abouts nor this store hold (see below).
  • Everything — reading the destination log, the verdict, the checks, the appends and the projections — is one SQLite write transaction (BEGIN IMMEDIATE), so no other writer commits in between and a refusal writes nothing. A second run reports every about unchanged and writes nothing.
  • Output: {"abouts":[...],"aggregates":{"<about>":"imported|unchanged|extended"},"data_dir":...,"events_imported":N,"from":...,"mutations_applied":M}; refusals exit 2 with the reason on stderr.
  • Plain kmp-mcp import [file] (empty-store replay) is unchanged. No new MCP tool (16).

Decisions

Prefix → extend (not refuse). Appending revision n+1 to a stream at head n is exactly the live write path (append_in_transaction requires current == expected and assigns current + 1); projections are applied per event in the same transaction, as append_projected does. Imported events land at the end of the log, so a full rebuild reproduces the same projections — kmp-embedded/tests/import_abouts.rs asserts wake (nodes and relationships) is identical to the source's and unchanged by rebuild_projections. The lexical sidecar follows the log by position/tail, so it picks the new events up on its next catch-up.

Relations → refuse. The projection silently materialises a missing relation endpoint as an UNMATERIALIZED placeholder node, and the write path only allows a cross-about relation to a ref verified to exist. So an appended relation whose endpoint is produced by no event of the requested abouts (whole source stream) and does not exist as a non-placeholder node here is refused, naming the relation and the missing node and suggesting the extra --about. Measured first on a copy of a real 474-event user store: each of its 20 abouts imported alone into an empty store with zero refusals, so the rule does not reject ordinary memory. Strictness to be aware of: a relation already dangling in the source is refused too.

Verification

  • New tests: 5 plan unit tests (about_import_plan), 10 adapter tests (kmp-adapter-embedded/tests/about_import.rs: imported + idempotent rerun, prefix extended to the source's exact digest, divergence refused with event count and digest unchanged and a valid sibling about not written, destination ahead, missing/near-miss about, dangling relation refused then accepted with its owner, relation to an existing destination node, idempotency-key collision, source store byte-identical + writes refused through the read-only handle, non-store dir refused and not created), 1 kernel test (kmp-embedded/tests/import_abouts.rs: live source read while open, imported then extended, wake parity, rebuild parity, about stays writable), 3 binary tests on a hermetic machine (kmp-mcp/tests/import_from_cli.rs: store source imported/unchanged/document, source tree byte-identical, bundle source divergence refused with digest unchanged, missing about, argument rules without preparing the store).
  • cargo test --locked -p kmp-mcp -p kmp-embedded -p kmp-adapter-embedded: 1516 passed, 0 failed, 3 ignored; kmp-release tests green.
  • cargo fmt --all, cargo clippy --locked --workspace --all-targets -- -D warnings, scripts/ci/kmp-mcp-architecture-gate.sh (0 debt), scripts/ci/mcp-registry.sh (16 tools), scripts/ci/kmp-capability-contract.py: pass.
  • CLI surface fixtures re-blessed (help.txt, import.txt); guide entry advanced:import-abouts added to editorial.json and assets regenerated; CHANGELOG [Unreleased] entry.
  • Manual runs only against copies of real stores in a scratch HOME/XDG/KMP_MCP_DATA_DIR: every about of a copied user store, and the copied my-ships-game store (46 events → imported, then unchanged; document renders; missing about refused).

Not done / notes

  • No store lease is taken, same as plain import; SQLite's write lock is what serialises against live sessions.
  • The project head .kmp/memory.jsonl is not refreshed after import (same as plain import); the next export/kmp:save shows the diff.
  • known-stores.jsonl is not touched (S3's inventory).
  • Reading a source dir that had no -wal/-shm may leave those (empty) SQLite sidecars behind; the database file and everything else stay byte-identical (tested).
  • Not verified: importing while a live serve holds the destination open (single BEGIN IMMEDIATE with the 10 s busy timeout should wait it out; untested). Not run: an all-abouts import of the real-store copy compared by digest against its full export.
  • Expect a merge conflict with S3 in crates/kmp-mcp/src/cli/mod.rs help text and fixtures/contract/cli/help.txt; whoever merges second re-blesses.

codex added 3 commits October 4, 2026 18:57
…ng (#903)

EmbeddedKernelStore::import_abouts compares each (about, role) stream of a verified bundle with the destination log and, in one SQLite write transaction, replays absent streams, leaves identical ones alone, extends exact prefixes with the missing tail, and refuses divergence, a destination ahead, reused idempotency keys and relations that would dangle. EmbeddedKernelStore::open_read_only reads another store through read-only SQLite connections without stamping, migrating or adopting it.
@tgarciai
tgarciai merged commit 8c756eb into integration/903-workspace-memory Oct 4, 2026
35 of 36 checks passed
tgarciai added a commit that referenced this pull request Oct 4, 2026
…d about import (#903) (#905)

S1 projects beat the saved selection; an existing .kernel marks a project; git worktrees share the main checkout store (#904).
S2 every session names the store it opened, in initialize and on every kmp_wake (#906).
S3 kmp-mcp memories: one inventory of every store, its reach and its abouts; memories register (#908).
S4 kmp-mcp import --from <store|bundle> --about: incorporate exact abouts into a live store, all or nothing, idempotent (#907).
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