Skip to content

Durable document identity and per-document persistence - #13

Draft
kbaxter wants to merge 1 commit into
mainfrom
feature/2-document-persistence
Draft

kbaxter wants to merge 1 commit into
mainfrom
feature/2-document-persistence

Conversation

@kbaxter

@kbaxter kbaxter commented Aug 28, 2026

Copy link
Copy Markdown
Owner

Why

Margin previously stored one global workspace in localStorage. Switching documents cleared or conflated editorial state, so findings, discussions, sources, reviews, brief corrections, proposals, and assets could not compound reliably.

What changed

  • Added stable UUID-backed DocumentRecord identity and per-document companion state.
  • Added IndexedDB stores for documents, documentState, and settings.
  • Added an asynchronous document-session boundary and recent-document switching.
  • Preserved Markdown Save and Export behavior.
  • Added a one-time migration from margin.workspace.v1 while retaining the legacy value as rollback data.
  • Added conservative file recovery based on filename plus normalized content hash; ambiguous files become new documents.

Architecture

Persistence now lives under margin/src/persistence with pure normalization and sanitization helpers, a versioned IndexedDB schema, legacy migration, and a document-session API. App initializes asynchronously and remounts its document workspace by stable document ID, which keeps transient UI state from leaking across documents.

Migration / compatibility

The legacy localStorage workspace is imported once and is not deleted or rewritten. DocumentRecord includes a local Markdown working snapshot because browsers cannot reliably restore external file handles after reload. The snapshot restores the editing session; ordinary Markdown remains the portable authored artifact and external files are only written through explicit Save or Export actions.

Tests

  • npm ci
  • npm run verify
  • 41 tests passed, including migration, independent A/B state, malformed data fallback, same-name collision safety, file-handle identity, and rename identity.
  • Production TypeScript and Vite builds passed.
  • git diff --check passed.

Screenshots or UX notes

The document menu now includes recent Margin documents, and startup has an explicit local-library loading boundary. Manual browser acceptance could not be run in this Codex session because the browser controller reported that no browser was available. The persistence paths are covered by focused fake-IndexedDB integration tests, but this PR remains draft until the following UI scenario is exercised:

  1. Open A and create a finding, source, and review state.
  2. Open B and confirm A state is absent.
  3. Return to A and confirm A state returns.
  4. Reload and confirm the active document and its state return.
  5. Rename A and confirm its identity and state remain stable.

Documentation

  • Added the canonical roadmap.
  • Added architecture overview and persistence pages.
  • Added 0.2 release notes.
  • Recorded the browser file-handle recovery finding.
  • Updated documented automated test counts.

Risks / follow-ups

  • PRs Introduce durable TextAnchor passage identity #3 and Map editorial anchors through CodeMirror edits #4 will add durable passage anchors and CodeMirror mapping; this PR deliberately leaves existing excerpt matching unchanged.
  • The repository dependency audit still reports one moderate and one high advisory. No automatic audit fix was applied because it could introduce unrelated dependency changes.
  • Cross-device sync, projects, library UI, and claims/evidence remain explicit non-goals for this slice.

Closes #2

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.

Durable document identity and per-document editorial persistence

1 participant