The tier axis comes out of the git stack — compose unconditional, postures mode-driven

HistoryStore.compose(boardRoot📒) returns non-optional and runs for
every session — the nil the gate produced was the only nil it ever had.
makeHistoryProvider is a one-axis decision: git-mode boards bind the git
provider, everything else native, in every tier; Session.tier stays
recorded, dormant. BoardGitSection shrinks to the four mode postures
(.absent and .proPointer die, BoardGitNote and the .git probe with them);
every board carries all three popover tabs (BoardInfoTab.available
retired); the titlebar branch shows on any git-mode board; the settings
sheet and card History section stop reading tier. InertGitTests is
repurposed as UntouchedGitTests — the file layer still never opens .git,
now load-bearing for mode-none boards. The accessibility audit reaches the
settings sheet at last: the fixture board hosts it in every tier, so the
free-fixture disabled-row test becomes an open-and-audit test.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-08-07 20:21:28 -04:00
parent da0d7fd2d7
commit 798a8bac73
32 changed files with 830 additions and 904 deletions
+16 -12
View File
@@ -32,7 +32,7 @@ private func makeBoard() throws -> WriterFixture {
private func makeGitBoard() async throws -> (fixture: WriterFixture, git: HistoryStore, ledger: EchoLedger) {
let fixture = try makeBoard()
let ledger = EchoLedger()
let git = try #require(HistoryStore.compose(boardRoot: fixture.root, tier: .pro, ledger: ledger))
let git = HistoryStore.compose(boardRoot: fixture.root, ledger: ledger)
#expect(await git.addGit())
return (fixture, git, ledger)
}
@@ -805,7 +805,7 @@ struct AutoCommitContentionTests {
encoding: .utf8
)
let git = try #require(HistoryStore.compose(boardRoot: fixture.root, tier: .pro))
let git = HistoryStore.compose(boardRoot: fixture.root)
#expect(git.mode == .git)
let committer = try quickCommitter(git)
@@ -902,29 +902,33 @@ struct FlushBeforeOverwriteTests {
}
}
// MARK: - Composition and the tier gate
// MARK: - Composition
@MainActor
@Suite("Auto-commit ▸ composition")
struct AutoCommitCompositionTests {
@Test("The free tier composes no committer, because it composes no git state at all")
func theFreeTierHasNoCommitter() throws {
@Test("A board carrying a `.git` composes a committer — there is no tier to compose under")
func aGitBearingBoardComposesACommitter() throws {
let fixture = try makeBoard()
defer { fixture.tearDown() }
try fixture.file(".git/HEAD", Data("ref: refs/heads/main\n".utf8))
// The tier gate is one level up: no `HistoryStore` means no committer, nothing to disable,
// and no path by which a free-tier session could touch `.git` (12-editions.md).
#expect(HistoryStore.compose(boardRoot: fixture.root, tier: .free) == nil)
// **PIVOT 2026-08-07** (12-editions.md git left the paywall). This test used to assert the
// gate one level up: `compose(tier: .free)` was `nil`, so there was no committer, nothing to
// disable and no path by which a free session could touch `.git`. What decides now is the
// board's own mode, and this board has a `.git` at its root.
let git = HistoryStore.compose(boardRoot: fixture.root)
#expect(git.mode == .git)
#expect(git.committer != nil, "the committer's existence is exactly mode == .git")
}
@Test("A Pro board without a repository has no committer either")
@Test("A board without a repository has no committer")
func modeNoneHasNoCommitter() throws {
let fixture = try makeBoard()
defer { fixture.tearDown() }
let git = try #require(HistoryStore.compose(boardRoot: fixture.root, tier: .pro))
let git = HistoryStore.compose(boardRoot: fixture.root)
#expect(git.mode == .none)
#expect(git.committer == nil, "the committer's existence is exactly mode == .git")
}
@@ -934,7 +938,7 @@ struct AutoCommitCompositionTests {
let fixture = try makeBoard()
defer { fixture.tearDown() }
let git = try #require(HistoryStore.compose(boardRoot: fixture.root, tier: .pro))
let git = HistoryStore.compose(boardRoot: fixture.root)
var wired = 0
git.activateAutoCommit { _ in wired += 1 }
#expect(git.committer == nil)
@@ -949,7 +953,7 @@ struct AutoCommitCompositionTests {
let (fixture, _, _) = try await makeGitBoard()
defer { fixture.tearDown() }
let reopened = try #require(HistoryStore.compose(boardRoot: fixture.root, tier: .pro))
let reopened = HistoryStore.compose(boardRoot: fixture.root)
let committer = try #require(reopened.committer)
// Composition happens on the board-open path, where arming a debounce would be a side effect
// of *detection*. `activateAutoCommit` is what starts it.