Realign code with the 2026-07-31 rulings

The trash sorts by modified descending — the arrival rank mint retires
(Ranks.isOrderedForTrash one comparator, loader + merged order agree;
the legacy deleted: migration stamps modified from the tombstone
timestamp where parseable; delete undo steps validate existence-only;
agent guide v8). Trash selection goes kind-blind — ranges, marquee,
Select All, and the successor walk sweep both kinds; the guard moves to
the exits (mixed-payload drop refusal, copy/cut validation). The copy
stamping preflight widens back to comment depth (load-scoped posture —
the board always loads, the gesture refuses whole). Fixes a latent
no-op: trashed-lane drag restore never fired (DragSession.beginLanes
hard-coded the board container).

2403 tests in 413 suites green.

Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
2026-07-31 18:35:07 -04:00
parent 542ab169a3
commit bec75e4282
37 changed files with 1200 additions and 551 deletions
+50 -14
View File
@@ -638,27 +638,63 @@ struct CommentCopyTests {
#expect(!fixture.exists("\(copiedCard)/comments/.trash"))
}
@Test("A comment nobody can stamp never refuses the copy — it travels verbatim")
func brokenCommentDoesNotRefuseACopy() throws {
/// **The preflight reaches comment depth** (01-storage-format.md § Identity lifecycle and
/// § Enhanced schema, ruled 2026-07-31 reversing the 2026-07-30 carve-out this suite used to
/// pin): "a comment whose frontmatter cannot take the stamp refuses the copy exactly like a card
/// or lane never-refuse is a *load* posture, and a user-initiated copy is a transaction, not
/// a load".
///
/// The board still loads with this comment on it that is the other test in this file and it
/// is the gesture that refuses. Whole, and with nothing materialized: a partial copy is the one
/// outcome the transaction rule exists to prevent.
@Test("A comment nobody can stamp refuses the whole copy, and nothing is materialized")
func brokenCommentRefusesACopy() throws {
let fixture = try WriterFixture()
defer { fixture.tearDown() }
try fixture.item("", Item.board)
try fixture.item(Ident.lane1, Item.rich(order: "1024", title: "Todo"))
try fixture.item(Ident.lane2, Item.rich(order: "2048", title: "Doing"))
try fixture.item("\(Ident.lane1)/\(Ident.card1)", Item.rich(order: "1024", title: "Fix login"))
// Readable, uneditable the shape that refuses a copy at card level.
// Readable, uneditable the shape that refuses a copy at card level, now at comment depth.
try fixture.item("\(Ident.lane1)/\(Ident.card1)/comments/\(CommentIdent.one)", Item.uneditable)
let copy = try BoardWriter.copyItem(
at: fixture.url("\(Ident.lane1)/\(Ident.card1)"),
toParent: fixture.url(Ident.lane2),
order: 1024,
stamps: .fork
)
let copied = "\(Ident.lane2)/\(copy.rawValue)"
let names = try postedNames(fixture, inCard: copied)
#expect(names.count == 1, "the copy landed whole")
#expect(try fixture.indexText("\(copied)/comments/\(names[0])") == Item.uneditable, "verbatim")
var thrown: BoardWriteError?
do {
_ = try BoardWriter.copyItem(
at: fixture.url("\(Ident.lane1)/\(Ident.card1)"),
toParent: fixture.url(Ident.lane2),
order: 1024,
stamps: .fork
)
} catch {
thrown = error
}
let error = try #require(thrown)
if case .uneditableFrontmatter = error.reason {} else {
Issue.record("expected an uneditable-frontmatter refusal, got \(error.reason)")
}
#expect(error.path.hasSuffix("comments/\(CommentIdent.one)/index.md"),
"the path points at the annotation, which is where the fix is")
// Nothing landed, and the source is untouched.
#expect(try fixture.entryNames(Ident.lane2).filter { IntegrityRules.isIdentityShaped($0) }.isEmpty)
#expect(try fixture.indexText("\(Ident.lane1)/\(Ident.card1)/comments/\(CommentIdent.one)")
== Item.uneditable)
}
/// The other side of "load-scoped": the very same board **loads**, and its card window's thread
/// read tolerates the annotation. Only the copy gesture refuses.
@Test("The same broken comment never refuses the board")
func brokenCommentNeverRefusesTheBoard() throws {
let fixture = try WriterFixture()
defer { fixture.tearDown() }
try fixture.item("", Item.board)
try fixture.item(Ident.lane1, Item.rich(order: "1024", title: "Todo"))
try fixture.item("\(Ident.lane1)/\(Ident.card1)", Item.rich(order: "1024", title: "Fix login"))
try fixture.item("\(Ident.lane1)/\(Ident.card1)/comments/\(CommentIdent.one)", Item.uneditable)
let model = try BoardLoader.load(boardRoot: fixture.root).model
#expect(model.lanes.first?.cards.map(\.id.rawValue) == [Ident.card1])
}
@Test("Template instantiation is born-today at comment depth too")
@@ -739,7 +775,7 @@ struct CommentCopyTests {
try fixture.item(Ident.lane1, Item.rich(order: "1024", title: "Todo"))
let card = try threadedCard(fixture, in: Ident.lane1, card: Ident.card1)
try BoardWriter.deleteCardToTrash(at: fixture.url(card), inBoard: fixture.root, order: 1024)
try BoardWriter.deleteCardToTrash(at: fixture.url(card), inBoard: fixture.root)
let trashedCard = ".trash/\(Ident.card1)"
#expect(fixture.exists("\(trashedCard)/comments/\(CommentIdent.one)"))