diff --git a/DESIGN/02-architecture.md b/DESIGN/02-architecture.md index 0387e23..cd1db01 100644 --- a/DESIGN/02-architecture.md +++ b/DESIGN/02-architecture.md @@ -53,6 +53,7 @@ The read-side rules above have a write-side mirror — one banner vocabulary for - **The debounced body save retries on its own cadence** — keystrokes stay in the dirty buffer, so nothing is lost while the window stays open; the banner stands until a save lands. The **one modal moment on the write-failure path**: closing a window (or the board, or quitting) with a dirty buffer that cannot be written — the only state that exists nowhere but memory — raises an alert (retry / save a copy elsewhere / discard) instead of failing silently. Everything else on this path stays non-modal. (Deliberate confirmations elsewhere are their own stories: Empty Trash… and Delete Immediately on boards without git history — 03-board-ui.md, the branch-switch save-or-discard step — 06-history-undo.md, machine-key regeneration — 07-sync-collab.md, the raw-source Apply validation alert — 05-card-window.md, the once-per-board iCloud/network-volume warning on open/create — 07, and the SSH trust-on-first-use fingerprint confirmation with its mismatch hard-block — 07.) - **A renamed or moved board root follows its file identity** (settled): the board the app has open is the *file*, not the path string — the registry's security-scoped bookmark is the identity, mid-session as much as across opens (01-storage-format.md calls Finder renames ordinary, and mid-session must honor that). On any root-gone signal — the watcher's path stops delivering, a write lands on a stale path — the app first **re-resolves the bookmark**: if it resolves to a new location, the rename/move is absorbed transparently — the watcher re-attaches there, Writer URLs and card-window keys re-derive from the new root, one full reload runs, and the window title follows the folder-name fallback where it applies — no banner, no lock, nothing was ever wrong. Only when the bookmark does not resolve is the root truly vanished (below). - **A vanished board root locks the board read-only** — the bracketed-reload vocabulary applied to a root that is gone (volume unmounted, folder Finder-deleted while open — and the rename re-resolution above found nothing): every write would land nowhere, so the last-good snapshot stays on screen, read-only, banner up. The watcher keeps watching; if the root returns (remount, Finder undo), the next successful reload clears the lock and pending dirty buffers save normally. +- **An unwritable board location enters the read-only lock at open** (settled): opening probes the root's writability — a read-only volume (DMG, snapshot, read-only share) or a permission-denied folder opens straight into the read-only lock, banner naming the cause ("this board's volume is read-only"), rather than letting every gesture fail one at a time — fail loudly, specifically, *once*. The open-time agent-guide write (08-agent-integration.md) is skipped-with-log, the `CLAUDE.user.md`-taken precedent. Writability re-probes on every reconciling reload (wake, activation — above), so a fixed permission or rewritable remount clears the lock without ceremony. The lock's read affordances stay live as always — inspecting an archived board on a DMG is a legitimate errand, and viewing-first is the point. - **Auto-commit failures beyond `index.lock` contention** (06-history-undo.md covers the lock) — disk full mid-commit, repo corruption: the files are safely on disk but history stops advancing, which quietly suspends the undo trail and the flush-before-overwrite guarantee. That degradation is surfaced, not hidden: the banner states that changes aren't being recorded to history; the committer retries on the next debounce and the banner clears on the first successful commit. - **Attachment import copy failures** (source unreadable, destination full): the drop was accepted — "never refuses the drop" (01-storage-format.md ▸ Attachments) is policy, not an I/O guarantee — so a failed copy surfaces in the banner with the filename, and any partial file is removed; no half-copied attachment is ever left in `attachments/`.