The paper agrees with the code — guide v11, README, DESIGN re-rulings, and the adjudicated sweep
Step 7 of strategy/01-git-excision.md, the companions. The agent guide bumps to v11: the Git section teaches repo-resident etiquette alone (stage only your own paths, commit your own changes, leave app-maintained files to the app) — existing boards heal to the new text on next open. README re-anchors: the four git feature bullets out, tiers say the complete Mac experience is free, and one bullet states the format's git-friendliness promise. The changelog drops the never-shipped git entries. DESIGN re-rules: 06 retired with Undo routing migrated to 13 (now the sole substrate's doc, seam kept open), 07 retired as written pending the ops-service workstream, 14 retired as superseded record, 12 carries the second pivot note, the index reflects all of it; the charter gets a pointer note (the anchors' full re-ruling stays with the user). InertGitTests renames to GitAgnosticStorageTests — the excision restores its original claim app-wide. And the sweep: ~70 comment sites across 36 files adjudicated against the keeper list, every present-tense description of the excised machinery made past tense or repointed, keepers untouched. 2,707 tests green. Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -442,9 +442,10 @@ public final class BoardStore: HealHost {
|
||||
/// answer to "which ledger is this board's", and a second one would be a second provenance.
|
||||
///
|
||||
/// Its consumers today are the announcer's digest and the vanishing-focus sentence, both
|
||||
/// through `land`. Pro's auto-committer (06-history-undo.md) becomes the second one without
|
||||
/// this line changing — which is why the type lives in `LiveStore/` beside `BoardDiff` rather
|
||||
/// than inside the git provider.
|
||||
/// through `land`. The foreign-change journal is slated to become the second one without this
|
||||
/// line changing (`strategy/01-git-excision.md` ▸ Successors) — once the auto-committer's job,
|
||||
/// before app-managed git was excised 2026-08-08 — which is why the type lives in `LiveStore/`
|
||||
/// beside `BoardDiff` rather than inside any one consumer's module.
|
||||
@ObservationIgnored
|
||||
public let echoes = EchoLedger()
|
||||
|
||||
@@ -668,9 +669,11 @@ public final class BoardStore: HealHost {
|
||||
/// raised lock above the completion), and the phrase must not survive to be spoken by some
|
||||
/// later, unrelated reload.
|
||||
///
|
||||
/// **`nil` on every free-tier bracket today.** The free tier has no git operations, and the design's
|
||||
/// examples ("Pulled 3 commits", "Switched to branch 'redesign'") are pro-m1's; the parameter
|
||||
/// exists so that milestone supplies phrasing rather than re-plumbing the seam.
|
||||
/// **`nil` on every bracket today.** App-managed git is gone (`strategy/01-git-excision.md`,
|
||||
/// 2026-08-08) and nothing currently supplies a phrase — the design's examples ("Pulled 3
|
||||
/// commits", "Switched to branch 'redesign'") were git's. The parameter stays so a future
|
||||
/// wholesale operation (the ops-based sync service — `strategy/01-git-excision.md` ▸ Successors)
|
||||
/// can supply one without re-plumbing the seam.
|
||||
@ObservationIgnored
|
||||
private var wholesaleCompletion: String?
|
||||
|
||||
@@ -1399,12 +1402,13 @@ public final class BoardStore: HealHost {
|
||||
///
|
||||
/// - Parameter completion: what to announce when the closing reload lands
|
||||
/// (10-accessibility.md ▸ Live board announcements: "bracketed operations announce once, at
|
||||
/// completion" — "Pulled 3 commits", "Switched to branch 'redesign'"). `nil`, the default, is
|
||||
/// an operation whose completion is not worth speech, which is **every free-tier bracket
|
||||
/// today**: no git operations run there, and the two app-initiated writes that do reach disk on
|
||||
/// their own — the loose-file relocation and the legacy-tombstone migration — are ordinary
|
||||
/// `performWrite` calls that already say what they did on the banner strip. The parameter is
|
||||
/// the seam pro-m1 fills; see `wholesaleCompletion`.
|
||||
/// completion" — "Pulled 3 commits", "Switched to branch 'redesign'", both git's). `nil`, the
|
||||
/// default, is an operation whose completion is not worth speech, which is **every bracket
|
||||
/// today**: app-managed git is gone (`strategy/01-git-excision.md`, 2026-08-08), and the two
|
||||
/// app-initiated writes that do reach disk on their own — the loose-file relocation and the
|
||||
/// legacy-tombstone migration — are ordinary `performWrite` calls that already say what they
|
||||
/// did on the banner strip. The parameter is the seam a future wholesale operation fills; see
|
||||
/// `wholesaleCompletion`.
|
||||
///
|
||||
/// - Throws: `BoardStoreWriteRefusal.readOnlyLocked` if the board is already locked — a locked
|
||||
/// board refuses to *start* wholesale work, not just ordinary writes. Otherwise rethrows
|
||||
@@ -1484,8 +1488,7 @@ public final class BoardStore: HealHost {
|
||||
/// Three ways this does nothing, all deliberate: a count below 1 clamps to 1 (a lane spans at
|
||||
/// least one unit), an id that is not in the snapshot is ignored (the lane vanished under the
|
||||
/// gesture — the reload that removed it is the authority), and a count already equal to what the
|
||||
/// lane displays writes nothing (a drag that ends where it started must not stamp `modified` or
|
||||
/// mint a git commit).
|
||||
/// lane displays writes nothing (a drag that ends where it started must not stamp `modified`).
|
||||
///
|
||||
/// Failures are already the banner's: `performWrite` posts every `BoardWriteError` before it
|
||||
/// rethrows, so the rethrow is swallowed here rather than propagated to a gesture that has no
|
||||
@@ -1519,7 +1522,7 @@ public final class BoardStore: HealHost {
|
||||
|
||||
/// The one commit point every width mechanism shares — the edge drag, the context-menu stepper,
|
||||
/// and the menu items' batch. One `performWrite` bracket whatever the count: one gesture, one
|
||||
/// app-mediated reload, one commit on git boards (the style batch's rule).
|
||||
/// app-mediated reload (the style batch's rule).
|
||||
///
|
||||
/// **A width landing on 1 removes the `width` key** (03-board-ui.md § Lane, settled — the
|
||||
/// remove-at-default family beside the empty rename's `title` and the None well's
|
||||
@@ -1674,13 +1677,14 @@ public final class BoardStore: HealHost {
|
||||
/// row with them.
|
||||
///
|
||||
/// **One bracket, whatever the target set's size.** "Choosing a well applies to the whole
|
||||
/// selection — one gesture, one commit on git boards" (§ Controls), so every target's `index.md`
|
||||
/// is rewritten inside a single `performWrite`: the churn rounds back as one app-mediated reload,
|
||||
/// and the auto-committer (m7) sees one operation rather than N.
|
||||
/// selection — one gesture, one write" (§ Controls), so every target's `index.md`
|
||||
/// is rewritten inside a single `performWrite`: the churn rounds back as one app-mediated reload
|
||||
/// — once one operation for the auto-committer too (m7), before app-managed git was excised
|
||||
/// entirely (`strategy/01-git-excision.md`, 2026-08-08).
|
||||
///
|
||||
/// **No-ops are skipped per dimension and per target** — `setLaneWidth`'s rule, for its reason: a
|
||||
/// well clicked twice, or a batch where half the cards are already that colour, must not stamp
|
||||
/// `modified` or mint a commit on the items that were already right. A dimension whose value is
|
||||
/// `modified` on the items that were already right. A dimension whose value is
|
||||
/// already what the gesture asks contributes nothing; a target both of whose dimensions are
|
||||
/// no-ops is dropped entirely; and a gesture that changes nothing anywhere never opens the
|
||||
/// bracket at all.
|
||||
@@ -1883,8 +1887,8 @@ public final class BoardStore: HealHost {
|
||||
/// The image lands first and the frontmatter second, so a failure to write the picture never
|
||||
/// leaves the board naming one that is not there. The reverse order would; the two are not atomic
|
||||
/// together, and this is the ordering that makes the non-atomic half harmless. Both are inside the
|
||||
/// same bracket, so the churn rounds back as one app-mediated reload and mints one commit on git
|
||||
/// boards — the style batch's rule, one gesture one commit.
|
||||
/// same bracket, so the churn rounds back as one app-mediated reload — the style batch's rule,
|
||||
/// one gesture one write.
|
||||
///
|
||||
/// ### The name is chosen, not minted
|
||||
///
|
||||
@@ -2218,7 +2222,7 @@ public final class BoardStore: HealHost {
|
||||
/// "Committing an empty rename on an existing item removes its `title` key"), rather than
|
||||
/// writing `title: ""` — titles are optional, and the face shows the untitled placeholder.
|
||||
/// - **An unchanged title writes nothing.** `setLaneWidth`'s rule, for the same reason: an
|
||||
/// editor opened and dismissed with Return must not stamp `modified` or mint a commit.
|
||||
/// editor opened and dismissed with Return must not stamp `modified`.
|
||||
///
|
||||
/// The folder is re-derived from the *current* snapshot, which is what makes a foreign move
|
||||
/// mid-rename invisible: the editor follows the UUID, and the write lands wherever the item is
|
||||
@@ -2319,8 +2323,9 @@ public final class BoardStore: HealHost {
|
||||
/// Ticks or unticks a Preview task-list checkbox — **the app's one write into a card's body**
|
||||
/// (05-card-window.md ▸ Preview), and otherwise an entirely ordinary one: the same
|
||||
/// `performWrite` bracket, the same banner on failure, the same one-way flow back through the
|
||||
/// watcher. "A toggle is an ordinary user edit — the standard atomic write, auto-committed and
|
||||
/// undoable on git boards."
|
||||
/// watcher. "A toggle is an ordinary user edit — the standard atomic write." (Once
|
||||
/// "auto-committed and undoable on git boards" too; app-managed git is gone now,
|
||||
/// `strategy/01-git-excision.md`.)
|
||||
///
|
||||
/// `bodyOffset` is the UTF-8 byte offset the parse handed the renderer (`BodyTask
|
||||
/// .markerOffset`) and `checked` is the state the user was looking at; both travel to
|
||||
@@ -2330,9 +2335,10 @@ public final class BoardStore: HealHost {
|
||||
///
|
||||
/// **It registers no undo step.** 13-native-undo.md ▸ Rules' inventory names the body write it
|
||||
/// makes undoable precisely — "Edit-session body save → restore prior body bytes" — and a Preview
|
||||
/// checkbox is not one: it belongs to no session, has no flip to coalesce at, and 05 files it
|
||||
/// under what is "undoable on git boards", which is the *other* substrate's answer. Registering it
|
||||
/// here would be extending 13's inventory rather than implementing it.
|
||||
/// checkbox is not one: it belongs to no session and has no flip to coalesce at. (05 once filed it
|
||||
/// under "undoable on git boards" instead, the other substrate's answer; app-managed git is gone
|
||||
/// now, `strategy/01-git-excision.md`, so a checkbox toggle is simply not undoable today.)
|
||||
/// Registering it here would be extending 13's inventory rather than implementing it.
|
||||
///
|
||||
/// **A checkbox in a card that has gone writes nothing** — the vanished-target guard every
|
||||
/// gesture in this file makes, ancestor-walked through `boardItem`: the card window would be
|
||||
@@ -2356,8 +2362,9 @@ public final class BoardStore: HealHost {
|
||||
/// flush that closes the window (05-card-window.md ▸ Edit).
|
||||
///
|
||||
/// An ordinary store write in every mechanical respect: one `performWrite` bracket, so the churn
|
||||
/// rounds back as a single app-mediated reload (and, on git boards, sits inside the session's
|
||||
/// one commit — see `CardBodyEditSession` for that seam); the banner posts itself on failure;
|
||||
/// rounds back as a single app-mediated reload (once it also sat inside the session's one commit
|
||||
/// on git boards — see `CardBodyEditSession` for that seam, native-only now since the excision,
|
||||
/// `strategy/01-git-excision.md`); the banner posts itself on failure;
|
||||
/// the snapshot is never touched here, because the watcher's reload is what brings the text
|
||||
/// back.
|
||||
///
|
||||
@@ -2605,7 +2612,7 @@ public final class BoardStore: HealHost {
|
||||
/// name* (§ Board naming) — never the "Untitled" placeholder cards and lanes show, and never
|
||||
/// `title: ""`, which would be a real if blank title with nothing to fall back to.
|
||||
/// - **An unchanged title writes nothing**, so a popover opened and dismissed with Return
|
||||
/// neither stamps `modified` nor mints a commit.
|
||||
/// does not stamp `modified`.
|
||||
///
|
||||
/// There is no vanished-target guard, because a board cannot tombstone itself out of its own
|
||||
/// window (01-storage-format.md § Deletion): the only way this target goes away is the root
|
||||
@@ -2652,8 +2659,8 @@ public final class BoardStore: HealHost {
|
||||
/// exactly one file — the moved lane's `order`.
|
||||
///
|
||||
/// **A drag that ends where it started writes nothing**: `index == from` re-inserts the lane in
|
||||
/// its own slot, and a no-op must not stamp `modified` or mint a commit — the resize drag's
|
||||
/// rule, and for the same reason.
|
||||
/// its own slot, and a no-op must not stamp `modified` — the resize drag's rule, and for the
|
||||
/// same reason.
|
||||
public func moveLane(_ id: ItemID, toIndex index: Int) {
|
||||
let lanes = snapshot.lanes
|
||||
guard let from = lanes.firstIndex(where: { $0.id == id }) else { return }
|
||||
@@ -2730,14 +2737,13 @@ public final class BoardStore: HealHost {
|
||||
///
|
||||
/// `moveLane`'s plural, and it exists rather than a loop over it because "one `performWrite`
|
||||
/// bracket per gesture whatever the set's size" is load-bearing (DRAG-REORDER.md § The drop
|
||||
/// commits): one app-mediated reload, and on git boards one commit rather than N.
|
||||
/// commits): one app-mediated reload rather than N.
|
||||
///
|
||||
/// The run keeps **board order**, which is the lane level's flatten order — a multi-lane drag has
|
||||
/// no other relative order to preserve.
|
||||
///
|
||||
/// A drag that changes nothing writes nothing, stated as the arrangement rather than as a special
|
||||
/// case: if the strip would render exactly what it renders now, no rank is rewritten and no
|
||||
/// commit is minted.
|
||||
/// case: if the strip would render exactly what it renders now, no rank is rewritten.
|
||||
public func moveLanes(_ ids: Set<ItemID>, toIndex index: Int) {
|
||||
let lanes = snapshot.lanes
|
||||
let members = lanes.filter { ids.contains($0.id) }
|
||||
@@ -2910,8 +2916,7 @@ public final class BoardStore: HealHost {
|
||||
// produces their `index` is DRAG-REORDER.md's, implemented in `DropSlotMath`).
|
||||
//
|
||||
// **One `performWrite` bracket per gesture**, whatever the set's size — the style batch's and
|
||||
// the tombstone batch's rule, for their reason: one gesture, one app-mediated reload, one commit
|
||||
// on git boards.
|
||||
// the tombstone batch's rule, for their reason: one gesture, one app-mediated reload.
|
||||
//
|
||||
// **`index` always means the same thing**: a position among the destination's *rendered* items
|
||||
// counted with the dragged run already removed — the resting layout's own convention, so the
|
||||
@@ -2920,7 +2925,7 @@ public final class BoardStore: HealHost {
|
||||
// must not trap.
|
||||
//
|
||||
// **Ranks are inserted, never permuted.** A drop rewrites only the dragged items' `order`, so
|
||||
// the siblings' files — and `modified`, and a git commit — stay honest about what actually
|
||||
// the siblings' files — and `modified` — stay honest about what actually
|
||||
// moved. That is the one place these differ from `sortSelection`, which permutes because its
|
||||
// gesture is a permutation. `Ranks.insertionRanks` answering `nil` is the renumber trigger, and
|
||||
// the fallback is `moveLane`'s: compact the destination, then place against the fresh ladder.
|
||||
@@ -2928,8 +2933,8 @@ public final class BoardStore: HealHost {
|
||||
// **Silent no-ops throughout**, all of them the reload being the authority rather than the
|
||||
// gesture: a destination lane that is gone or tombstoned (04's "a card is never filed under a
|
||||
// `deleted:` parent"), a dragged set emptied by a foreign reload, and a drop that lands exactly
|
||||
// where everything already is (a drag that ends where it started must not stamp `modified` or
|
||||
// mint a commit — the resize drag's rule).
|
||||
// where everything already is (a drag that ends where it started must not stamp `modified` —
|
||||
// the resize drag's rule).
|
||||
|
||||
/// One member of a dragged card set, resolved against the snapshot: **where it is now** — a lane,
|
||||
/// or the board's trash.
|
||||
@@ -3507,8 +3512,7 @@ public final class BoardStore: HealHost {
|
||||
/// `FileManager` call in the view: it mutates the card's folder, so the churn has to round back
|
||||
/// as one *app-mediated* reload (the echo the watcher would otherwise read as a foreign edit),
|
||||
/// it has to refuse under the read-only lock like every other mutation (`performWrite`'s gate),
|
||||
/// and its failures have to reach the banner strip like every other write's. On git boards it
|
||||
/// is also one commit, for free, for the same reason.
|
||||
/// and its failures have to reach the banner strip like every other write's.
|
||||
///
|
||||
/// The guards are `importAttachments`' exactly, and its inverse in every way: **the board
|
||||
/// container and only it** (`boardItem`), so a trashed card is as unreachable as a deleted one
|
||||
@@ -3546,12 +3550,13 @@ public final class BoardStore: HealHost {
|
||||
/// **It registers no undo step**, and unlike its neighbours that is not a deferral: nobody asked
|
||||
/// for it. The relocation is the app tidying its own house on a reload, not a gesture — there is
|
||||
/// no ⌘Z that should follow it, and putting one on the stack would let the next ⌘Z undo something
|
||||
/// the user never did. (It is `renumberVisibleChildren`'s posture: bookkeeping composes no event,
|
||||
/// 06-history-undo.md ▸ Commit messages.)
|
||||
/// the user never did. (It is `renumberVisibleChildren`'s posture: bookkeeping composes no event —
|
||||
/// once 06-history-undo.md ▸ Commit messages' rule, now the change narrator's,
|
||||
/// `Kanban/Changes/ChangeNarrator.swift`.)
|
||||
///
|
||||
/// **One bracket over the whole board's worth of relocation**, so the churn rounds back as a
|
||||
/// single app-mediated reload and (on git boards) a single commit — the style batch's rule,
|
||||
/// applied to a batch the app started itself. The snapshot is not touched here any more than it
|
||||
/// single app-mediated reload — the style batch's rule, applied to a batch the app started
|
||||
/// itself. The snapshot is not touched here any more than it
|
||||
/// is anywhere else: the files move, the watcher notices, the reload lands.
|
||||
///
|
||||
/// **The write half re-verifies against disk**: `BoardWriter.relocateLooseFiles` re-reads each
|
||||
@@ -3728,13 +3733,13 @@ public final class BoardStore: HealHost {
|
||||
/// Board ▸ Move Up / Move Down (⌥⌘↑/⌥⌘↓) — the within-lane sort (04-interactions.md ▸ The map).
|
||||
///
|
||||
/// **One `performWrite` bracket**, like every other batch here: one gesture, one app-mediated
|
||||
/// reload, one commit on git boards.
|
||||
/// reload.
|
||||
///
|
||||
/// **The ranks are permuted, not invented.** The lane's existing `order` values, read in display
|
||||
/// order, are already a sorted ladder of exactly the right length — so the new ordering takes
|
||||
/// them rung for rung and only the cards whose *position* changed are rewritten. A block stepping
|
||||
/// past one sibling therefore touches the block plus that sibling and nothing else, which is what
|
||||
/// keeps `modified` (and, later, a git commit) honest about what actually moved.
|
||||
/// keeps `modified` honest about what actually moved.
|
||||
///
|
||||
/// The one case that ladder cannot serve is **duplicate `order` values**, where display order is
|
||||
/// decided by the folder-name tie-break (`Ranks.isOrderedForDisplay`) rather than by the rank —
|
||||
@@ -3824,14 +3829,15 @@ public final class BoardStore: HealHost {
|
||||
/// therefore whether a permanent delete stands an alert between one keystroke and unrecoverable
|
||||
/// deletion (03-board-ui.md § Trash, "Both confirm exactly where the loss is real").
|
||||
///
|
||||
/// **Every board is `true` today**, because every board is history mode *none*: nothing in the
|
||||
/// app keeps a second copy, so a purge is final everywhere.
|
||||
/// **Every board is `true`**, because app-managed git is gone (`strategy/01-git-excision.md`,
|
||||
/// 2026-08-08) and every board is history mode *none*: nothing in the app keeps a second copy, so
|
||||
/// a purge is final everywhere.
|
||||
///
|
||||
// m7-git: git boards answer `false` here — "on git boards they act immediately (delete-never-
|
||||
// forgets)" (06-history-undo.md). Repo-nested boards stay `true` alongside mode none: the app
|
||||
// manages no history for them either. The named predicate exists now so the committer card
|
||||
// changes one expression rather than hunting the confirmation logic out of two menu items and an
|
||||
// alert.
|
||||
// Once slated for a `false` answer on git boards — "on git boards they act immediately
|
||||
// (delete-never-forgets)" (06-history-undo.md, retired) — before the excision closed that door.
|
||||
// Repo-nested boards stay `true` alongside every other board now: the app manages no history for
|
||||
// any of them. The predicate stays a predicate rather than a literal at its two call sites
|
||||
// regardless — cheap insurance if a future substrate ever needs a `false` again.
|
||||
public var purgeIsUnrecoverable: Bool { true }
|
||||
|
||||
/// **File ▸ Delete ⌘⌫ and its plain-⌫ grammar twin — staged by place** (04-interactions.md ▸
|
||||
@@ -4323,8 +4329,8 @@ public final class BoardStore: HealHost {
|
||||
/// `comments` is a tolerated stray until the feature consumes the name.
|
||||
///
|
||||
/// **One bracket over every displacement the load found**, whatever their levels: the batch is one
|
||||
/// app-mediated reload and, on git boards, one heal commit — the loose-file relocation's rule, and
|
||||
/// this heal's own memo is board-wide anyway.
|
||||
/// app-mediated reload — the loose-file relocation's rule, and this heal's own memo is board-wide
|
||||
/// anyway.
|
||||
///
|
||||
/// **Displacement, never destruction**, and never a mint: the freed name is left empty and the next
|
||||
/// gesture that needs it creates the real folder — the next delete mints `.trash/`, the next import
|
||||
@@ -4483,9 +4489,11 @@ public final class BoardStore: HealHost {
|
||||
// MARK: - The board's noise definition
|
||||
|
||||
/// Puts the seeded `.gitignore` on a board that has none, and does nothing at all to a board
|
||||
/// that has one — 06-history-undo.md ▸ Repository hygiene's whole scheduling ("a board missing
|
||||
/// the file gains it by scheduled heal at open (the guide-refresh cadence)", re-ruled
|
||||
/// 2026-07-31).
|
||||
/// that has one — the scheduled-heal cadence every heal in this file shares (02-architecture.md ▸
|
||||
/// Components ▸ HealScheduler): "a board missing the file gains it by scheduled heal at open (the
|
||||
/// guide-refresh cadence)", re-ruled 2026-07-31. (06-history-undo.md ▸ Repository hygiene once
|
||||
/// carried this rule under a git-flavored heading; the doc retired with the git excision,
|
||||
/// `strategy/01-git-excision.md`, 2026-08-08 — the noise-gate rule itself did not.)
|
||||
///
|
||||
/// **`refreshAgentGuide()`'s twin, deliberately**, down to the shape of this method: the file is
|
||||
/// a board-root courtesy the app owns, its defect is *presence* rather than anything a tree walk
|
||||
@@ -4502,7 +4510,8 @@ public final class BoardStore: HealHost {
|
||||
/// 6), which is what makes a foreign deletion healable: the picture "missing" is restored by the
|
||||
/// delete, and a standing memo would make that deletion the one thing this could not answer —
|
||||
/// while a failing write still gets exactly one attempt per changed picture rather than one per
|
||||
/// reload. "Deletion is answered by re-seeding" is 06's own wording.
|
||||
/// reload. "Deletion is answered by re-seeding" was 06-history-undo.md's own wording; the rule
|
||||
/// survives the doc's retirement (`strategy/01-git-excision.md`, 2026-08-08).
|
||||
///
|
||||
/// **It registers no undo step**, like every heal: nobody asked for it.
|
||||
public func seedGitignore() {
|
||||
|
||||
Reference in New Issue
Block a user