Ratify the edition split and the 2026-07-28 design-review resolutions

Edition split follow-through: 12-editions.md and 13-native-undo.md join the
corpus; git-era docs (06, 07) are Pro-scoped and cross-references realigned.

Resolution session rulings written in place: caret chords yield to any focused
text control (04, 11); the drop settle holds its proposal as overlay state and
02 gains the overlays family; Duplicate gets the save-panel fallback and a
cancellable copy walk (03); Finder open is a standard document open (02);
failed first opens record before loading with the folder name provisional (02);
trash pointer ranges skip by kind, full-height marquee surface, Select All by
kind, rows-only trashed universe (02, 04); Empty Trash counts entries (03);
create handoff reads as one arrival (02); range-anchor lifecycle, board-
background click grammar, jump landing cards, lane-domain shift-arrows (04);
Reduce Motion restated per voice (10).

Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
2026-07-27 20:29:32 -04:00
parent edc094a5f4
commit cacd48cb0f
14 changed files with 164 additions and 57 deletions
+5 -3
View File
@@ -1,5 +1,7 @@
# Sync & Collaboration
**Edition scope: Lanework Pro** (12-editions.md). Base Lanework ships mode:none only — the state machine below never leaves its first state there, `.git` encountered on disk is inert (12), and the Mode: none section's no-undo caveat is superseded in base by native undo (13-native-undo.md). Teams adds tracker-backed sync behind the same seam (deferred).
Every board has exactly one **collab mode** at a time, but the mode is not fixed at creation — it can evolve over the board's lifetime:
```
@@ -21,9 +23,9 @@ The board is a git repository (the 06-history-undo.md substrate — undo/redo, a
- Push/pull becomes a sharing mechanism between machines/people at file-level granularity; the fractal one-item-one-file design keeps conflicts rare and small (a reorder touches one file).
- **Remote tracking lives in the board popover** (03-board-ui.md): ahead/behind indicator plus manual **Pull** and **Push** controls — which are also Board-menu items (no default chord, remappable — 11-command-nexus.md; the configuration carve-out is 04-interactions.md's).
- **Which remote is the board's remote** (settled — adopted clones can carry several): resolution is git's own defaulting — the current branch's upstream remote; else `origin`; else the repo's sole remote. The popover names the remote it tracks, its change-remote control edits exactly that one, add-remote on a remote-less repo creates `origin`, and 06-history-undo.md's quiet first `push -u` targets the resolved remote (recording it as the upstream, which pins resolution thereafter). The unresolvable case — several remotes, no upstream, none named `origin` — is surfaced honestly: remote operations disable and the popover offers a one-time remote picker, whose choice becomes the branch's upstream on the next push.
- **Optional push-on-commit**: a per-board setting (stored app-side in the board registry — 02-architecture.md's per-board app state); when enabled, every auto-commit is pushed immediately.
- **Optional push-on-commit**: a per-board setting (stored app-side in the board registry — 02-architecture.md's per-board app state); when enabled, **every commit the app makes** is pushed immediately — auto-commits and undo/redo restore commits alike (06-history-undo.md's restores are commits like any other; a shared board never shows a phantom lag after an undo).
- **Push failures never nag.** A push rejected as non-fast-forward (another machine pushed first) triggers an automatic **fetch → rebase → push**, with bounded retries — the same rebase machinery as Pull, so it cannot block and cannot conflict. Manual Push behaves identically. Stated plainly: enabling push-on-commit implicitly accepts that remote commits may land in the live board whenever pushes race — consistent with the board's live-reload nature, but it should be learned from the design, not discovered. All other push failures stay quiet: pushes queue, the ahead/behind indicator in the board popover carries the pending count and the last error, and pushing resumes automatically on the next commit or manual Push. No modals, no per-commit errors. The one refinement: **authentication failures pause rather than retry** — see Remote authentication below.
- **There can be no conflicts — and no data loss.** Every edit becomes a commit before anything can overwrite it (auto-commit settles local changes; the tree is clean by the time a pull runs). The clean-tree premise is why **abnormal repo states pause the remote half too**: a detached HEAD or in-progress merge/rebase holds Pull, Push, and push-on-commit alongside auto-commit (06-history-undo.md ▸ Abnormal repo states — the whole git surface pauses; the ahead/behind badge keeps counting, a fetch being a read). A pull — manual, or the automatic fetch-rebase after a rejected push — runs only at **interaction rest**: it queues behind an in-flight drag or open inline editor (the same settled-change notion the auto-commit debounce uses), flushes the pending auto-commit (06-history-undo.md's flush-before-overwrite), then runs bracketed (02-architecture.md's live-reload resilience) — never an error dialog, never a board yanked mid-drag. An open card-body **Edit session neither blocks a pull nor is interrupted by one**: the flush commits the session's on-disk saves as-is (06-history-undo.md's mechanical exception to session-granularity commits) and the rebase runs; when the pulled commits touch the very card being edited, the card window **signposts** the remote change (a transient banner, no modal, no merge UI) while the dirty buffer stays put and wins per 05-card-window.md — the losing remote version is a commit, one revert away. An open **raw-source buffer gets the same treatment** (settled): a pull neither blocks on it nor invalidates it — the bracket's write lock merely disables Apply while the pull runs — and the same-card signpost shows in source mode too (the banner strip is window furniture, not part of the swapped content area). A later Apply is last-writer-wins across the *whole file*, frontmatter included, with the overwritten pulled version a commit one revert away — on the same branch this is exactly the Edit-buffer race. (Branch switch and undo restore must gate raw buffers on save-or-discard instead — 06-history-undo.md ▸ Branch switching — because there a stale Apply would write onto a *different tree's* card, not merely race a newer version of the same one.) A pull fetches the remote's commits and **rebases local commits on top of them**; where a rebase hits a genuinely conflicting hunk, the **local side wins** — always, with no configuration. (This is the one deliberate history rewrite in the app, and it only ever touches unpushed local commits — see 06-history-undo.md's undo-never-rewrites rule.) Crucially, resolution discards nothing: the losing remote version survives intact in the fetched commits below, so an edit that "lost" the rebase is visible in any git client and one revert away. What may *appear* as data loss is always recoverable. No interactive merge UI, no conflict markers written by the app, sync never blocks. A pull that cannot start or fails cleanly follows 06-history-undo.md's app-initiated-operation posture — lock contention shows as a waiting state in the operation's banner row, clean failures as one-shot banner errors, an interrupted rebase is aborted-and-reported via the own-leftovers exemption; push alone keeps the richer queue-and-badge story (below). Conflict markers encountered in files (from git activity *outside* the app) fail fast only where they break parsing — markers in or around the frontmatter make the file the malformed-input case the loader rejects loudly with the offending path. Markers wholly inside a Markdown body are, honestly, valid input: they load fine and render as body text, and the app deliberately doesn't police body content to detect them (stated stance, not an oversight). (The old repo's AI-THINKING-merge-conflicts.md explored this territory; mine it when specifying the rebase mechanics.)
- **There can be no conflicts — and no data loss.** Every edit becomes a commit before anything can overwrite it (auto-commit settles local changes; the tree is clean by the time a pull runs). The clean-tree premise is why **abnormal repo states pause the remote half too**: a detached HEAD or in-progress merge/rebase holds Pull, Push, and push-on-commit alongside auto-commit (06-history-undo.md ▸ Rules ▸ Abnormal repo states — the whole git surface pauses; the ahead/behind badge keeps counting, a fetch being a read). A pull — manual, or the automatic fetch-rebase after a rejected push — runs only at **interaction rest**: it queues behind an in-flight drag or open inline editor (the same settled-change notion the auto-commit debounce uses), flushes the pending auto-commit (06-history-undo.md's flush-before-overwrite), then runs bracketed (02-architecture.md's live-reload resilience) — never an error dialog, never a board yanked mid-drag. An open card-body **Edit session neither blocks a pull nor is interrupted by one**: the flush commits the session's on-disk saves as-is (06-history-undo.md's mechanical exception to session-granularity commits) and the rebase runs; when the pulled commits touch the very card being edited, the card window **signposts** the remote change (a transient banner, no modal, no merge UI) while the dirty buffer stays put and wins per 05-card-window.md — the losing remote version is a commit, one revert away. An open **raw-source buffer gets the same treatment** (settled): a pull neither blocks on it nor invalidates it — the bracket's write lock merely disables Apply while the pull runs — and the same-card signpost shows in source mode too (the banner strip is window furniture, not part of the swapped content area). A later Apply is last-writer-wins across the *whole file*, frontmatter included, with the overwritten pulled version a commit one revert away — on the same branch this is exactly the Edit-buffer race. (Branch switch and undo restore must gate raw buffers on save-or-discard instead — 06-history-undo.md ▸ Branch switching — because there a stale Apply would write onto a *different tree's* card, not merely race a newer version of the same one.) A pull fetches the remote's commits and **rebases local commits on top of them**; where a rebase hits a genuinely conflicting hunk, the **local side wins** — always, with no configuration. (This is the one deliberate history rewrite in the app, and it only ever touches unpushed local commits — see 06-history-undo.md's undo-never-rewrites rule.) Crucially, resolution discards nothing: the losing remote version survives intact in the fetched commits below, so an edit that "lost" the rebase is visible in any git client and one revert away. What may *appear* as data loss is always recoverable. No interactive merge UI, no conflict markers written by the app, sync never blocks. A pull that cannot start or fails cleanly follows 06-history-undo.md's app-initiated-operation posture — lock contention shows as a waiting state in the operation's banner row, clean failures as one-shot banner errors, an interrupted rebase is aborted-and-reported via the own-leftovers exemption; push alone keeps the richer queue-and-badge story (below). Conflict markers encountered in files (from git activity *outside* the app) fail fast only where they break parsing — markers in or around the frontmatter make the file the malformed-input case the loader rejects loudly with the offending path. Markers wholly inside a Markdown body are, honestly, valid input: they load fine and render as body text, and the app deliberately doesn't police body content to detect them (stated stance, not an oversight). (The old repo's AI-THINKING-merge-conflicts.md explored this territory; mine it when specifying the rebase mechanics.)
## Remote authentication (settled)
@@ -40,7 +42,7 @@ Everything above assumes credentials exist; this is where they come from. Constr
## iCloud Drive — not supported (decided)
Boards should not live in iCloud Drive. The app makes **no iCloud accommodations**: no NSMetadataQuery watching, no eviction handling, no download triggering, no NSFileVersion conflict resolution. When the user opens or creates a board at a path inside iCloud Drive, the app **warns with a thorough explanation and recommends git integration instead** — it does not hard-block (the user is always right), but the warning must genuinely teach why this is a bad idea:
Boards should not live in iCloud Drive. The app makes **no iCloud accommodations**: no NSMetadataQuery watching, no eviction handling, no download triggering, no NSFileVersion conflict resolution. When the user opens or creates a board at a path inside iCloud Drive, the app **warns with a thorough explanation and recommends git integration instead** — it does not hard-block (the user is always right), but the warning must genuinely teach why this is a bad idea. (Base Lanework keeps the warning without the recommendation — there is no git to recommend and no Pro pitch in a warning; it recommends a local folder on the eviction/silent-fork grounds alone — 12-editions.md ▸ Edition naming in base.)
- **Git and iCloud corrupt each other.** A board with git enabled (the undo substrate) has a `.git` inside; iCloud syncs `.git` internals — thousands of small object files and constantly-rewritten refs/packs — poorly and non-atomically; partial or reordered sync can corrupt the repository. Two Macs auto-committing the same board produce divergent histories iCloud cannot merge.
- **Eviction breaks fail-fast loading.** iCloud may evict any file's contents to free space, leaving a placeholder. An evicted `index.md` is unreadable; with no download-trigger machinery the board simply fails to load with an I/O error until the user manually re-downloads it.