* build: add dulwich as optional version extra
* feat(version): scaffold module
* feat(version): VersionStore.open inits bare repo
* feat(version): blob write/read with content dedup
* feat(version): nested tree write/read
* feat(version): commit, head, read_commit + HEAD ref
* feat(version): branches + log
* feat(version): file-level tree diff
* feat(version): to_tree_inputs maps workspace content to tree entries + meta
* feat(version): to_state reverse-maps entries+meta to workspace state
* feat(version): meta sidecar serialization (meta_to_blob/blob_to_meta)
* feat(version): snapshot_tree writes blobs + meta sidecar to a git tree
* feat(version): commit composes snapshot_tree + parent linkage
* refactor(version): drop unused META_FORMAT sidecar field
* feat(version): version_log + version_diff + status (meta sidecar excluded)
* feat(version): branch creates a new line at a branch head
* feat(version): materialize reads a version tree back to entries + meta
* refactor(snapshot): extract install_fingerprints from Workspace.load for reuse
* refactor(version): rename materialize -> read_version
* feat(version): checkout rebuilds workspace content in place (cache cleared)
* refactor(version): commit/snapshot from state (enables CLI commit from daemon snapshot)
* feat(version): guard dulwich import so core/CLI load without the version extra
* refactor(version): move version engine under cli; dulwich -> cli extra
* feat(version): store top-level config in meta; add status_state + resolve_ref
* feat(cli): wire workspace commit/log/diff/branch/status/checkout to local .mirage store
* feat(version): commit file cache + remote revision pins + sessions
* refactor(version): rename mapping.py -> state_tree.py
* feat(version): CAS commit (reject stale head) instead of clobbering the ref
* feat(version): versioning server API + CLI (commit/log/diff/branch/checkout/clone)
Engine moved under server/ and exposed over HTTP; mirrored in TypeScript.
Adds branching examples (py + ts) checked against one shared truth file,
brings the CLI tests to parity, and documents the verbs.
* style(schemas): use # isort: skip for the long workspaces re-export
Match the convention already used in routers/workspaces.py and
routers/versions.py instead of a one-off # yapf: disable.
* ci(ts): run the CLI e2e suite isolated from the other packages
The cli e2e tests spawn a real daemon + a CLI subprocess per call (blocking
spawnSync). Running them in parallel with the other packages' vitest (pnpm -r)
starved CPU on CI, so the cli worker's onTaskUpdate RPC timed out (all 51 tests
passed, but the run errored). Run libraries in parallel, then cli alone.
* fix(ts/cli): make e2e CLI calls async to stop blocking the vitest worker
Root cause: the e2e helpers used blocking spawnSync, freezing the vitest
worker's event loop for the whole 103s suite. vitest's onTaskUpdate RPC has a
fixed 60s birpc timeout, so the parent's ack couldn't be processed in time and
the run errored (all 51 tests passed). Isolating the package didn't help (still
one continuously-blocked worker). Switch spawnSync -> async spawn so the loop
stays responsive between calls; revert the now-unneeded CI step split.