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:
@@ -757,10 +757,15 @@ struct LooseFilePasteTests {
|
||||
#expect(try BoardLoader.load(boardRoot: destination.root).looseCardFiles.isEmpty)
|
||||
}
|
||||
|
||||
/// The staging-less fallback carries only `index.md`, so there is nothing to normalize and the
|
||||
/// normalization must not invent an `attachments/` for a card that has none.
|
||||
@Test("A degraded paste normalizes nothing and mints no attachments folder")
|
||||
func degradedPasteIsUnaffected() async throws {
|
||||
/// **A refused paste normalizes nothing, because it materializes nothing** (04-interactions.md ▸
|
||||
/// Clipboard, re-ruled 2026-07-29 — refuse, never degrade).
|
||||
///
|
||||
/// This was the degraded fallback's normalization case: the fallback carried only `index.md`, so the
|
||||
/// claim was that normalization must not invent an `attachments/` for a card that had none. With the
|
||||
/// fallback retired the claim gets stronger and simpler — there is no arrival to normalize at all,
|
||||
/// and the destination is exactly what it was.
|
||||
@Test("A refused paste normalizes nothing because nothing arrives")
|
||||
func aRefusedPasteNormalizesNothing() async throws {
|
||||
let harness = try ClipboardHarness(fixture: try makeLooseFileClipboardBoard())
|
||||
defer { harness.tearDown() }
|
||||
let destination = try makePasteDestination()
|
||||
@@ -776,10 +781,12 @@ struct LooseFilePasteTests {
|
||||
}
|
||||
|
||||
target.select([ItemID(rawValue: Ident.lane4)], in: .board)
|
||||
let before = try destination.entryNames(Ident.lane4).sorted()
|
||||
await harness.clipboard.paste(into: target)?.value
|
||||
|
||||
let arrived = try arrivedCard(in: destination)
|
||||
#expect(!destination.exists("\(Ident.lane4)/\(arrived)/attachments"))
|
||||
#expect(try destination.entryNames("\(Ident.lane4)/\(arrived)") == ["index.md"])
|
||||
#expect(try destination.entryNames(Ident.lane4).sorted() == before, "no card arrived")
|
||||
#expect(target.banners.oneShots.count == 1, "and the refusal said so")
|
||||
// The resident is untouched — no attachments folder was invented anywhere in the lane.
|
||||
#expect(try destination.entryNames("\(Ident.lane4)/\(Ident.indexless)") == ["index.md"])
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user