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:
@@ -28,8 +28,11 @@ private enum More {
|
||||
static let cardF = "ffffffff-ffff-4fff-8fff-ffffffffffff"
|
||||
}
|
||||
|
||||
private func card(order: String, title: String) -> String {
|
||||
"---\nschema: 1\ntitle: \(title)\norder: \(order)\n---\n\(title) body.\n"
|
||||
private func card(order: String, title: String, modified: String? = nil) -> String {
|
||||
// The trash sorts by `modified` descending (01-storage-format.md § Deletion, re-ruled
|
||||
// 2026-07-31), so a trash fixture states its column position here rather than in `order`.
|
||||
let stamp = modified.map { "modified: \($0)\n" } ?? ""
|
||||
return "---\nschema: 1\ntitle: \(title)\norder: \(order)\n\(stamp)---\n\(title) body.\n"
|
||||
}
|
||||
|
||||
private func untitled(order: String) -> String {
|
||||
@@ -48,10 +51,11 @@ private func makeBoard() throws -> WriterFixture {
|
||||
try fixture.item(More.laneA, card(order: "1024", title: "Todo"))
|
||||
try fixture.item("\(More.laneA)/\(Ident.card1)", card(order: "1024", title: "First"))
|
||||
try fixture.item("\(More.laneA)/\(Ident.card2)", card(order: "2048", title: "Second"))
|
||||
// Newest-first by ordinary ranks: every arrival mints above the current top.
|
||||
try fixture.item(".trash/\(More.cardD)", card(order: "1024", title: "Oldest"))
|
||||
try fixture.item(".trash/\(More.cardE)", card(order: "512", title: "Middle"))
|
||||
try fixture.item(".trash/\(More.cardF)", card(order: "256", title: "Newest"))
|
||||
// Newest-first by `modified`; the ranks disagree on purpose, so nothing here can pass by
|
||||
// accident of the retired arrival-rank rule.
|
||||
try fixture.item(".trash/\(More.cardD)", card(order: "256", title: "Oldest", modified: "2026-05-01T09:00:00Z"))
|
||||
try fixture.item(".trash/\(More.cardE)", card(order: "512", title: "Middle", modified: "2026-05-03T09:00:00Z"))
|
||||
try fixture.item(".trash/\(More.cardF)", card(order: "1024", title: "Newest", modified: "2026-05-05T09:00:00Z"))
|
||||
return fixture
|
||||
}
|
||||
|
||||
@@ -70,16 +74,16 @@ private let cardF = ItemID(rawValue: More.cardF)
|
||||
@Suite("The trash's contents are the container")
|
||||
struct TrashContentsTests {
|
||||
|
||||
@Test("The trash is `snapshot.trash`, newest first by ordinary ranks")
|
||||
@Test("The trash is `snapshot.trash`, newest first by `modified`")
|
||||
func theContainerIsTheList() throws {
|
||||
let fixture = try makeBoard()
|
||||
defer { fixture.tearDown() }
|
||||
let snapshot = try load(fixture)
|
||||
|
||||
#expect(snapshot.trash.compactMap(\.title.value) == ["Newest", "Middle", "Oldest"],
|
||||
"03 ▸ Trash: the trash sorts by `order` like any lane, and entry is at the top")
|
||||
"03 ▸ Trash: the trash sorts by `modified` descending, and entry is at the top")
|
||||
#expect(snapshot.trash.allSatisfy { $0.deleted.isMissing },
|
||||
"there is no `deleted:` key and no timestamp sort")
|
||||
"there is no `deleted:` key — the stamp is the position")
|
||||
}
|
||||
|
||||
@Test("Lanes are never in it, whatever a hand-editor nests in there")
|
||||
@@ -306,8 +310,11 @@ struct TrashFreightTests {
|
||||
let fixture = try WriterFixture()
|
||||
try fixture.item("", Item.board)
|
||||
try fixture.item(More.laneA, card(order: "1024", title: "Todo"))
|
||||
try fixture.item(".trash/\(More.cardD)", card(order: "1024", title: "A card"))
|
||||
try fixture.item(".trash/\(Ident.lane2)", "---\nschema: 1\ntitle: Doing\norder: 512\nkind: lane\n---\n")
|
||||
try fixture.item(
|
||||
".trash/\(More.cardD)", card(order: "1024", title: "A card", modified: "2026-05-01T09:00:00Z"))
|
||||
try fixture.item(
|
||||
".trash/\(Ident.lane2)",
|
||||
"---\nschema: 1\ntitle: Doing\norder: 512\nmodified: 2026-05-03T09:00:00Z\nkind: lane\n---\n")
|
||||
for index in 0 ..< held {
|
||||
try fixture.item(
|
||||
".trash/\(Ident.lane2)/\(UUID().uuidString.lowercased())",
|
||||
@@ -396,15 +403,15 @@ struct TrashFreightTests {
|
||||
== [.trashLane(ItemID(rawValue: Ident.lane2))])
|
||||
}
|
||||
|
||||
/// The column is one list: `resolve` hands back the trash's paths interleaved by rank, because
|
||||
/// a batch's order is the column's order (03 § Trash).
|
||||
@Test("Resolution interleaves the container's two kinds by rank")
|
||||
/// The column is one list: `resolve` hands back the trash's paths interleaved by `modified`,
|
||||
/// because a batch's order is the column's order (03 § Trash).
|
||||
@Test("Resolution interleaves the container's two kinds by the column's own order")
|
||||
func resolutionInterleaves() throws {
|
||||
let fixture = try makeFreightBoard(held: 0)
|
||||
defer { fixture.tearDown() }
|
||||
let snapshot = try load(fixture)
|
||||
|
||||
// The lane sits at 512 and the card at 1024, so the lane row comes first.
|
||||
// The lane's stamp is the newer of the two, so the lane row comes first.
|
||||
#expect(ItemPath.resolve(
|
||||
[ItemID(rawValue: Ident.lane2), cardD], in: .trash, snapshot: snapshot
|
||||
) == [.trashLane(ItemID(rawValue: Ident.lane2)), .trashCard(cardD)])
|
||||
|
||||
Reference in New Issue
Block a user