Vault integration phase 3: wire synced vaults into the UI
Make synced vaults functional end-to-end by driving the push-only sync
engine from the providers and the composition root:
- SyncedVaultFilesystemProvider: disk tree + CRUD (extends
LocalFilesystemProvider), reconciling each mutation through the engine
(pushFile on create/copy, sync on rename/delete).
- SyncedVaultDocumentsProvider: opens files via the live Automerge session
(multiplayer over libp2p), resolving disk path -> Habitat URI from the
snapshot (pushing first if new), pulling latest on open, and mirroring
session edits back to disk. The session factory is injected (DI) so the
module stays free of the libp2p runtime and is testable.
- engine.ts: add noteMirroredFile() so mirrored in-app edits aren't
re-pushed as phantom external changes.
- registry.ts: persist OpenVault.rootUri.
- App.tsx: provision a synced vault's root directory record on first open,
construct the per-vault engine stack, run the first-mount sync(), and
wire the two synced providers (acquireSession = real acquireDocSession).
Testing uses fakes over mocks: extract InMemoryHabitatRepo +
NodeFilesystemApi to src/vault/testing/fakeHabitat.ts and add a
FakeDocSession sharing one in-memory doc store with the repo. The provider
test runs the real engine/detector/snapshot manager against the fakes and
asserts on real state (on-disk files, snapshot, doc store) — no vi.mock.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>