The prose catches up with the one-version app — tier qualifiers retired, the entitlement comment carried over
The excision's follow-up sweep: present-tense prose that still implied an edition axis now reads
correctly under one version ("in every tier" clauses dropped or turned substrate-shaped, the
announcer's "every free-tier bracket today" is "every bracket today" — nothing passes a phrase),
and forward-looking promises pinned to the mooted pro-m1/pro-m2 milestones now name the thing
itself (the change narrator in Kanban/Changes/, the foreign-change journal successor) or fall to
past tense. Kanban.entitlements' network-client comment sheds its "dormant until Pro ships"
framing for the pivot's own reasoning: the key stays because the sync capability to come needs
it regardless. Untouched on purpose: the storage layer's coerce/tolerate/refuse tiers, the
chooser's bundled/user/keyless tiers, verbatim design-doc quotations, and genuine past-tense
record. One dangling reference repaired en route: EndToEndVerification.md cited the long-renamed
InertGitTests. Comment-only throughout; 2,686 unit tests green, unchanged.
Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -277,8 +277,9 @@ enum ChangeNarrator {
|
|||||||
///
|
///
|
||||||
/// `BoardDiff.LaneEntry`'s twin rather than a shared type, deliberately: the announcer asks three
|
/// `BoardDiff.LaneEntry`'s twin rather than a shared type, deliberately: the announcer asks three
|
||||||
/// questions of a lane and this asks a dozen, at a granularity (which *field* changed) the digest
|
/// questions of a lane and this asks a dozen, at a granularity (which *field* changed) the digest
|
||||||
/// is explicitly built not to keep — see `BoardDiff`'s own note, which says pro-m1's composer
|
/// is explicitly built not to keep — see `BoardDiff`'s own note, which says a future message
|
||||||
/// "will phrase the very same counts differently without either of them knowing about the other".
|
/// composer "will phrase the very same counts differently without either of them knowing about
|
||||||
|
/// the other".
|
||||||
private struct LaneEntry {
|
private struct LaneEntry {
|
||||||
let container: ItemContainer
|
let container: ItemContainer
|
||||||
let title: FieldValue<String>
|
let title: FieldValue<String>
|
||||||
|
|||||||
@@ -154,8 +154,8 @@ public final class BoardUndoManager: UndoManager {
|
|||||||
// MARK: - BoardUndoRouting
|
// MARK: - BoardUndoRouting
|
||||||
|
|
||||||
/// Which undo a hosted window answers with, given what holds the keyboard — 06-history-undo.md
|
/// Which undo a hosted window answers with, given what holds the keyboard — 06-history-undo.md
|
||||||
/// ▸ Undo routing, which 12 records as **tier-independent**: "focus decides text-undo vs
|
/// ▸ Undo routing, which 12 records as a fact about *focus* and never about the substrate: "focus
|
||||||
/// board-undo; only the substrate behind board-undo differs".
|
/// decides text-undo vs board-undo; only the substrate behind board-undo differs".
|
||||||
///
|
///
|
||||||
/// ### Most of the rule is AppKit's, and is not here
|
/// ### Most of the rule is AppKit's, and is not here
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -258,13 +258,13 @@ public struct HistoryStep {
|
|||||||
/// "Two levels: one stack per board, one per open card window" (13-native-undo.md ▸ Rules, re-ruled
|
/// "Two levels: one stack per board, one per open card window" (13-native-undo.md ▸ Rules, re-ruled
|
||||||
/// 2026-07-31). The **board** stack is the session's, shared by board surfaces, and its
|
/// 2026-07-31). The **board** stack is the session's, shared by board surfaces, and its
|
||||||
/// implementation is what this protocol is a seam for. A **card window** owns a second stack for its
|
/// implementation is what this protocol is a seam for. A **card window** owns a second stack for its
|
||||||
/// own gestures — always a `NativeHistoryProvider`, in either tier, because a window's fine-grained
|
/// own gestures — always a `NativeHistoryProvider`, because a window's fine-grained inverses are
|
||||||
/// inverses are values-based whatever the board's substrate is (`CardWindowUndo`); what reaches this
|
/// values-based whatever the board's substrate is (`CardWindowUndo`); what reaches this
|
||||||
/// seam from a window is the one coarse step its close registers.
|
/// 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
|
/// `AppModel.BoardSession` is where the board half's ownership lives, and the composition root binds
|
||||||
/// which implementation it gets — **always `NativeHistoryProvider`** now (two step stacks over the
|
/// which implementation it gets — **always `NativeHistoryProvider`** now (two step stacks over the
|
||||||
/// inverses registered at the Writer boundary), on every board, in every tier
|
/// inverses registered at the Writer boundary), on every board there is
|
||||||
/// (`AppModel.makeHistoryProvider`). A git-mode board once bound a git provider instead — undo as
|
/// (`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
|
/// 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
|
/// 2026-08-07 pivot (12-editions.md ▸ PIVOT 2026-08-07, when the tier stopped deciding whether git
|
||||||
@@ -286,7 +286,7 @@ public struct HistoryStep {
|
|||||||
/// `strategy/01-git-excision.md`). Both were honest implementations of these seven members; only
|
/// `strategy/01-git-excision.md`). Both were honest implementations of these seven members; only
|
||||||
/// native's promise is live to keep now.
|
/// native's promise is live to keep now.
|
||||||
/// - **No routing.** Which surface ⌘Z reaches is focus's answer, not the substrate's
|
/// - **No routing.** Which surface ⌘Z reaches is focus's answer, not the substrate's
|
||||||
/// (13-native-undo.md ▸ Undo routing, tier-independent) — `BoardUndoRouting`.
|
/// (13-native-undo.md ▸ Undo routing) — `BoardUndoRouting`.
|
||||||
///
|
///
|
||||||
/// ### One obligation every implementation shares: retire what you let go of
|
/// ### One obligation every implementation shares: retire what you let go of
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -8,14 +8,14 @@
|
|||||||
<true/>
|
<true/>
|
||||||
<key>com.apple.security.files.bookmarks.app-scope</key>
|
<key>com.apple.security.files.bookmarks.app-scope</key>
|
||||||
<true/>
|
<true/>
|
||||||
<!-- Remotes: pull/push and auth (07-sync-collab.md), which only the git provider ever makes.
|
<!-- **Declared now and exercised by nothing.** Nothing in the shipped app opens a socket: the
|
||||||
**Declared now, dormant until Pro ships** — the one-app collapse traded "the free app
|
git provider that would have used this key is excised (strategy/01-git-excision.md), and
|
||||||
provably has no network access" for "no network use until you subscribe", with eyes open
|
the ops-based sync capability that will is a design pass still to come. The key stays
|
||||||
(12-editions.md ▸ The target, re-ruled 2026-07-30). It sits in the shipped entitlements
|
because that capability needs it regardless (12-editions.md ▸ PIVOT 2026-08-08), and
|
||||||
from the start because a key added later is a new provisioning profile and a new review
|
because a key added later is a new provisioning profile and a new review surface — cheaper
|
||||||
surface for what is meant to be a StoreKit-only change. No `keychain-access-groups`
|
to carry from the start than to introduce. No `keychain-access-groups` alongside it: a
|
||||||
alongside it: a group shares items *between* apps, and there is one app — Pro's
|
group shares items *between* apps, and there is one app, so whatever credentials sync
|
||||||
credentials go to the sandbox's own keychain, which needs no key at all. -->
|
eventually holds go to the sandbox's own keychain, which needs no key at all. -->
|
||||||
<key>com.apple.security.network.client</key>
|
<key>com.apple.security.network.client</key>
|
||||||
<true/>
|
<true/>
|
||||||
<!-- No `com.apple.security.application-groups`: a group exists to share a container *between*
|
<!-- No `com.apple.security.application-groups`: a group exists to share a container *between*
|
||||||
|
|||||||
@@ -218,8 +218,9 @@ public enum BoardAnnouncer {
|
|||||||
public var endsBracketedOperation = false
|
public var endsBracketedOperation = false
|
||||||
|
|
||||||
/// What that operation wants said when it lands — "Pulled 3 commits", "Switched to branch
|
/// What that operation wants said when it lands — "Pulled 3 commits", "Switched to branch
|
||||||
/// 'redesign'". `nil` for a bracket whose completion is not worth speech, which is every
|
/// 'redesign'", both git's. `nil` for a bracket whose completion is not worth speech, which
|
||||||
/// free-tier bracket today (see `BoardStore.performWholesale(announcing:_:)`).
|
/// is **every bracket today**: nothing in the app passes a phrase, and the parameter is the
|
||||||
|
/// seam a future wholesale operation fills (see `BoardStore.performWholesale(announcing:_:)`).
|
||||||
public var completion: String?
|
public var completion: String?
|
||||||
|
|
||||||
/// The snapshot comparison **already narrowed to the foreign-classified changes**
|
/// The snapshot comparison **already narrowed to the foreign-classified changes**
|
||||||
|
|||||||
@@ -2416,7 +2416,7 @@ public final class BoardStore: HealHost {
|
|||||||
/// would walk backwards through the user's keystrokes in seven-hundred-millisecond slices rather
|
/// would walk backwards through the user's keystrokes in seven-hundred-millisecond slices rather
|
||||||
/// than undoing the edit they made. So `CardBodyEditSession` remembers the bytes disk held when
|
/// than undoing the edit they made. So `CardBodyEditSession` remembers the bytes disk held when
|
||||||
/// the session's first save landed, and calls this once at the flip with that pair — the same
|
/// the session's first save landed, and calls this once at the flip with that pair — the same
|
||||||
/// boundary pro-m1's auto-committer coalesces on, for the same reason.
|
/// boundary 06's auto-committer coalesced on, for the same reason.
|
||||||
///
|
///
|
||||||
/// ### The bytes are the whole state
|
/// ### The bytes are the whole state
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ public struct HarvestedReceipt: Sendable, Equatable {
|
|||||||
// MARK: - EchoLedger
|
// MARK: - EchoLedger
|
||||||
|
|
||||||
/// **What the app wrote, so a landing reload can tell its own echo from someone else's edit** —
|
/// **What the app wrote, so a landing reload can tell its own echo from someone else's edit** —
|
||||||
/// 02-architecture.md ▸ Components ▸ EchoLedger, built here in base ahead of Pro's committer
|
/// 02-architecture.md ▸ Components ▸ EchoLedger, built in the store layer ahead of any consumer
|
||||||
/// (ruled 2026-07-29).
|
/// that would record what it says (ruled 2026-07-29).
|
||||||
///
|
///
|
||||||
/// ### The one thing it is not
|
/// ### The one thing it is not
|
||||||
///
|
///
|
||||||
@@ -153,10 +153,11 @@ public final class EchoLedger: Sendable {
|
|||||||
/// messages, ruled 2026-07-29: "the Writer's heal operations drop heal-marked receipts in
|
/// messages, ruled 2026-07-29: "the Writer's heal operations drop heal-marked receipts in
|
||||||
/// the EchoLedger — attribution machinery like the author split, never message tagging").
|
/// the EchoLedger — attribution machinery like the author split, never message tagging").
|
||||||
///
|
///
|
||||||
/// **Inert in base beyond the ledger itself.** Nothing here reads it and nothing renders it;
|
/// **Inert beyond the ledger itself.** Nothing here reads it and nothing renders it; it is
|
||||||
/// it is the flag pro-m1's committer reads to split a heal's paths into their own commit,
|
/// the flag whatever records history next — the foreign-change journal
|
||||||
/// and it is stored rather than derived because by commit time the only thing that still
|
/// (`strategy/01-git-excision.md` ▸ Successors) is the candidate — would read to split a
|
||||||
/// knows a path was healed is the receipt.
|
/// heal's paths out from a user's own, and it is stored rather than derived because by the
|
||||||
|
/// time anything asks, the only thing that still knows a path was healed is the receipt.
|
||||||
var isHeal: Bool = false
|
var isHeal: Bool = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -584,7 +585,8 @@ extension EchoLedger {
|
|||||||
/// handed — it is something the two snapshots identify. An item whose folder, whose `index.md`
|
/// handed — it is something the two snapshots identify. An item whose folder, whose `index.md`
|
||||||
/// bytes, or whose attachment listing differs between them is precisely a changed file (or set
|
/// bytes, or whose attachment listing differs between them is precisely a changed file (or set
|
||||||
/// of them) that this reload observed, and the item is the unit every consumer downstream — the
|
/// of them) that this reload observed, and the item is the unit every consumer downstream — the
|
||||||
/// digest, the vanishing-focus sentence, pro-m1's commit message — actually speaks in.
|
/// digest, the vanishing-focus sentence, the change narrator's message (`Kanban/Changes/`) —
|
||||||
|
/// actually speaks in.
|
||||||
///
|
///
|
||||||
/// The bytes come from the snapshot rather than from a second read of the disk:
|
/// The bytes come from the snapshot rather than from a second read of the disk:
|
||||||
/// `FrontmatterDocument` owns a byte-identical round trip, so `document.serialized()` **is** the
|
/// `FrontmatterDocument` owns a byte-identical round trip, so `document.serialized()` **is** the
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ extension BoardWriter {
|
|||||||
|
|
||||||
/// **The post's inverse: the posted folder renamed back to `.draft`** (13-native-undo.md, the
|
/// **The post's inverse: the posted folder renamed back to `.draft`** (13-native-undo.md, the
|
||||||
/// comment clause: "post-undo naturally rides the same rail" — the move-based inverse family,
|
/// comment clause: "post-undo naturally rides the same rail" — the move-based inverse family,
|
||||||
/// with no byte capture in any tier).
|
/// with no byte capture anywhere in it).
|
||||||
///
|
///
|
||||||
/// **A rename and nothing else, so it stamps nothing.** The folder keeps its parent, so no
|
/// **A rename and nothing else, so it stamps nothing.** The folder keeps its parent, so no
|
||||||
/// container changed, and the existing write discipline answers without a rule of its own: "a
|
/// container changed, and the existing write discipline answers without a rule of its own: "a
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ import Foundation
|
|||||||
/// consumer left.) This type is the app's half of that — once split from libgit2 because the load
|
/// consumer left.) This type is the app's half of that — once split from libgit2 because the load
|
||||||
/// path couldn't reach for it while the committer did; libgit2 left the project entirely with the
|
/// path couldn't reach for it while the committer did; libgit2 left the project entirely with the
|
||||||
/// git excision (`strategy/01-git-excision.md`, step 6), so today there is no other implementation to
|
/// git excision (`strategy/01-git-excision.md`, step 6), so today there is no other implementation to
|
||||||
/// duplicate at all. The free tier always opened a board without so much as a `fileExists` under
|
/// duplicate at all. The posture 12 wrote as the free tier's is simply the app's now (12-editions.md
|
||||||
/// `.git` (12-editions.md ▸ The free tier and `.git`), and the heal runs on boards that have no
|
/// ▸ The free tier and `.git`; ▸ PIVOT 2026-08-08): a board opens without so much as a `fileExists`
|
||||||
/// repository at all.
|
/// under `.git`, and the heal runs on boards that have no repository at all.
|
||||||
///
|
///
|
||||||
/// **Pure: no I/O, no filesystem, no `URL`.** It parses text and answers about paths. Reading the
|
/// **Pure: no I/O, no filesystem, no `URL`.** It parses text and answers about paths. Reading the
|
||||||
/// bytes is `BoardLoader.ignoreRules(atBoardRoot:)`' job, once per walk — the loader stays a pure
|
/// bytes is `BoardLoader.ignoreRules(atBoardRoot:)`' job, once per walk — the loader stays a pure
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ extension NSToolbarItem.Identifier {
|
|||||||
/// predicate written here — it is literally the same validation. Both reach the board window, whose
|
/// predicate written here — it is literally the same validation. Both reach the board window, whose
|
||||||
/// `windowWillReturnUndoManager` hands back the session's `BoardUndoManager`, and both therefore
|
/// `windowWillReturnUndoManager` hands back the session's `BoardUndoManager`, and both therefore
|
||||||
/// enable exactly when that board has a step to cross and no read-only lock stands
|
/// enable exactly when that board has a step to cross and no read-only lock stands
|
||||||
/// (13-native-undo.md ▸ Rules). **Every board has undo in every tier**, so there is no tier-shaped
|
/// (13-native-undo.md ▸ Rules). **Every board has undo**, so there is no substrate-shaped
|
||||||
/// disablement to write: 03's parenthetical about boards without undo was 06's *git* substrate,
|
/// disablement to write: 03's parenthetical about boards without undo was 06's *git* substrate,
|
||||||
/// which a board once bound by being in git mode — on any tier since 12-editions.md ▸ PIVOT
|
/// which a board once bound by being in git mode — on any tier since 12-editions.md ▸ PIVOT
|
||||||
/// 2026-08-07, and under a Pro subscription before it, until app-managed git left the app entirely
|
/// 2026-08-07, and under a Pro subscription before it, until app-managed git left the app entirely
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ public final class CardBodyEditSession {
|
|||||||
|
|
||||||
/// **Whether an Edit session is open right now** — the body column is showing the editor.
|
/// **Whether an Edit session is open right now** — the body column is showing the editor.
|
||||||
///
|
///
|
||||||
/// The fact pro-m1's committer stages around: "a board change committing mid-session excludes
|
/// The fact 06's auto-committer staged around, before app-managed git was excised
|
||||||
|
/// (`strategy/01-git-excision.md`, 2026-08-08): "a board change committing mid-session excludes
|
||||||
/// the session card's folder from staging, so a lane move never sweeps half-typed body text into
|
/// the session card's folder from staging, so a lane move never sweeps half-typed body text into
|
||||||
/// its commit" (06-history-undo.md ▸ Rules ▸ Auto-commit).
|
/// its commit" (06-history-undo.md ▸ Rules ▸ Auto-commit).
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ struct CommentsBesideBodyCommand: View {
|
|||||||
|
|
||||||
// MARK: - File ▸ Add Comment
|
// MARK: - File ▸ Add Comment
|
||||||
|
|
||||||
/// File ▸ Add Comment (**no default chord**) — card window, all tiers (11-command-nexus.md;
|
/// File ▸ Add Comment (**no default chord**) — card window (11-command-nexus.md;
|
||||||
/// 05-card-window.md ▸ The comments column).
|
/// 05-card-window.md ▸ The comments column).
|
||||||
///
|
///
|
||||||
/// ### One gesture, two effects, in the one order that works
|
/// ### One gesture, two effects, in the one order that works
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import Observation
|
|||||||
/// Added, and the only genuinely new thing in this type: **session-start bytes**. The body has no
|
/// Added, and the only genuinely new thing in this type: **session-start bytes**. The body has no
|
||||||
/// Cancel — leaving Edit is a commit, and ⌘Z in the editor is the text view's own undo — while an
|
/// Cancel — leaving Edit is a commit, and ⌘Z in the editor is the text view's own undo — while an
|
||||||
/// inline comment edit has a Cancel button and an Escape that means it. 13-native-undo.md forbids
|
/// inline comment edit has a Cancel button and an Escape that means it. 13-native-undo.md forbids
|
||||||
/// byte capture *on the undo stack* in every tier, and this is not that: the capture is a live
|
/// byte capture *on the undo stack* anywhere, and this is not that: the capture is a live
|
||||||
/// buffer's, held for the length of one session, discarded when the session ends, and never
|
/// buffer's, held for the length of one session, discarded when the session ends, and never
|
||||||
/// registered anywhere. `BoardStoreComments`' own note says so — "an inline edit's revert is its
|
/// registered anywhere. `BoardStoreComments`' own note says so — "an inline edit's revert is its
|
||||||
/// *session*'s … which is a live buffer, not a stack entry".
|
/// *session*'s … which is a live buffer, not a stack entry".
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ struct BoardAnnouncerSpeechTests {
|
|||||||
#expect(BoardAnnouncer.speech(for: facts) == "Pulled 3 commits")
|
#expect(BoardAnnouncer.speech(for: facts) == "Pulled 3 commits")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Every free-tier bracket today. The seam exists; pro-m1 supplies the phrases.
|
/// Every bracket today. The seam exists; nothing in the app supplies a phrase yet.
|
||||||
@Test("A bracket with no phrase to say stays silent rather than falling back to the digest")
|
@Test("A bracket with no phrase to say stays silent rather than falling back to the digest")
|
||||||
func bracketWithoutAPhrase() {
|
func bracketWithoutAPhrase() {
|
||||||
var facts = BoardAnnouncer.ReloadFacts()
|
var facts = BoardAnnouncer.ReloadFacts()
|
||||||
@@ -733,9 +733,9 @@ struct BoardAnnouncerStoreTests {
|
|||||||
#expect(log.lines == ["Card 'Fix login' was deleted externally"])
|
#expect(log.lines == ["Card 'Fix login' was deleted externally"])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The seam pro-m1 fills. No free-tier operation passes a phrase today, so this is the one
|
/// The seam a future wholesale operation fills. Nothing in the app passes a phrase today, so
|
||||||
/// place the completion path is exercised end to end — including that the phrase is *consumed*
|
/// this is the one place the completion path is exercised end to end — including that the phrase
|
||||||
/// rather than left armed for whatever reload comes next.
|
/// is *consumed* rather than left armed for whatever reload comes next.
|
||||||
@Test("A bracketed operation announces at completion, once, and never again")
|
@Test("A bracketed operation announces at completion, once, and never again")
|
||||||
func bracketSpeaksOnceAtCompletion() async throws {
|
func bracketSpeaksOnceAtCompletion() async throws {
|
||||||
let fixture = try makeBoard()
|
let fixture = try makeBoard()
|
||||||
@@ -755,7 +755,7 @@ struct BoardAnnouncerStoreTests {
|
|||||||
#expect(log.lines == ["Board changed: 1 card edited"], "the phrase was consumed, not carried forward")
|
#expect(log.lines == ["Board changed: 1 card edited"], "the phrase was consumed, not carried forward")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test("A bracket with no phrase to say stays quiet — every free-tier bracket today")
|
@Test("A bracket with no phrase to say stays quiet — every bracket today")
|
||||||
func silentBracket() async throws {
|
func silentBracket() async throws {
|
||||||
let fixture = try makeBoard()
|
let fixture = try makeBoard()
|
||||||
defer { fixture.tearDown() }
|
defer { fixture.tearDown() }
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import Testing
|
|||||||
@testable import Kanban
|
@testable import Kanban
|
||||||
|
|
||||||
/// The snapshot summarizer — 10-accessibility.md ▸ Live board announcements' "one polite digest per
|
/// The snapshot summarizer — 10-accessibility.md ▸ Live board announcements' "one polite digest per
|
||||||
/// reload debounce", and (later) pro-m1's commit-message engine, whose counting rules these are too.
|
/// reload debounce", and (later) the change narrator's message engine (`Kanban/Changes/`), whose
|
||||||
|
/// counting rules these are too.
|
||||||
///
|
///
|
||||||
/// Every case here is **two real loads of a real board**, written and mutated on disk the way an
|
/// Every case here is **two real loads of a real board**, written and mutated on disk the way an
|
||||||
/// outside writer mutates one: `BoardDiff` compares what a reload compares, and a suite that
|
/// outside writer mutates one: `BoardDiff` compares what a reload compares, and a suite that
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import Testing
|
|||||||
/// **A provider that retires every step on arrival** — the minimal fake `HistoryProviding.backedContent`'s
|
/// **A provider that retires every step on arrival** — the minimal fake `HistoryProviding.backedContent`'s
|
||||||
/// own doc names ("a substrate that keeps no steps... and a test fake's"). `register(_:)` runs the
|
/// own doc names ("a substrate that keeps no steps... and a test fake's"). `register(_:)` runs the
|
||||||
/// step's retirement immediately and keeps nothing, which is what makes "purge rides the close flush"
|
/// step's retirement immediately and keeps nothing, which is what makes "purge rides the close flush"
|
||||||
/// true over such a substrate with no tier check anywhere in the call path.
|
/// true over such a substrate with no substrate check anywhere in the call path.
|
||||||
@MainActor
|
@MainActor
|
||||||
private final class NoBackingHistoryProvider: HistoryProviding {
|
private final class NoBackingHistoryProvider: HistoryProviding {
|
||||||
var canUndo = false
|
var canUndo = false
|
||||||
@@ -843,8 +843,9 @@ struct CardSessionPurgeTests {
|
|||||||
let window = try makeWindow(fixture)
|
let window = try makeWindow(fixture)
|
||||||
window.comments.reload()
|
window.comments.reload()
|
||||||
// A substrate that keeps no steps drops every registration and retires it on the way past —
|
// A substrate that keeps no steps drops every registration and retires it on the way past —
|
||||||
// which is what makes "purge rides the close flush" true structurally, with no tier check at
|
// which is what makes "purge rides the close flush" true structurally, with no substrate
|
||||||
// any call site. `history` is weak, so the fake is held locally for the assertion's duration.
|
// check at any call site. `history` is weak, so the fake is held locally for the assertion's
|
||||||
|
// duration.
|
||||||
let noBackingProvider = NoBackingHistoryProvider()
|
let noBackingProvider = NoBackingHistoryProvider()
|
||||||
window.store.history = noBackingProvider
|
window.store.history = noBackingProvider
|
||||||
|
|
||||||
|
|||||||
@@ -796,7 +796,7 @@ struct CommentUndoTests {
|
|||||||
#expect(store.editComment(ItemID(rawValue: CommentIdent.one), inCard: cardID, body: "revised\n"))
|
#expect(store.editComment(ItemID(rawValue: CommentIdent.one), inCard: cardID, body: "revised\n"))
|
||||||
store.purgeCommentTrash(inCard: cardID)
|
store.purgeCommentTrash(inCard: cardID)
|
||||||
|
|
||||||
#expect(!history.canUndo, "no byte capture in any tier — 13's rule")
|
#expect(!history.canUndo, "no byte capture on the undo stack, anywhere — 13's rule")
|
||||||
// The gate did not make the purge inert: nothing on the stack names this entry, so it goes —
|
// The gate did not make the purge inert: nothing on the stack names this entry, so it goes —
|
||||||
// per entry, and still without a step to show for it.
|
// per entry, and still without a step to show for it.
|
||||||
#expect(!fixture.exists("\(card)/comments/.trash/\(CommentIdent.two)"))
|
#expect(!fixture.exists("\(card)/comments/.trash/\(CommentIdent.two)"))
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ struct DedupeRuleTests {
|
|||||||
#expect(verdict.duplicates.map(\.path) == ["\(Ident.lane2)/\(Dup.lower)"])
|
#expect(verdict.duplicates.map(\.path) == ["\(Ident.lane2)/\(Dup.lower)"])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Rung one — once the git seam, which pro-m1 filled and base never did; nothing fills it now
|
/// Rung one — once the git seam, which only the git provider ever filled; nothing fills it now
|
||||||
/// that app-managed git is excised (`strategy/01-git-excision.md`, 2026-08-08), and the ladder
|
/// 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.
|
/// falls through to birth date on every board.
|
||||||
@Test("Git path history beats birth date")
|
@Test("Git path history beats birth date")
|
||||||
|
|||||||
@@ -522,9 +522,10 @@ struct EchoLedgerStoreTests {
|
|||||||
/// **The Writer's heal operations drop heal-marked receipts** (06-history-undo.md ▸ Commit messages,
|
/// **The Writer's heal operations drop heal-marked receipts** (06-history-undo.md ▸ Commit messages,
|
||||||
/// ruled 2026-07-29: "attribution machinery like the author split, never message tagging").
|
/// ruled 2026-07-29: "attribution machinery like the author split, never message tagging").
|
||||||
///
|
///
|
||||||
/// **Inert in base beyond the ledger itself**: nothing here reads the flag and nothing renders it —
|
/// **Inert beyond the ledger itself**: nothing here reads the flag and nothing renders it — it is
|
||||||
/// it is what pro-m1's committer will read to split a heal's paths into their own commit, and these
|
/// what whatever records history next (the foreign-change journal, `strategy/01-git-excision.md` ▸
|
||||||
/// tests pin the seam it will read from, not a committer that does not exist yet.
|
/// Successors) would read to split a heal's paths out from a user's own, and these tests pin the
|
||||||
|
/// seam it will read from, not a consumer that does not exist yet.
|
||||||
@Suite("EchoLedger — heal-marked receipts")
|
@Suite("EchoLedger — heal-marked receipts")
|
||||||
struct EchoLedgerHealMarkTests {
|
struct EchoLedgerHealMarkTests {
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import Testing
|
|||||||
///
|
///
|
||||||
/// **EXCISION 2026-08-08** (`strategy/01-git-excision.md`) retired the pivot in turn: app-managed
|
/// **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
|
/// 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
|
/// `Kanban/` that opens `.git` for writing, on any board at all. 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
|
/// 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
|
/// 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.
|
/// decision. This suite is that restored claim's proof.
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ There is one UI bundle because there is one app (`project.yml`; DESIGN/12-editio
|
|||||||
|
|
||||||
The trash grammar these flows encode is the current one: delete moves a card into `<root>/.trash/`; ⌘⌫ stages by place (board → trash, no confirmation; trash → permanent, with one); ⌥⌘⌫ skips the trash from anywhere, with a confirmation; restore is an ordinary move out — a drag, or ⌘X in the trash and ⌘V into a lane. **There is no Put Back.**
|
The trash grammar these flows encode is the current one: delete moves a card into `<root>/.trash/`; ⌘⌫ stages by place (board → trash, no confirmation; trash → permanent, with one); ⌥⌘⌫ skips the trash from anywhere, with a confirmation; restore is an ordinary move out — a drag, or ⌘X in the trash and ⌘V into a lane. **There is no Put Back.**
|
||||||
|
|
||||||
Two things this pass deliberately does **not** cover, because they are not base's: any git flow (commit, pull, push — pro-m1's, and base has no git operations at all; that a `.git` folder is inert in base is pinned by `KanbanTests/InertGitTests.swift`), and the accessibility audits, which are `AccessibilityVerification.md`'s.
|
Two things this pass deliberately does **not** cover: any git flow (commit, pull, push — the app has no git operations at all since app-managed git was excised, `strategy/01-git-excision.md`; that the file layer leaves a `.git` subtree byte-identical is pinned by `KanbanTests/GitAgnosticStorageTests.swift`), and the accessibility audits, which are `AccessibilityVerification.md`'s.
|
||||||
|
|
||||||
## Manual-only flows
|
## Manual-only flows
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user