A local-first note taking app

Vault integration phase 3: wire synced vaults into the UI master

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>