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:
2026-08-08 12:31:27 -04:00
parent 8cf1457814
commit 445d035a83
50 changed files with 397 additions and 313 deletions
+1 -1
View File
@@ -245,7 +245,7 @@ struct AgentGuideStoreTests {
}
/// "Left untouched when current or newer" and untouched means the file is not even opened for
/// writing, so its mtime (and, on git boards, the tree) is undisturbed.
/// writing, so its mtime is undisturbed.
@Test("A current guide is left byte-for-byte alone, mtime included")
func currentGuideIsNotRewritten() throws {
let fixture = try makeBoard()
+1 -1
View File
@@ -81,7 +81,7 @@ struct CardBodyWriteGateTests {
#expect(!session.isDirty)
// The pending save was cancelled by the revert rather than firing on a no-op a write that
// landed here would stamp `modified` and mint a commit for nothing.
// landed here would stamp `modified` for nothing.
await waitUntil { spy.count > 0 }
#expect(spy.count == 0)
#expect(session.flush() == .unchanged)
+2 -1
View File
@@ -15,7 +15,8 @@ import Testing
/// sheet its own flag here, because the branch-name and commit-identity fields had moved onto it; the
/// reversal retired that sheet and brought those fields back into the popover's Git tab, so the
/// popover's own open-at-all clause covers every configuration field again and the third disjunct
/// went with the surface it described.
/// went with the surface it described. (The Git tab itself retired in turn, 2026-08-08 with the git
/// excision, `strategy/01-git-excision.md`; the two-flag shape this file tests did not change again.)
@MainActor
@Suite("caretChordsYield ▸ the caret-chords rule")
struct CaretChordTests {
+4 -3
View File
@@ -110,7 +110,7 @@ private func compose(
snapshot: try after.snapshot(),
previousSnapshot: try before.snapshot(),
agentGuideText: guideText,
// Resolved the way a flush resolves it off the "after" tree, through the committer's own
// Resolved the way a flush resolves it off the "after" tree, through the narrator's own
// reader rather than hand-assembled, for the same reason both snapshots are loaded rather
// than built: a map the flush could never produce would prove nothing about the flush.
commentTimestamps: ChangeNarrator.commentTimestamps(for: paths, boardRoot: after.root)
@@ -834,8 +834,9 @@ struct CommitMessageCommentTests {
Data("---\nschema: 1\nkind: comment\nauthor: Ada\ncreated: \(created)\n---\n\(body)\n".utf8)
}
/// **"A commit's comment bullets sort chronologically never UUID-arbitrary"** (06 Rules
/// Auto-commit, blessed 2026-07-31).
/// **"A commit's comment bullets sort chronologically never UUID-arbitrary"** once
/// 06-history-undo.md Rules Auto-commit's rule (blessed 2026-07-31), now the change
/// narrator's (`strategy/01-git-excision.md` What is harvested).
///
/// Three comments on one card, each with a *different verb* so the bullets are distinguishable,
/// and identities deliberately ordered against their chronology: the earliest comment carries the
+2 -2
View File
@@ -4,7 +4,7 @@ import Testing
/// The close flush is an *order*, and an order is only worth stating if something checks it. 02
/// § Windows fixes it: the board's card windows and their sessions first, then pending debounced work
/// with editor saves before the pending auto-commit, then the registry stamp, then teardown and
/// with editor saves before the board-level flush, then the registry stamp, then teardown and
/// "nothing about this is conditional".
///
/// So every test here is an assertion about a list. The coordinator is driven with fakes that append
@@ -110,7 +110,7 @@ struct CloseFlushCoordinatorTests {
"card-session work/card-b",
"dismiss work/card-a",
"dismiss work/card-b",
// Pending debounced work, editor saves before the pending auto-commit.
// Pending debounced work, editor saves before the board-level flush.
"store-flush work",
"editor-flush work",
"committer-flush work",
+6 -2
View File
@@ -142,7 +142,9 @@ struct DedupeRuleTests {
#expect(verdict.duplicates.map(\.path) == ["\(Ident.lane2)/\(Dup.lower)"])
}
/// Rung one the git seam. Base never fills it; pro-m1 does.
/// Rung one once the git seam, which pro-m1 filled and base never did; nothing fills it now
/// that app-managed git is excised (`strategy/01-git-excision.md`, 2026-08-08), and the ladder
/// falls through to birth date on every board.
@Test("Git path history beats birth date")
func historyBeatsBirthDate() {
let verdict = IntegrityRules.dedupe([
@@ -812,7 +814,9 @@ struct DuplicateIdentityRemintTests {
}
/// A rename is a move as far as provenance goes, and **heal-marked**, because the app started it
/// on its own the receipt is what splits it into its own commit on git boards.
/// on its own once the receipt that split it into its own commit on git boards; app-managed
/// git is gone now (`strategy/01-git-excision.md`), but the receipt still exists for whatever
/// consumes it next.
@Test("It drops a heal-marked move receipt")
func dropsAHealMarkedReceipt() throws {
let (fixture, duplicate) = try board()
+2 -1
View File
@@ -328,7 +328,8 @@ struct FolderWatcherTests {
defer { watcher.stop() }
await drainStartupChurn(log)
// What the app's own auto-commit produces, and what an external `git gc` produces: pure
// What an external `git commit` or `git gc` produces once what the app's own auto-commit
// produced too, before it was excised (`strategy/01-git-excision.md`, 2026-08-08): pure
// history churn that cannot alter the rendered tree.
fixture.write(".git/index", "fake index")
fixture.write(".git/objects/ab/cdef", "fake object")
+3 -2
View File
@@ -129,7 +129,8 @@ struct WriteBoardImageTests {
}
/// The receipt: without one the churn the write produces classifies as somebody else's, and the
/// auto-committer would name the commit for a foreign edit.
/// announcer or any future ledger consumer would attribute the app's own write to a foreign
/// editor.
@Test("The write leaves a content receipt in the ledger")
func dropsAReceipt() throws {
let fixture = try makeBoard()
@@ -170,7 +171,7 @@ struct GeneratedBackgroundWriteTests {
}
/// One gesture, one bracket the style batch's rule, which is what makes one reroll one
/// app-mediated reload and one commit on a git board, though it writes two files.
/// app-mediated reload, though it writes two files.
@Test("Two files, one bracket")
func oneBracketForBothFiles() throws {
let fixture = try makeBoard()
+22 -12
View File
@@ -9,22 +9,32 @@ import Testing
///
/// It was written as the **inert-`.git` posture**'s byte-level proof (12-editions.md The free tier
/// and `.git`, as it then read: "any `.git` is inert the app never reads history, never commits,
/// never touches `.git` in any way"). **PIVOT 2026-08-07** retired that posture outright git left
/// the paywall, so a `.git` at a board root is *live* in every tier, detection runs at every open,
/// and the auto-committer writes into exactly the directory this file's fixture plants.
/// never touches `.git` in any way"). **PIVOT 2026-08-07** retired that posture, if only for a day
/// git left the paywall, so a `.git` at a board root became *live* in every tier, detection ran at
/// every open, and an auto-committer wrote into exactly the directory this file's fixture plants.
///
/// The tests survive the retirement unchanged because none of them ever composed a `HistoryStore` or
/// asserted anything about a tier: they drive `BoardWriter` and `BoardLoader` directly, and what they
/// pin is that **those layers are git-agnostic**. That claim is not only still true, it is now
/// load-bearing in two places the retired posture never reached:
/// **EXCISION 2026-08-08** (`strategy/01-git-excision.md`) retired the pivot in turn: app-managed
/// git is gone outright. There is no mode detection, no auto-committer, nothing left anywhere under
/// `Kanban/` that opens `.git` for writing, on any board, in any tier. What the inert posture claimed
/// once, and the pivot scoped down to a single tier for a single day, is again the app's whole git
/// story this time app-wide rather than tier-gated, and for good rather than pending a paywall
/// decision. This suite is that restored claim's proof.
///
/// - **A board nobody added git to** (mode `none`) is the one board the app manages no git for, for
/// good git stays opt-in per board (06 Rules; 13-native-undo.md's header read through the
/// pivot). Nothing but this layer runs on such a board, so this layer's indifference *is* the
/// whole promise.
/// The tests survived both transitions unchanged because none of them ever composed a `HistoryStore`
/// or asserted anything about a tier: they drive `BoardWriter` and `BoardLoader` directly, and what
/// they pin is that **those layers are git-agnostic**. That claim was true before the pivot, stayed
/// true underneath the pivot's live machinery, and is now again the whole of the app's git
/// story, load-bearing in two places a tier-gated posture never could have reached uniformly:
///
/// - **Every board** is a board the app manages no git for. Before the pivot that was true because
/// nothing read `.git` at all; during the pivot it was true only for boards nobody had added git
/// to (mode `none`); since the excision it is true unconditionally, because there is no mode left
/// to be in. Nothing but this layer runs on any board, so this layer's indifference *is* the whole
/// promise.
/// - **A `.git` the app does not manage** a repo-nested board's ancestor, a clone dropped inside a
/// card folder (which the fixture below plants deliberately) is left strictly alone by the same
/// indifference, whatever the board's own mode or the user's tier.
/// indifference, unconditionally now rather than "whatever the board's own mode or the user's
/// tier."
///
/// Two neighbouring claims are pinned elsewhere and are referenced, not repeated: `FolderWatcherTests`
/// ".git filtering" proves the watcher ignores churn under a `.git` at any depth, and
+6 -3
View File
@@ -7,9 +7,12 @@ import Testing
///
/// `GitignoreRules` is pure text in, verdicts out so this suite is a table and nothing else: no
/// filesystem, no board, no store. What it pins is that the app's own matcher is **git's**, because
/// the ruling's whole premise is one definition of noise shared with the committer: "on Pro boards
/// the same file governs the committer, so ignored noise neither relocates nor commits". A matcher
/// that read the file differently from libgit2 would make that one sentence two behaviours.
/// `gitignore(5)` is the format's own semantics and a file a user may also be handing to their own
/// git a matcher that read it differently from git itself would make the file lie to one reader or
/// the other. (Once doubly true while a committer shared this definition too "on Pro boards the
/// same file governs the committer, so ignored noise neither relocates nor commits" before
/// app-managed git was excised entirely, `strategy/01-git-excision.md`, 2026-08-08; libgit2 is gone,
/// but the format's own semantics remain the standard regardless.)
///
/// The claims are grouped as `gitignore(5)` states them, in its order, plus the seed the app writes
/// and the three deliberate divergences the type documents.
+8 -5
View File
@@ -284,8 +284,9 @@ struct NativeHistoryProviderTests {
/// A provider with no `NSUndoManager` anywhere in it which is the point: `BoardUndoManager` is
/// tested against *this* rather than against the native stack, because what has to be true is that
/// the adapter works for any implementation of the seam (Pro's git provider binds the same protocol
/// in pro-m1).
/// the adapter works for any implementation of the seam (Pro's git provider once bound the same
/// protocol, in pro-m1, before app-managed git was excised entirely,
/// `strategy/01-git-excision.md`).
@MainActor
private final class FakeHistoryProvider: HistoryProviding {
@@ -637,9 +638,11 @@ struct BoardSessionHistoryTests {
// The subscription lapses mid-session the one thing 12 The entitlement says must not
// disturb a board that is already on screen: "an open board finishes with the provider it
// composed". (Its second clause "the next open composes the native stack over inert
// `.git`" went with PIVOT 2026-08-07: the next open composes the git stack too. What is
// pinned here is the recorded fact's immutability, which is what makes the first clause
// true whatever a future split gates.)
// `.git`" went with PIVOT 2026-08-07, when for one day the next open could compose a git
// stack instead; the 2026-08-08 excision (`strategy/01-git-excision.md`) then restored the
// original clause outright every open composes the native stack over inert `.git` again,
// permanently. What is pinned here is the recorded fact's immutability, which is what makes
// the first clause true whatever a future split gates.)
model.currentTier = { .free }
#expect(try #require(model.session(for: ref)).tier == .pro)
+5 -5
View File
@@ -190,8 +190,8 @@ struct InlineRenameWriteTests {
let titled = try fixture.indexData("\(Ident.lane1)/\(Ident.card1)")
let untouched = try fixture.indexData("\(Ident.lane2)/\(Ident.card3)")
// An editor opened and dismissed with Return must not stamp `modified` or (on a git board)
// mint a commit the lane-resize rule, for the same reason.
// An editor opened and dismissed with Return must not stamp `modified` the lane-resize
// rule, for the same reason.
store.transient.beginRename(of: card1, currentTitle: "First")
store.commitRename()
#expect(try fixture.indexData("\(Ident.lane1)/\(Ident.card1)") == titled)
@@ -549,7 +549,7 @@ struct LaneReorderWriteTests {
let before = try fixture.indexData(Ident.lane1)
// Index 0 is where lane1 already sits, counted with itself removed the release must not
// stamp `modified` or mint a commit.
// stamp `modified`.
store.moveLane(lane1, toIndex: 0)
#expect(try fixture.indexData(Ident.lane1) == before)
@@ -806,8 +806,8 @@ struct BoardRenameWriteTests {
let store = try BoardStore(rootURL: fixture.root)
let titled = try fixture.indexData("")
// A popover opened and dismissed with Return must not stamp `modified` or (on a git board)
// mint a commit `commitRename`'s rule, for its reason.
// A popover opened and dismissed with Return must not stamp `modified` `commitRename`'s
// rule, for its reason.
store.renameBoard("Roadmap")
#expect(try fixture.indexData("") == titled)
+1 -1
View File
@@ -152,7 +152,7 @@ struct LaneWidthWriteTests {
let atOne = try fixture.indexData(Ident.lane1)
// A drag that ends where it started, and a stepper pressed against its floor: neither may
// stamp `modified` or (on a git board) mint a commit.
// stamp `modified`.
store.setLaneWidth(ItemID(rawValue: Ident.lane2), units: 3)
#expect(try fixture.indexData(Ident.lane2) == atThree)
+1 -1
View File
@@ -559,7 +559,7 @@ struct LooseFileStoreTests {
#expect(!fixture.exists("\(cardPath)/notes.txt"))
#expect(store.banners.losses.map(\.message) == ["Moved 'notes.txt' into attachments — 'Fix login'"])
#expect(store.banners.oneShots.isEmpty)
// One gesture, one bracket one app-mediated reload and, on git boards, one commit.
// One gesture, one bracket one app-mediated reload.
#expect(brackets.begins == 1)
}
+4 -4
View File
@@ -202,8 +202,8 @@ struct StyleWriteTests {
let log = BracketLog()
log.attach(to: store)
// "Choosing a well applies to the whole selection one gesture, one commit on git boards"
// (03 § Styling Controls): the churn has to round back as ONE app-mediated reload.
// "Choosing a well applies to the whole selection one gesture, one write" (03 § Styling
// Controls): the churn has to round back as ONE app-mediated reload.
store.applyStyle(to: .items([card1, card2]), background: .set("light-cayenne"))
#expect(log.begins == 1)
@@ -222,8 +222,8 @@ struct StyleWriteTests {
let untouchedCard = try fixture.indexData("\(Ident.lane1)/\(Ident.card1)")
// `card1` is already `{color: fern}` and `card2` has no background at all: only the second file may
// move. A well clicked twice must not stamp `modified` or mint a commit on what was already
// right (`setLaneWidth`'s rule).
// move. A well clicked twice must not stamp `modified` on what was already right
// (`setLaneWidth`'s rule).
store.applyStyle(to: .items([card1, card2]), background: .set("fern"))
#expect(try fixture.indexData("\(Ident.lane1)/\(Ident.card1)") == untouchedCard)