Every comment-trash purge kneels to the ownership gate — the container-whole retirement retires
Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
@@ -541,6 +541,48 @@ public final class BoardStore: HealHost {
|
||||
@ObservationIgnored
|
||||
public var commitSeam: HistoryCommitSeam?
|
||||
|
||||
// MARK: The open card windows
|
||||
|
||||
/// **Which of this board's cards have a card window open right now** — one fact, kept for one
|
||||
/// consumer: the `comments/.trash/` purge's ownership gate (13-native-undo.md ▸ Interaction with
|
||||
/// the trash, ruled 2026-08-06).
|
||||
///
|
||||
/// > "One carve-out: **an open card window is itself an owner of its card's comment trash** — a
|
||||
/// > retirement firing while the card's window is open defers its purge to that window's close,
|
||||
/// > because entries deleted in the live session are backed by the window's fine steps, which the
|
||||
/// > board-stack inventory cannot see; the close then settles by the same gate."
|
||||
///
|
||||
/// A window's fine comment steps live on the *window's* stack (`CardWindowUndo`), which
|
||||
/// `history.backedContent` — the board's stack — cannot see. So while a window is open the
|
||||
/// inventory is knowingly incomplete for that card, and the only honest answer to a purge is *not
|
||||
/// yet*. `purgeCommentTrash(inCard:)` is the one reader.
|
||||
///
|
||||
/// **Deliberately not a reference to the window.** The store is the *board's*, a card window is a
|
||||
/// scene, and 02-architecture.md § Components has the ownership pointing the other way; what the
|
||||
/// gate needs is an identity, not an object. It is also why this is a `Set` rather than a count:
|
||||
/// a card window's identity is `(board, card)` (`CardWindowRef`), so a card has at most one.
|
||||
///
|
||||
/// A window that never unmarked — a store torn down under an open window — costs nothing beyond a
|
||||
/// purge that did not run, which the next open's residue sweep collects. Convergence, not a leak.
|
||||
@ObservationIgnored
|
||||
public private(set) var openCardWindows: Set<ItemID> = []
|
||||
|
||||
/// **A card window opened over this card** — called from the window's open, beside the crash-residue
|
||||
/// sweep it already runs there (`CardWindowHost.openCommentThread`).
|
||||
public func cardWindowDidOpen(inCard id: ItemID) {
|
||||
openCardWindows.insert(id)
|
||||
}
|
||||
|
||||
/// **A card window over this card is closing** — called from the window's session end, and
|
||||
/// **before** that close's own purge (`CardWindowSession.endSession`).
|
||||
///
|
||||
/// The order is the whole of the carve-out's honesty: the close is the moment the window stops
|
||||
/// being an owner, so a close that unmarked itself *after* running its purge would defer that
|
||||
/// purge into a no-op and hand the work to nobody.
|
||||
public func cardWindowDidClose(inCard id: ItemID) {
|
||||
openCardWindows.remove(id)
|
||||
}
|
||||
|
||||
// MARK: Reload machinery
|
||||
|
||||
/// Monotonic id of the most recently *started* reload — and therefore also the number of tree
|
||||
|
||||
Reference in New Issue
Block a user