Give transient UI state an explicit home in the store

TransientBoardState, one per store, holds state by how a reload treats
it: item-referencing sets (selection, drag membership, pending cut)
share one shape and one constraint rule — members must exist in the
current universe — applied in two directions by one primitive, so the
search filter's hidden-cards rule and reload survival are one rule
expressed once; derived state is stored as its inputs only (the query,
never its result set); and the new-card placeholder is a lane-anchored
overlay with no UUID until commit, discarded when its lane vanishes or
tombstones, handed off when the created card's UUID appears. Trash
visibility rides along per-open, never persisted. The decision is
written back into DESIGN/02 § Changes from Kanban — the TBD is closed.

Full suite 352 tests in 64 suites green. Two findings filed.

Claude-Session: https://claude.ai/code/session_018BjQRYBR6jQja3jCRi5S3A
This commit is contained in:
2026-07-26 21:06:26 -04:00
parent 8285e19497
commit 23e761120f
6 changed files with 712 additions and 107 deletions
+6
View File
@@ -360,6 +360,12 @@ struct BoardStoreTests {
// MARK: Selection across reloads
/// The rules themselves belong to `ItemReferenceSet`/`TransientBoardState` and are proved in
/// `TransientBoardStateTests` across all three referencing sets. These stay because what they
/// pin here is the *store's* half: that `land(_:generation:origin:)` re-resolves on success and
/// only on success, read through the `selection`/`select`/`clearSelection` conveniences the
/// command sites use.
@Test("A selected item that vanished from the tree leaves the selection; the survivor stays")
func selectionDropsVanishedMembers() async throws {
let fixture = try makeBoard()