Realign code with the 2026-07-29 findings-resolution rulings
Nine rulings land as code. Reorders don't stamp — one container-change predicate (WriteOperation.rewritesOrderOnly): within-container reorders and the renumber rescale rewrite only order, while cross-lane, cross-board, and trash moves stamp modified and clear modified-by; no trash special case exists, and the m8 undo inverses conform through the same seam. Copies are transactions: the root-strict/nested-lenient split retires for a whole-subtree stampability preflight that refuses loudly naming the offender, and every item-level copy severs remote/remote-state at every level (whole-board forks carry them verbatim). Paste refuses, never degrades: the embedded-index.md materialization and its loss row retire; a missing staged snapshot produces nothing and posts an error-tone one-shot named from manifest metadata. Coerce-tier fallbacks log through the Defect stream with path context attached loader-side. Displacement is level-uniform: a file squatting attachments inside a card heals by the same rename ladder as board-root squatters; comments stays tolerated. Delete Immediately joins card and lane context menus as Delete's ⌥-alternate with its own VO custom action, routed through an explicit container so the menu target outranks standing selection. Agent guide v7 teaches the stamp discipline and the card-level attachments claim, and sheds two stale v6 lines (lanes trash now; kind is taught). Verified conformant, unchanged: edition-aware Undo/Redo disable, trash marquee full-height backdrop. Both schemes 1854 tests / 318 suites green; verify-editions 30/30. Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -36,7 +36,12 @@ final class TrashConfirmations {
|
||||
/// names the whole container and re-derives its targets at the moment it runs).
|
||||
enum Action: Equatable {
|
||||
case deleteTrashCards(Set<ItemID>)
|
||||
case purge(Set<ItemID>)
|
||||
/// **The container travels with the ids**, because a purge can be aimed at either side and
|
||||
/// the two entry points below disagree about which: the menu-bar command means the
|
||||
/// selection's container, a context menu means `.board` whatever is selected. Re-reading
|
||||
/// the selection when the alert is answered would resolve a board card against `.trash` and
|
||||
/// purge nothing — a confirmed destructive command becoming a silent no-op.
|
||||
case purge(Set<ItemID>, ItemContainer)
|
||||
case emptyTrash
|
||||
}
|
||||
}
|
||||
@@ -91,20 +96,54 @@ final class TrashConfirmations {
|
||||
/// expression rather than three call sites.
|
||||
///
|
||||
/// Its one caller is File ▸ Delete Immediately, which passes the selection's own ids — which is
|
||||
/// what makes reading `store.selection.container` for the prompt correct here and wrong for a
|
||||
/// context menu (`requestTrashDelete` above exists for exactly that difference).
|
||||
/// what makes reading `store.selection.container` correct here and wrong for a context menu
|
||||
/// (`requestTrashDelete` above exists for exactly that difference).
|
||||
func requestPurge(of ids: Set<ItemID>, in store: BoardStore) {
|
||||
let container = store.selection.container
|
||||
guard store.purgeIsUnrecoverable else {
|
||||
store.deleteImmediately(ids)
|
||||
store.deleteImmediately(ids, in: container)
|
||||
return
|
||||
}
|
||||
guard let prompt = TrashModel.purgePrompt(
|
||||
for: ids,
|
||||
in: store.selection.container,
|
||||
in: container,
|
||||
snapshot: store.snapshot,
|
||||
unrecoverable: true
|
||||
) else { return }
|
||||
pending = Pending(prompt: prompt, action: .purge(ids))
|
||||
pending = Pending(prompt: prompt, action: .purge(ids, container))
|
||||
}
|
||||
|
||||
/// **Delete's ⌥-alternate, aimed at an explicit set** — the board-side card and lane
|
||||
/// context-menu rows' Delete Immediately (11-command-nexus.md ▸ Context menus' Card and Lane
|
||||
/// rows: "Delete — with Delete Immediately as its ⌥-alternate … Finder's pattern: hold ⌥ and
|
||||
/// Delete becomes Delete Immediately").
|
||||
///
|
||||
/// A third entry point beside `requestPurge` and `requestTrashDelete`, for `requestTrashDelete`'s
|
||||
/// own reason mirrored onto the other container: `requestPurge(of:in:)` reads
|
||||
/// `store.selection.container` for the prompt, which is correct for its one caller (File ▸
|
||||
/// Delete Immediately, whose ids *are* the selection) and wrong for a context menu, which names
|
||||
/// its target by where the ⌥-held click landed — right-clicking a card or lane while a *trash*
|
||||
/// selection stands must still purge the clicked item.
|
||||
///
|
||||
/// The container is always `.board`: this alternate exists only on the board-side rows —
|
||||
/// "the trash needs no alternate: its Delete is already permanent" (11-command-nexus.md's Lane
|
||||
/// row).
|
||||
///
|
||||
/// **The container is supplied end to end**, prompt and write alike: `store.deleteImmediately`
|
||||
/// takes it as a parameter rather than reading the selection, so a confirmed purge aimed at a board
|
||||
/// item cannot silently find nothing because a trash selection happened to be standing.
|
||||
func requestBoardPurge(of ids: Set<ItemID>, in store: BoardStore) {
|
||||
guard store.purgeIsUnrecoverable else {
|
||||
store.deleteImmediately(ids, in: .board)
|
||||
return
|
||||
}
|
||||
guard let prompt = TrashModel.purgePrompt(
|
||||
for: ids,
|
||||
in: .board,
|
||||
snapshot: store.snapshot,
|
||||
unrecoverable: true
|
||||
) else { return }
|
||||
pending = Pending(prompt: prompt, action: .purge(ids, .board))
|
||||
}
|
||||
|
||||
/// Raises Empty Trash…'s alert. **Always** — it guards bulk scope rather than per-item
|
||||
@@ -124,7 +163,7 @@ final class TrashConfirmations {
|
||||
self.pending = nil
|
||||
switch pending.action {
|
||||
case let .deleteTrashCards(ids): store.deleteTrashCards(ids)
|
||||
case let .purge(ids): store.deleteImmediately(ids)
|
||||
case let .purge(ids, container): store.deleteImmediately(ids, in: container)
|
||||
case .emptyTrash: store.emptyTrash()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user