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:
@@ -15,7 +15,8 @@ import os
|
||||
/// touched and what they said before. It also keeps the one rule every step must obey in a single
|
||||
/// place — **an inverse is performed as an ordinary app-mediated write**, through `performWrite`,
|
||||
/// so an undo brackets the watcher, echoes back through the reload like any other change, refreshes
|
||||
/// every window on the board, and (on git boards, pro-m1) commits. Undone changes are real writes,
|
||||
/// every window on the board — once it also committed on git boards, pro-m1, before app-managed
|
||||
/// git was excised (`strategy/01-git-excision.md`, 2026-08-08). Undone changes are real writes,
|
||||
/// never in-memory reverts.
|
||||
///
|
||||
/// ### Values, never live references
|
||||
|
||||
@@ -13,13 +13,15 @@ import AppKit
|
||||
/// exactly how the system's Edit ▸ Undo row and the toolbar's nil-target pair (`BoardToolbar`) light
|
||||
/// up, disable and retitle with no code of the app's own.
|
||||
///
|
||||
/// The seam, though, must not be an `NSUndoManager`: a gitless board's stack is one, a Pro git
|
||||
/// board's is git (12-editions.md ▸ The provider seam), and a protocol that vended one could only
|
||||
/// ever have had a single implementation. So the substrate stays behind `HistoryProviding` and
|
||||
/// The seam, though, must not be an `NSUndoManager`: a protocol that vended one could only ever have
|
||||
/// had a single implementation, and the substrate stays behind `HistoryProviding` so a future
|
||||
/// provider can bind without touching this class (`strategy/01-git-excision.md` ▸ Reversibility). So
|
||||
/// *this* object is the translation — one per board session, over whichever provider that session
|
||||
/// was composed with. A git board inherits the whole command surface (enablement, dynamic titles,
|
||||
/// ⌘Z, the toolbar pair) by binding its provider and changing nothing here, which is what "a user
|
||||
/// subscribing (or lapsing) relearns nothing" (12) has to mean in code.
|
||||
/// was composed with. (Once two providers shared the seam this way — a gitless board's native stack,
|
||||
/// a Pro git board's git history, 12-editions.md ▸ The provider seam — with whichever bound
|
||||
/// inheriting the whole command surface, enablement, dynamic titles, ⌘Z, the toolbar pair, by
|
||||
/// binding and changing nothing here. App-managed git is gone now, 2026-08-08, and native is the
|
||||
/// only one left.)
|
||||
///
|
||||
/// **And one per open card window**, over that window's own stack (13 ▸ Rules ▸ two levels, re-ruled
|
||||
/// 2026-07-31 — `CardWindowUndo.manager`): the second level needs precisely the same translation, so
|
||||
@@ -52,8 +54,8 @@ import AppKit
|
||||
/// responder chain — validates through this object, so answering `false` here disables all of them
|
||||
/// at once, exactly as the lock's other victims disable through menu validation (02-architecture.md
|
||||
/// § "The lock's scope"). Putting it in the *provider* would have been the same answer in the wrong
|
||||
/// place: the stack is not the thing that is locked, the board is, and a Pro session binding the git
|
||||
/// provider must inherit the rule without reimplementing it.
|
||||
/// place: the stack is not the thing that is locked, the board is, and whatever provider a session
|
||||
/// binds — a future one included — must inherit the rule without reimplementing it.
|
||||
public final class BoardUndoManager: UndoManager {
|
||||
|
||||
/// The substrate this manager is a face for. Strong: the session owns both, and the manager is
|
||||
@@ -65,7 +67,10 @@ public final class BoardUndoManager: UndoManager {
|
||||
/// only under locks and on empty stacks — the provider follows the board, so boards without
|
||||
/// app-managed git — repo-nested included — bind 13-native-undo.md's native stack in **every**
|
||||
/// tier" (03-board-ui.md ▸ Toolbar ▸ Catalog). The repo-nested board was the last holder of this
|
||||
/// state and no longer is: that rule was about *git*, and this stack never touches git.
|
||||
/// state and no longer is: that rule was about *git*, and this stack never touches git. (The
|
||||
/// distinction the quote draws collapsed entirely with the 2026-08-08 excision,
|
||||
/// `strategy/01-git-excision.md`: every board is "without app-managed git" now, not just
|
||||
/// repo-nested ones.)
|
||||
///
|
||||
/// The state stays modelled because the seam still admits it — a test binds a substrate-less
|
||||
/// session through `AppModel.makeHistoryProvider` — and because an absent substrate is the honest
|
||||
@@ -73,20 +78,19 @@ public final class BoardUndoManager: UndoManager {
|
||||
/// toolbar pair, and ⌘Z itself go quiet together through the same validation path a lock uses,
|
||||
/// and nothing can accidentally accumulate in a stack that is not there.
|
||||
///
|
||||
/// ### Settable, for exactly one event
|
||||
/// ### Settable, once for exactly one event — now for none
|
||||
///
|
||||
/// **Add-git** (06 ▸ Rules ▸ Detection) is the design's one sanctioned mid-session mode flip:
|
||||
/// "clicking it flips the open board into git mode immediately". 13's header says what that means
|
||||
/// here — the flip **swaps the substrate**: the mode-none board's native stack is discarded and
|
||||
/// the git trail seeded from the root commit, the branch-switch discard-and-reseed precedent. The
|
||||
/// composition root writes the new provider into this property rather than rebuilding this
|
||||
/// object, so AppKit keeps the identical manager it has already been handed by
|
||||
/// `windowWillReturnUndoManager` and simply revalidates over a different stack.
|
||||
/// **Add-git** (06-history-undo.md ▸ Rules ▸ Detection, retired) was the design's one sanctioned
|
||||
/// mid-session mode flip: "clicking it flips the open board into git mode immediately", swapping
|
||||
/// the substrate — the mode-none board's native stack discarded for the git trail seeded from the
|
||||
/// root commit. The composition root wrote the new provider into this property rather than
|
||||
/// rebuilding this object, so AppKit kept the identical manager it had already been handed by
|
||||
/// `windowWillReturnUndoManager` and simply revalidated over a different stack.
|
||||
///
|
||||
/// (This is *not* a tier flip. 12-editions.md's "an open board finishes with the provider it
|
||||
/// composed" is about a subscription lapsing, which cannot change a running session's tier at
|
||||
/// all — `BoardSession.tier` is a `let` with no setter. Mode can change, by explicit command,
|
||||
/// and only in this one direction.)
|
||||
/// App-managed git left the app entirely on 2026-08-08 (`strategy/01-git-excision.md`), and
|
||||
/// add-git went with it: nothing left in the app ever assigns this property after composition. It
|
||||
/// stays a `var` rather than a `let` for the same reason `HistoryProviding` stays a protocol — a
|
||||
/// future substrate swap, mid-session or not, re-binds without re-plumbing this class.
|
||||
var history: (any HistoryProviding)?
|
||||
|
||||
/// Whether the board is refusing writes — `BoardStore.isReadOnly`, read through a closure rather
|
||||
|
||||
@@ -15,10 +15,10 @@ import Foundation
|
||||
///
|
||||
/// **The fine stack**: an ordinary `NativeHistoryProvider`, on *every* board. The steps a card window
|
||||
/// registers are values-based inverses at the Writer boundary — the same shape whatever substrate the
|
||||
/// board's own history has — so a git board's card window still walks its own gestures with the
|
||||
/// native grammar, and only the *coarse* close unit splits by substrate ("one native board step, or
|
||||
/// one commit" — 06-history-undo.md ▸ Undo routing). The split was written as a tier split and is a
|
||||
/// board split since 12-editions.md ▸ PIVOT 2026-08-07; nothing in this type ever read either.
|
||||
/// board's own history has. (Once only the *coarse* close unit split by substrate — "one native board
|
||||
/// step, or one commit" — 13-native-undo.md ▸ Undo routing; the split was written as a tier split and
|
||||
/// became a board split with the 2026-08-07 pivot before the 2026-08-08 excision removed the git half
|
||||
/// of it outright, `strategy/01-git-excision.md`. Nothing in this type ever read either axis.)
|
||||
///
|
||||
/// **The fold**: window close registers "one coarse step ... whose undo restores the card subtree to
|
||||
/// its session-start state ... and whose redo reapplies the net effect". That net effect is exactly
|
||||
|
||||
@@ -80,9 +80,10 @@ public enum HistoryStepOutcome: Equatable, Sendable {
|
||||
/// holds: move → move back ...; rename → restore title" — and both halves of that write are already
|
||||
/// in the caller's hands: the before-value *is* the inverse, and the after-value is what the write
|
||||
/// set (which is also what the staleness predicate compares). A step is therefore that pair, in the
|
||||
/// design's own terms, and deliberately says nothing about how a stack stores it: a gitless board's
|
||||
/// stack is `NSUndoManager`-backed and a Pro git board's is git (12-editions.md ▸ The provider
|
||||
/// seam), and neither substrate appears here.
|
||||
/// design's own terms, and deliberately says nothing about how a stack stores it: the board's stack
|
||||
/// is `NSUndoManager`-backed, and no substrate appears here. (Once two substrates shared this seam —
|
||||
/// a gitless board's native stack, a Pro git board's git history, 12-editions.md ▸ The provider
|
||||
/// seam — before app-managed git left the app entirely, 2026-08-08, `strategy/01-git-excision.md`.)
|
||||
///
|
||||
/// ### `name` is the 06 vocabulary, unprefixed
|
||||
///
|
||||
@@ -262,27 +263,30 @@ public struct HistoryStep {
|
||||
/// seam from a window is the one coarse step its close registers.
|
||||
///
|
||||
/// `AppModel.BoardSession` is where the board half's ownership lives, and the composition root binds
|
||||
/// which implementation it gets — **following the board, and since the 2026-08-07 pivot the board
|
||||
/// alone** (re-ruled 2026-07-31; 12-editions.md ▸ PIVOT 2026-08-07): a board without app-managed git
|
||||
/// — repo-nested included (re-ruled 2026-07-31) — binds `NativeHistoryProvider` (two step stacks over
|
||||
/// the inverses registered at the Writer boundary), and a git-mode board binds the git provider (undo
|
||||
/// as forward restore commits over HEAD's first-parent ancestry — 06-history-undo.md). Both answers
|
||||
/// are the same in every tier; the tier used to decide whether git was on the table at all, and no
|
||||
/// longer does.
|
||||
/// which implementation it gets — **always `NativeHistoryProvider`** now (two step stacks over the
|
||||
/// inverses registered at the Writer boundary), on every board, in every tier
|
||||
/// (`AppModel.makeHistoryProvider`). A git-mode board once bound a git provider instead — undo as
|
||||
/// forward restore commits over HEAD's first-parent ancestry, 06-history-undo.md — between the
|
||||
/// 2026-08-07 pivot (12-editions.md ▸ PIVOT 2026-08-07, when the tier stopped deciding whether git
|
||||
/// was on the table at all) and the 2026-08-08 excision that removed the git provider outright
|
||||
/// (`strategy/01-git-excision.md`).
|
||||
///
|
||||
/// ### What this protocol deliberately does not say
|
||||
///
|
||||
/// - **No `NSUndoManager`, anywhere in the signature.** It is the native provider's implementation
|
||||
/// detail, and a seam that vended one would be a seam only one provider could ever satisfy — the
|
||||
/// opposite of the reason the seam exists at all ("the free tier's native undo is the first proof
|
||||
/// the seam is real", 12 — written when native undo was the free tier's; the proof it names is
|
||||
/// two working substrates, which the pivot leaves standing). AppKit still needs an `UndoManager`
|
||||
/// to hand the responder chain; that adapter is `BoardUndoManager`, which sits *over* this
|
||||
/// protocol rather than inside it.
|
||||
/// - **No persistence promise.** The native stack dies with the session (13); the git provider's
|
||||
/// survives relaunch because git does (06). Both are honest implementations of these seven members.
|
||||
/// the seam is real", 12 — written when native undo was the free tier's; the proof it named was
|
||||
/// two working substrates, which the pivot left standing and the 2026-08-08 excision then narrowed
|
||||
/// back to one, `strategy/01-git-excision.md` — the seam itself is what stays proved either way).
|
||||
/// AppKit still needs an `UndoManager` to hand the responder chain; that adapter is
|
||||
/// `BoardUndoManager`, which sits *over* this protocol rather than inside it.
|
||||
/// - **No persistence promise.** The native stack dies with the session (13); a git provider's once
|
||||
/// survived relaunch because git does, before app-managed git was excised entirely (2026-08-08,
|
||||
/// `strategy/01-git-excision.md`). Both were honest implementations of these seven members; only
|
||||
/// native's promise is live to keep now.
|
||||
/// - **No routing.** Which surface ⌘Z reaches is focus's answer, not the substrate's
|
||||
/// (06 ▸ Undo routing, tier-independent) — `BoardUndoRouting`.
|
||||
/// (13-native-undo.md ▸ Undo routing, tier-independent) — `BoardUndoRouting`.
|
||||
///
|
||||
/// ### One obligation every implementation shares: retire what you let go of
|
||||
///
|
||||
@@ -298,7 +302,8 @@ public struct HistoryStep {
|
||||
/// nonetheless stands to the session's end, because "the skip banner says nothing was applied, and an
|
||||
/// irreversible purge riding that gesture would be surprise loss". A substrate that pops stale steps
|
||||
/// therefore has a place to put them (`NativeHistoryProvider.strandedSteps`); one that never keeps a
|
||||
/// step is untouched by the distinction, which is why the git provider needed no change.
|
||||
/// step is untouched by the distinction — which is why the git provider, while it existed, needed no
|
||||
/// change for this rule at all.
|
||||
///
|
||||
/// ### And its mirror: say what you are still holding
|
||||
///
|
||||
|
||||
@@ -2,10 +2,10 @@ import Foundation
|
||||
|
||||
// MARK: - NativeHistoryProvider
|
||||
|
||||
/// The undo substrate of every board without app-managed git: one stack per board session
|
||||
/// (13-native-undo.md, whose header lost its tier axis with the 2026-08-07 pivot — the substrate is
|
||||
/// the board's mode alone, and git is opt-in per board, so a board nobody opted in keeps this stack
|
||||
/// for good) — **and the substrate of every open card window's stack, on any board** (re-ruled 2026-07-31, the
|
||||
/// The undo substrate of every board: one stack per board session (13-native-undo.md, whose header
|
||||
/// lost its tier axis with the 2026-08-07 pivot and then its mode axis with the 2026-08-08 git
|
||||
/// excision, `strategy/01-git-excision.md` — there is no other substrate left to be the board's
|
||||
/// alternative to) — **and the substrate of every open card window's stack, on any board** (re-ruled 2026-07-31, the
|
||||
/// session-coarsening model): a window's fine-grained gestures are values-based inverses whatever the
|
||||
/// board's own substrate is, so `CardWindowUndo` holds one of these too. Nothing below knows which of
|
||||
/// the two it is; both need the same four-line grammar.
|
||||
@@ -147,7 +147,8 @@ public final class NativeHistoryProvider: HistoryProviding {
|
||||
|
||||
public func redo() { cross(.redo) }
|
||||
|
||||
/// Session teardown, the add-git substrate swap, a branch reseed — every step goes, so every step
|
||||
/// Session teardown — once the add-git substrate swap and a branch reseed too, both gone with
|
||||
/// app-managed git (`strategy/01-git-excision.md`, 2026-08-08) — every step goes, so every step
|
||||
/// retires: "the purge runs when the coarse step leaves the board stack ... or the board session
|
||||
/// ends" (13 ▸ Interaction with the trash).
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user