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
+11 -5
View File
@@ -893,14 +893,20 @@ struct BoardView: View {
return .handled
}
/// **-arrow extends, and stops at both boundaries** (04 The trash, settled): "a -arrow whose
/// next step would cross from live cards into the trash (or back), or from card entries onto a
/// lane entry within it, is simply inert".
/// **-arrow extends, and stops at the container boundary** (04 The trash: "-arrow extension
/// still stops at the container boundary") a -arrow whose next step would cross from live
/// cards into the trash, or back, is simply inert.
///
/// **The kind boundary is no longer one of the stops inside the trash** (re-ruled 2026-07-31):
/// "-click ranges, -arrow extension all sweep every row" there, so a -arrow from a trash
/// card onto a lane row extends across it. On the board the kind test stands the live grammar
/// is still cards XOR lanes.
///
/// The *step* that would cross is what goes inert the crossing item is never stepped over in
/// search of a legal one, because that would silently drop the held range for a longer reach
/// than the user asked for. So the nearest neighbour is computed **unrestricted** and then
/// tested: a different side or a different kind means this press does nothing at all.
/// tested: a different side (or, on the board, a different kind) means this press does nothing
/// at all.
private func extend(_ direction: NavigationMath.Direction, from head: ItemID) -> KeyPress.Result {
guard let origin = marqueeTargets.targets[head],
let nextID = NavigationMath.nearest(
@@ -910,7 +916,7 @@ struct BoardView: View {
),
let next = marqueeTargets.targets[nextID],
next.container == origin.container,
next.kind == origin.kind
next.container == .trash || next.kind == origin.kind
else { return .handled }
// An extension with no anchor makes one of where it started the keyboard's equivalent of