# Current architecture and vocabulary Maintained map of the implemented application, reviewed against source on 2026-10-06. Update this document when ownership or a domain contract changes. ## Application Reams uses CodeMirror 6 for source and live-preview Markdown editing, with Obsidian-flavored syntax: GFM, wikilinks, embeds, callouts, comments, and frontmatter. Cmd/Ctrl+E toggles the editing mode. The layout is sidebar, main area, optional right panel, and tabs. Sidebar/panel geometry and tabs persist device-locally; editor mode is a synced vault preference. Mobile delivery is a PWA. Fresh desktop layouts expand Quick Access; fresh mobile layouts show only the ribbon. Valid saved sidebar modes take precedence over these responsive defaults. The formatting toolbar is enabled on mobile and defaults to enabled on desktop. Settings → Formatting retains a synced desktop visibility switch; explicit saved desktop off choices survive reload, and resetting restores the enabled default. Plugin support exposes an Obsidian-shaped compatibility surface through `src/plugin/obsidian-compat/`; Reams-specific capabilities live in `src/plugin/reams/`. Export/type parity and runtime behavior are separate checks. Settings → Obsidian Plugins reads a host-owned static catalog of pinned releases. Only catalog-admitted bundles are restored or executed; old manual installs remain preserved and inactive. Downloaded code and styles are hash-checked and stored device-locally in IndexedDB. The catalog and bundles can be published independently of Reams; `plugin-catalog/README.md` owns the publishing contract. Indexer plugins can require initial host metadata readiness. The first-party workspace opens before their activation so progressive background indexing is not blocked by plugin boot. ## Domain vocabulary | Concept | Meaning and owning boundary | | --- | --- | | Edition | Boot/storage/transport selection: `src/entries/`, `src/editions/`, `src/repo.ts`, `src/atproto/native-repo.ts`. Shared editing behavior sits above the repository contract. | | Document | Persisted CRDT file or folder. `DocumentManager` owns canonical reads, writes, and frontier validation; `src/types/pushwork.ts` defines the compatible wire shape. | | Document identity | Application `DocumentId` currently aliases an Automerge URL in `document-manager.ts`. The runtime contract's `DocumentId` means the backend ID without the URL wrapper. A path/name is presentation/location, not durable identity. | | Heads/frontier | The exact CRDT document version a read or edit describes. Typed projections also depend on the effective catalog revision. Application heads are hexadecimal; the repository boundary encodes/decodes its heads representation in `document-runtime-contract.ts`. | | Vault | User-visible file/folder hierarchy, exposed through FileIndex and the plugin Vault facade. `VaultUserEntries` filters host-owned state out of user enumeration. | | Vault state | Host-owned, synced state in the reserved subtree: `vault-state-root.ts`, `vault-state-map.ts`, `vault-state-data.ts`. This includes vault preferences such as editor mode; sidebar/panel geometry and tabs remain device-local. | | Markdown block | Parser-recognized structure in visible text. Ordinary Markdown blocks do not acquire persistent Record identity merely by being parsed. | | Subject | A document root or a recognized typed block. Root identity uses document identity; a typed block has a native marker with `reams.nodeId`. | | Type | Scoped schema resolved by `TypeCatalog`, including fields and their bindings/defaults. A `#tag` is Type evidence only in an active recognized context. | | Record | One Type application on a subject, not a separate note or database row. Multiple Type applications can share a block subject. | | Fact | An explicit block field contribution keyed by `(nodeId, typeName, fieldId)`; root fields use the shared frontmatter surface. Defaults and derived values are not explicit facts. | | Projection/catalog | Disposable read-side interpretations of canonical data. `TypeCatalog` resolves schemas; `TypedNodeCatalog` aggregates projected typed subjects. They have different responsibilities. | | Base | YAML query in a `.base` document or Markdown fence. Rows select Notes, Records, or Both; a Base does not own another database. | | Plugin API | Supported Obsidian-shaped surface, explicit Reams namespace, and justified host-internal mechanisms. Export/type parity and runtime functionality are separate obligations. | ## Canonical editing path ```text CM6 transaction / public text write → TextFirstSyncController / DocumentManager → attachment planning + Record lifetime policy → TextFirstWriter: atomic text / marker / fact contribution → canonical CRDT change ├─ change observation → disposable read-side projections └─ separate flush → edition-specific durability / publication barrier ``` - `src/sync/use-document-sync.ts` binds the CM6 view to `TextFirstSyncController`; captured edit geometry matters for repeated text, selection, attachment continuity, and undo. - `src/services/document-manager.ts` is the application write/read facade. Whole-source APIs remain supported and use bounded text diffing when exact changes are unavailable. Binary writes have an explicit atomic path. - `src/editor/line-attachment-extension.ts` interprets structure/continuity; `src/types-records/record-structure-policy.ts` produces concrete marker/fact effects. For block Records, removing recognized Type evidence retires that Type's explicit block facts; removing the final Type retires the block identity. There is no dormant block Record restoration store. Retyping is distinct from local undo. Root Type removal changes the frontmatter application list without deleting its field values. - `src/spikes/text-first-writer.ts` is production code despite its directory name. It applies exact-frontier edits and tracks materialization versus durability; it does not interpret Markdown. - CM6 local history records concrete inverse contributions. No parallel durable undo ledger is introduced. A committed edit followed by an observer failure needs recovery from the committed state, not replay. - Disabling Types is distinct from deleting Type evidence. On documents carrying native Record markers or block facts, the controller suspends bound-editor writes to protect existing identity and facts. An unavailable Type catalog defers startup or suspends writes; an authoritative empty catalog can retire formerly recognized block Records. Import/bootstrap/opaque-marker recovery also has explicit compatibility rules; sparse identity is not permission to discard unknown native metadata. Sparse Record navigation reads supply effective Type recognition explicitly to `DocumentManager.readMarkdown`; recognition no longer requires a retained editor writer session. Background identity bootstrap scopes temporary writer ownership, while startup leases protect asynchronous editor preparation. Failed durability retains the writer for retry. Base cell editors use their row's schema descriptors and bindings; vault-wide Type summaries remain chooser/query presentation data. ## Read side and storage ATProto account behavior follows the user-approved [local-first lifecycle](plans/atproto-local-first-lifecycle.md). `BackgroundIndexer` coordinates discovery, scheduling, derivation, and checkpoints for MetadataCache, SearchService, TypeCatalog, and TypedNodeCatalog. Bulk metadata and Record projection derivation uses workers. Background indexing may request canonical Record-identity bootstrap through DocumentManager before deriving projections. Editor preview and Bases consume projections; they do not become independent canonical writers. SearchService's device-local IndexedDB checkpoint and keyed journal also retain versioned parser output for metadata at each indexed document frontier. Startup validates cached metadata against content and canonical heads/name/path, then publishes it in one batch so titles and tags are available together. Progressive opening restores this local checkpoint before workspace readiness; replicated contribution loading and discovery remain behind the background activation gate. Search worker activation and typed catalog reconciliation retain their separate completion lifecycle. See [local recovery evidence](verification/local-index-metadata.md). ATProto boot opens remembered identity and the existing local account partition before authentication, discovery or remote validation. `account-vault.ts` opens the same local runtime whether connected or disconnected. A new browser discovers the authoritative root once and opens progressively; uncached bodies load through the sync worker. Existing staging/adoption does not block opening the account vault. Initial adoption installs its graph locally before publication. The frozen journal retains recovery until background independent readback and retirement complete. `account-sync-worker.ts` owns library-backed catch-up, publication, body recovery and ordinary background adoption. IndexedDB/BroadcastChannel connect that worker to the mounted local Automerge runtime. `account-sync.ts` supplies the worker's OAuth storage snapshot and commits credential updates with a cross-tab lock/CAS; local editing and durability never take that lock. The current library requires Web Storage for OAuth, so this bridge does not reimplement permissioned protocols. Read paths infer missing external-file content types without mutating canonical documents. Current layout, hidden-state maps and replicated index readers no longer migrate historical pre-release payloads. Historical block reconciliation and emission remain in `tests/format-preservation/harness/legacy/`, behind a production-to-test boundary. Explicit Classic archive import/adoption remains supported; it is separate from automatic read-time migration. Classic import is accessed directly through the lightweight `/import` workspace, with other Reams tabs closed. The profile import action was removed after its workspace teardown failed in production. The dedicated import worker owns the local-only SDK runtime exclusively and merges compatible histories using deterministic original-document mappings. Source roots/hidden state retain separate archive identities; current account settings remain active. Compact prospective folder graphs and complete founding-history identities are checked before writes. Hidden links and folder repairs are durable before an atomic root-link/receipt change. Retries inspect receipts rather than replaying removed links. The UI has no whole-job 15-second deadline or per-document history budget; ownership lasts through settled writes and worker termination. Normal editor opening and asynchronous publication resume after local completion. Legacy signed-out staging remains a separate compatibility path. Import flushes each document before advancing the merge counter. Retry compares stored history and skips complete documents; visible root attachment still happens last, so interrupted new files remain stored but may not appear in the editor yet. Temporary source/verification native documents are explicitly freed. Worker heartbeats and operation/document progress distinguish response silence from an unfinished asynchronous operation. Cooperative stop drains accepted work; **Stop now** terminates the worker and releases the client operation lock, with retry rechecking actual durable state. SDK local hash tracking uses native metadata rather than decoding operation payloads into JavaScript. Incoming non-folder histories transfer their native backend to the SDK without a snapshot/load copy. Installed histories checkpoint on the first local flush while retaining publication intent; account opening performs one initial hydration. FileIndex owns the active file hierarchy and file events; the plugin Vault provides Obsidian-style operations. Workspace owns leaves/tabs/navigation; React presents them and persists device-local layout, with editor mode stored as a vault preference. Paths and ranked search hits do not define the entire query candidate universe. DocumentManager serializes accepted folder publications and deletion by repository instance and document identity. Deletion fences a detached identity through failures; folder attachment/factory publication checks loaded canonical parent evidence in its synchronous mutation segment. Failed factory cleanup preserves owned or concurrently written bodies. These are local mutation guarantees, not distributed ownership locks. Account lifecycle ownership is explicit: `account.ts` establishes identity and validates vault selection; a DID-bound opening intent carries an explicit choice across reload. `workspace-lifecycle.ts` owns edit acceptance, accepted-work drains, local durability and irreversible close/navigation. Initial login and selection use that handoff. Same-account reauthentication and sync retry keep the mounted workspace and handles. Logout pauses UI acceptance, requires publication, closes writers, then resets all Reams-owned local state and recreates anonymous onboarding. Its durable `closing` checkpoint resumes irreversible cleanup; an orphaned `draining` attempt reopens locally without authentication. Normal edition boot is the sole mounted account-vault opener. No temporary login repository or separate login file hierarchy is constructed. Classic uses Automerge-repo, IndexedDB, BroadcastChannel, and optional WebSocket. ATProto uses its native repository/compatibility adapter. The shared seam is `src/storage/document-runtime-contract.ts`. Canonical change observation can precede flush completion. Local durability and remote publication are distinct: ATProto adapter `flush`/`flushLocal` acknowledge device-local durability. `publish` is a separate barrier used by adoption and logout. Successful publication of a captured snapshot does not imply no later writes are queued; destructive account retirement separately requires an empty durable outbox and no live account runtime. Access tokens refresh automatically. Temporary refresh/provider failures retain credentials and retry without interactive login. A known local account can recover an orphaned active marker through library validation of its matching stored session; explicit logout removes those credentials. A definitively rejected refresh grant offers reauthorization while the account profile and local editing remain available. There is no separate offline account workspace. `OnboardingState` stores installation completion in `.reams/onboarding.json`. Host-created onboarding files, assets and folders record explicit starter provenance (`reamsOnboarding`, version 1); origin `anonymous` identifies signed-out starters, while `vault` identifies defaults installed directly into a vault. Text, binary and folder creation accept explicit `onboarding: true`; user files reused by path are not relabeled as starters. First setup adopts anonymous starters; returning accounts discard them without publishing their histories and transfer ordinary user files. Clean destination folder snapshots exclude discarded starter references while preserving user-file identities/history. Completed account vaults do not reseed defaults on ordinary boot. `install-onboarding.ts` installs the bundled v7 welcome note, its nine tagged wikilink placeholders, and inline photo/icon assets under `onboarding/images/` before recording completion. Templates install first; initial timestamps keep them below companions and welcome in Quick Access's default recent-edit order. Companion files/folders have root-scoped reserved identities and starter provenance; existing user paths retain their contents and ownership. Each welcome revision uses a new reserved scope so earlier immutable founding histories stay compatible. The 2026-10-10 welcome refresh runs after workspace readiness for existing vaults. Its descriptor in `welcome-refresh-upgrade.ts` pins eligibility to template revisions 1–5, independently of future fresh-user template changes. Revision 6 and newer are ignored, even without a receipt. It also recognizes the original Classic founding source (including later edits), installs companions, and swaps the old visible link for one root-scoped replacement. The synced `welcomeRefresh20261010` receipt in `.reams/onboarding.json` is written after local durability. Fresh installations record it immediately; subsequent editing, moving or deletion never triggers another refresh. Old histories are detached, not rewritten or globally tombstoned. Refresh completion requires a full canonical traversal of uncontested folder lists, followed by frontier validation after local durability. Missing bodies defer it; moves trigger rescanning. Occupied companion paths relocate the upgrade bundle to a free `Reams onboarding` folder and rewrite its links; replacement genesis is bound to that source payload. Workspace effects respect newer leaf open revisions and navigation rather than relying on stale active-file snapshots. Upgrade companions retain identity/placement bindings, so interrupted-install renames and moves rewrite guide links instead of restoring old paths. Bindings are checked at publication and inside the synchronous receipt mutation; untouched updater artifacts can be rebound before completion, while user-modified guides and their native state are never reconstructed. Imported guides from another root are not mistaken for this vault's own replacement. Scoped leaf handoff leases keep ordinary delete-driven tab closing from racing the reference swap; leases are released on completion, error and cancellation. Pending user opens and newer navigation still take precedence. This is a temporary release migration, not a continuous onboarding updater. Once the small legacy cohort has updated, retire its background runner, historical fixture and refresh-only machinery in a follow-up release; keep the synced receipt and normal first-install/completion policy. App release SemVer is not used to infer a note's revision or authorize overwriting user documents. ## Useful checks - `npm run typecheck` checks the TypeScript project; `npm test` combines selected lint, unit/type tests, and one seeded property campaign. - `npm run test:coverage` requires 100% coverage **of the explicit configured scope**, not the entire application. It is the configured pre-commit hook. - `npm run depcruise`, `npm run knip:report`, edition-source and boundary ratchets inspect architecture. Known drift is recorded in the [maintainability audit](audits/2026-10-05-maintainability.md); inspect results before treating them as reliable gates or deleting apparent dead code. - Browser, property, performance, and deployment tests have separate commands and prerequisites. See `package.json`, the corresponding configs, and the Nix browser instructions in `AGENTS.md`.