Realign undo with the evening rulings — repo-nested and identity anchors
Repo-nested boards bind native undo in every tier (25d2513): the no-undo case is gone, makeHistoryProvider answers git or native, and the native path provably never touches the enclosing repository's .git. Session undo steps anchor by card identity, never by path (9119aa1): HistoryAnchor carries the card UUID (plus comment/draft vocabulary) and apply-time validation resolves the current folder via the same both-container walk writeCardBody uses — a board-side lane or trash move no longer stales the coarse close step, while a genuine field collision still skips it whole. 2448 tests in 423 suites green. Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
@@ -898,17 +898,21 @@ struct GitUndoSessionTests {
|
||||
|
||||
/// **The provider follows the board, not the tier alone** (re-ruled 2026-07-31 — 12-editions.md
|
||||
/// ▸ The provider seam; 13-native-undo.md's header; 06-history-undo.md ▸ Rules), stated as the
|
||||
/// matrix it is: gitless boards bind the native stack in *every* tier ("an upgrade never removes
|
||||
/// undo"), a Pro git board binds the git provider, and a repo-nested board binds nothing.
|
||||
/// matrix it is: **boards without app-managed git — repo-nested included — bind the native stack in
|
||||
/// *every* tier** ("an upgrade never removes undo"), and a Pro git board binds the git provider.
|
||||
/// There is no third answer any more.
|
||||
///
|
||||
/// ### The free tier's row is one cell wide, structurally
|
||||
/// ### The repo-nested row stopped being an exception
|
||||
///
|
||||
/// `HistoryStore.compose` returns `nil` off Pro, so a free-tier session never detects a mode at all
|
||||
/// and cannot tell a repo-nested board from a plain one — which is not an omission but 12 ▸ The free
|
||||
/// tier and `.git` verbatim: "opening a board that has one (a formerly-subscribed user's board, a
|
||||
/// 1.x board, **a repo-nested board**) works normally — files read and write as on any board,
|
||||
/// **native undo runs**". 06's no-undo rule for repo-nested boards is a rule of a doc whose own first
|
||||
/// line reads "Tier scope: Lanework Pro", and the tests below pin both halves.
|
||||
/// **native undo runs**". Pro used to answer differently on the same board, which made subscribing
|
||||
/// *remove* ⌘Z from it; the re-ruling of 2026-07-31 retired that case outright — "leave strictly
|
||||
/// alone concerns *git*, and this stack never touches git — memory-only, journal-free,
|
||||
/// session-scoped" (13's header) — so the two tiers now agree on every board there is, and the tests
|
||||
/// below pin both halves of that agreement.
|
||||
@MainActor
|
||||
@Suite("Git undo ▸ which board gets a provider")
|
||||
struct GitUndoBindingTests {
|
||||
@@ -977,8 +981,8 @@ struct GitUndoBindingTests {
|
||||
#expect(log.crossings == ["undo Move Card"])
|
||||
}
|
||||
|
||||
@Test("Pro on a repo-nested board binds no provider — the one no-undo case")
|
||||
func proOnARepoNestedBoardBindsNothing() throws {
|
||||
@Test("Pro on a repo-nested board binds the native stack — the no-undo case is retired")
|
||||
func proOnARepoNestedBoardBindsTheNativeStack() throws {
|
||||
let outer = try WriterFixture()
|
||||
defer { outer.tearDown() }
|
||||
// A repository at the *parent*, with the board inside it — the nested posture.
|
||||
@@ -995,16 +999,26 @@ struct GitUndoBindingTests {
|
||||
let ref = try openBoard(model, at: boardRoot)
|
||||
let session = try #require(model.session(for: ref))
|
||||
|
||||
// 06 ▸ Rules: a board inside somebody else's repository is "left strictly alone … so they get
|
||||
// **no undo**" — no app-managed undo journal, which an in-memory stack here would be.
|
||||
// The mode is detected — Pro walks the ancestors — and it no longer decides ⌘Z: "Pro binds it
|
||||
// on mode-none **and repo-nested** boards alike … what repo-nested denies is app-managed
|
||||
// history, never ⌘Z" (13's header, re-ruled 2026-07-31).
|
||||
#expect(session.gitMode == .repoNested)
|
||||
#expect(session.history == nil, "the app leaves that repository strictly alone")
|
||||
#expect(session.undoManager.canUndo == false)
|
||||
#expect(session.undoManager.canRedo == false)
|
||||
#expect(session.undoManager.undoMenuItemTitle == "Undo", "a bare row, with nothing to name")
|
||||
// And a crossing that somehow started still writes nothing.
|
||||
#expect(session.history is NativeHistoryProvider)
|
||||
#expect(session.undoManager.canUndo == false, "empty, not absent")
|
||||
|
||||
// A working stack, exactly as the free tier's on this same board — which is the whole point
|
||||
// of the re-ruling: the two tiers answer alike here now.
|
||||
let log = StepLog()
|
||||
session.history?.register(log.step("Move Card"))
|
||||
#expect(session.undoManager.canUndo)
|
||||
#expect(session.undoManager.undoMenuItemTitle == "Undo Move Card")
|
||||
session.undoManager.undo()
|
||||
session.undoManager.redo()
|
||||
#expect(log.crossings == ["undo Move Card"])
|
||||
|
||||
// And the enclosing repository is still left strictly alone: no repository was created at the
|
||||
// board, and nothing here reaches for the ancestor's `.git` (13: memory-only, journal-free).
|
||||
#expect(!FileManager.default.fileExists(atPath: boardRoot.appendingPathComponent(".git").path))
|
||||
#expect(session.git?.committer == nil, "no committer, and so nothing that could write there")
|
||||
}
|
||||
|
||||
@Test("The free tier's repo-nested board still binds the native stack — it never detects one")
|
||||
@@ -1031,6 +1045,8 @@ struct GitUndoBindingTests {
|
||||
#expect(session.git == nil)
|
||||
#expect(session.gitMode == .none, "no detection ran; the session reports the tier's one mode")
|
||||
#expect(session.history is NativeHistoryProvider)
|
||||
// Pro on this same board now answers identically (above) — the two tiers agree, which is what
|
||||
// the re-ruling of 2026-07-31 bought: an upgrade never removes undo from *any* board.
|
||||
}
|
||||
|
||||
@Test("Add-git swaps the substrate — the native stack is discarded, the git trail seeded")
|
||||
|
||||
Reference in New Issue
Block a user