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:
2026-07-30 06:49:11 -04:00
parent 5ae48de0ea
commit 69084fdff7
27 changed files with 2159 additions and 542 deletions
+22 -12
View File
@@ -21,9 +21,11 @@ import UniformTypeIdentifiers
/// the full folder snapshots a paste reproduces byte-for-byte from and to a pending cut. It is
/// also the whole of "the snapshot survives relaunch exactly as long as the pasteboard still points
/// at it": a sweep keeps the one directory this id names and collects every other.
/// - Each `Entry` embeds the item's complete `index.md` text, so a paste still lands when the
/// snapshot is missing or unreadable "the staging-less fallback: content intact, attachments
/// absent", announced by a banner rather than discovered later.
/// - Each `Entry` embeds the item's complete `index.md` text as **identification metadata**
/// (04-interactions.md Clipboard, re-ruled 2026-07-29): menu validation, the refusal's wording,
/// and the plain-text flavor read it. It is emphatically **not** a materialization source a paste
/// whose staged snapshot is missing or unreadable refuses whole and writes nothing, because "an item
/// arrives whole index, attachments, loose files or not at all".
///
/// `kind` and `container` are the selection's own vocabulary (`SelectionKind`, `ItemContainer`) rather than
/// near-copies of it: a clipboard payload is a selection that was copied, and the cards-XOR-lanes and
@@ -67,15 +69,23 @@ public struct ClipboardManifest: Codable, Sendable, Equatable {
public var folder: String
/// The title as written, or `nil` for an untitled item "Untitled" is a rendering, never a
/// value (03-board-ui.md § Card face). Feeds the plain-text representation and the degraded
/// paste's banner.
/// value (03-board-ui.md § Card face). Feeds the plain-text representation and the refused
/// paste's banner, which names the offending entry from exactly this.
public var title: String?
/// The complete `index.md` at copy time the staging-less fallback's source bytes.
/// The complete `index.md` at copy time **identification metadata, never materialized**
/// (see the type comment). Kept because it is what lets the app answer "what was on the
/// clipboard" without touching the staging store: the plain-text flavor and a refusal's wording
/// both come from here, and both have to work when the snapshot is exactly what is missing.
public var index: String
/// How many files the item's own `attachments/` held. Zero for a lane, which has none; a
/// lane's attachments are its cards' and are counted there.
///
/// Identification metadata like the rest of the entry. It used to feed the degraded paste's
/// loss accounting ("Pasted 'Fix login' without its 3 attachments"), which is retired with the
/// degraded paste itself an item now arrives whole or not at all, so there is no partial
/// arrival left to count.
public var attachmentCount: Int
/// A **lane** entry's cards, index text and all "a lane entry embeds its cards' too,
@@ -83,9 +93,9 @@ public struct ClipboardManifest: Codable, Sendable, Equatable {
///
/// **Exactly the lane's cards**, which needs no filter: "a lane carries exactly its cards
/// the trash is board-level, so there is nothing lane-nested to strip or carry"
/// (04-interactions.md Drag and drop, resettled 2026-07-28), and the fallback only ever
/// materializes a copy a cut's move carries the real folder whole and never comes near this
/// array. So the embedded set is exactly what a fallback paste should produce.
/// (04-interactions.md Drag and drop, resettled 2026-07-28). Like the lane's own `index`, the
/// cards' text is identification metadata: it describes what the copy held, and nothing
/// materializes from it.
public var cards: [Card]
/// One card inside a copied lane.
@@ -103,9 +113,9 @@ public struct ClipboardManifest: Codable, Sendable, Equatable {
}
}
/// Everything a fallback paste of this entry would leave behind its own attachments plus,
/// for a lane, its cards'.
public var lostAttachmentCount: Int {
/// Every file this entry's subtree carried in an `attachments/` its own plus, for a lane, its
/// cards'. Identification metadata; nothing gates on it since the degraded paste retired.
public var totalAttachmentCount: Int {
attachmentCount + cards.reduce(0) { $0 + $1.attachmentCount }
}