Realign code with the 2026-07-31 findings-resolution rulings
The full bullet list from Implementation card bf080d9a — both ruling batches, including the three appended mid-session by16ef377: - Restore subjects compose the inverse, never nest: crossing "Undo: S" emits "Redo: S" and vice versa; parity, not stack depth, reads a legacy double prefix (GitHistoryProvider.restoreSubject). - Git-operation failures join the one-shot failure banner tier: BannerCenter.GitFailureBanner (undo/redo/branchSwitch/addGit), error tone at failure rank merged with write one-shots by recency; the postLoss compromise is retired at both AppModel wirings. - order/schema optional below the board root: append-at-end reading (ordered siblings first, folder-name tie-break among the order-less), schema reads 1, both coerce-tier logged; the root keeps its requirements. Ranks.resolvedOrders materializes finite ranks so models and placement math stay untouched; first Writer rewrite stamps a real rank on touch, placement against an order-less sibling stamps that sibling inline in the same bracket. Agent guide v10 teaches optional keys and zero-read filing. Hostile-YAML order shapes become coercion tests; Fixtures/Valid/optional-keys.kanban replaces the four retired Malformed boards. - .gitignore is the relocation-heal noise gate: GitignoreRules pure matcher (standard semantics, board-root file only), loader consults it once per walk so matched loose files keep the stray posture; seeded (.DS_Store + .*.lanework-*) at board creation and template instantiation, healed in when missing at open — repo-nested included; empty file honored, existing files never edited; the committer's obedience via libgit2 status is pinned by test. - Comments crash-residue sweep gates on step ownership: HistoryStep derives backing from its own undo expectations, backedContent unions both stacks, the sweep purges per-entry only what no live step owns. - Skip-purge decoupled (16ef377): a stale-skipped coarse step strands whole in NativeHistoryProvider.strandedSteps — still backing, retired only at session end; clean exits purge as before. - Coarse close step named "Changes to '<card>'"; the fine body-edit wording never leaks onto the board menu. - Branch-switch settle clears every open card window's fine stack on Save All and Discard alike; the empty fold registers no coarse step. - Close flush awaits its covering snapshot (quiesce + one generation bump, 1s bound), and an explicit flush now queues behind an in-flight one instead of skipping — the audit-caught interleaving could lose a close flush permanently when the debounce fired inside the close sequence; regression tests force both races. - Commit comment bullets sort chronologically by created, not UUID. - The production-unwired CardBodyEditSession.editSessionDidChange seam is deleted with its seam-only tests. - Composition-root pins: beginSession composes the committer with the store's own EchoLedger and binds the announcer (the miswire class). - Deliberate 06 conformance pass over every 2026-07-31-tagged sentence: fixed Change-custom-key subjects (the retired named generic was the only producer), the unbuilt Replace attachment vocabulary, heal commits now authored Lanework Integrity, the config reader scopes identity to plain [user] sections, add-git re-runs detection at create (a stale mode-none could initialize inside the user's repo), and add-git failures answer at the form or the banner. Structural residue filed on the Redesign board. 2554 tests / 439 suites green. Claude-Session: https://claude.ai/code/session_01CqjXB7ASoWtbyoGod68k97
This commit is contained in:
@@ -43,12 +43,18 @@ import Observation
|
||||
/// `NSUndoManager` of its own, which is what makes it session-scoped). What lives here is the other
|
||||
/// half of 05 ▸ Edit's undo sentence: the **session**, whose end is the effective Save.
|
||||
/// `endEditSession()` is that moment — the Edit→Preview flip, raw-source entry, or the window
|
||||
/// closing — and it is deliberately a named call rather than a side effect of `flush()`, because
|
||||
/// pro-m1's auto-commit coalesces exactly here: every debounced tick inside one session rides its
|
||||
/// own `performWrite` bracket, and the committer's rule is one commit per *session*, "never per save
|
||||
/// tick" (06-history-undo.md ▸ Rules ▸ Auto-commit). In the free tier there is no committer, so
|
||||
/// the two calls do the same work today; the seam is what keeps them from having to be pulled apart
|
||||
/// later.
|
||||
/// closing — and it is deliberately a named call rather than a side effect of `flush()`, because it
|
||||
/// is where this session's **one undo step** is registered: every debounced tick inside a session
|
||||
/// rides its own `performWrite` bracket, and 13-native-undo.md's rule is one step per session, never
|
||||
/// per save tick (`registerUndo`).
|
||||
///
|
||||
/// **The commit boundary is no longer here** (06-history-undo.md ▸ Rules ▸ Auto-commit, widened
|
||||
/// 2026-07-31 — "Board history sees card-window sessions, not gestures"): the committer's
|
||||
/// stage-around used to open and close with this flip, and it now opens with the *window* and
|
||||
/// releases when the window's session ends, so a comment posted with the body in Preview is inside
|
||||
/// the same one commit as the body. What this type still owes that model is `isEditing`, read as
|
||||
/// unsaved-content state rather than announced (`CardWindowHost`); the announcement seam it used to
|
||||
/// wire went with the widening.
|
||||
@MainActor
|
||||
@Observable
|
||||
public final class CardBodyEditSession {
|
||||
@@ -110,23 +116,6 @@ public final class CardBodyEditSession {
|
||||
@ObservationIgnored
|
||||
public var registerUndo: ((_ priorBody: String, _ newBody: String) -> Void)?
|
||||
|
||||
/// **The session boundary, announced** — called with `true` when an Edit session opens and
|
||||
/// `false` when it ends, and with nothing in between.
|
||||
///
|
||||
/// **No longer the committer's stage-around boundary** (06-history-undo.md ▸ Rules ▸ Auto-commit,
|
||||
/// widened 2026-07-31 — "Board history sees card-window sessions, not gestures"): the exclusion
|
||||
/// used to open and close with this flip, and it now opens with the *window* and releases when its
|
||||
/// session ends, so a comment posted with the body in Preview is inside the same one commit as the
|
||||
/// body. `CardWindowHost` therefore no longer wires this to anything, and the Edit→Preview flip is
|
||||
/// a save point rather than a commit point.
|
||||
///
|
||||
/// The seam stays, unwired, because it is the only announcement of the boundary this type makes
|
||||
/// and the ordering it carries — that the flush precedes the announcement — is a property worth
|
||||
/// keeping proved (`AutoCommitTests`). A closure for `save`'s reason exactly: this type is a
|
||||
/// buffer and a clock, and it stays testable by having no idea what a repository is.
|
||||
@ObservationIgnored
|
||||
public var editSessionDidChange: ((_ isEditing: Bool) -> Void)?
|
||||
|
||||
/// What disk said before this session's **first** landed save — the step's before-value, held
|
||||
/// from the first write until the session ends.
|
||||
///
|
||||
@@ -194,16 +183,16 @@ public final class CardBodyEditSession {
|
||||
/// because its card's body was empty (`CardBodyMode.opening(body:)`).
|
||||
///
|
||||
/// Idempotent, because the mode can be re-asserted by a menu validation pass or a re-published
|
||||
/// focus value, and a second announcement would register a session that is already registered.
|
||||
/// focus value, and `isEditing` is observed: assigning `true` over `true` would still notify, and
|
||||
/// the notification is a view rebuild for a session that never changed (`adopt(diskBody:)`'s
|
||||
/// equal-assignment note, one property along).
|
||||
public func beginEditSession() {
|
||||
guard !isEditing else { return }
|
||||
isEditing = true
|
||||
editSessionDidChange?(true)
|
||||
}
|
||||
|
||||
/// The end of one Edit session — the flip back to Preview, raw-source entry, or the window
|
||||
/// closing. Flushes, and marks the boundary pro-m1's auto-commit coalesces on (see the type's
|
||||
/// doc comment).
|
||||
/// closing. Flushes, then registers the session's one undo step (see the type's doc comment).
|
||||
@discardableResult
|
||||
public func endEditSession() -> CardBodyWriteOutcome {
|
||||
let outcome = flush()
|
||||
@@ -216,14 +205,9 @@ public final class CardBodyEditSession {
|
||||
registerUndo?(origin, disk)
|
||||
}
|
||||
sessionOriginBody = nil
|
||||
// **Last**, after the flush and after the undo step: the committer's nudge must find the
|
||||
// session's final bytes already on disk, or the commit it arms would carry the file as it
|
||||
// stood one keystroke ago. Guarded on `isEditing` so a window closing from Preview — which
|
||||
// calls this too, and should — announces nothing.
|
||||
if isEditing {
|
||||
isEditing = false
|
||||
editSessionDidChange?(false)
|
||||
}
|
||||
// Guarded on `isEditing` for `beginEditSession`'s reason — a window closing from Preview
|
||||
// calls this too, and should, but has no session flag to move.
|
||||
if isEditing { isEditing = false }
|
||||
return outcome
|
||||
}
|
||||
|
||||
@@ -243,10 +227,7 @@ public final class CardBodyEditSession {
|
||||
cancelPending()
|
||||
text = disk
|
||||
sessionOriginBody = nil
|
||||
if isEditing {
|
||||
isEditing = false
|
||||
editSessionDidChange?(false)
|
||||
}
|
||||
if isEditing { isEditing = false }
|
||||
}
|
||||
|
||||
/// `DirtyBufferGuard`'s `attemptSave`: the same flush, with a real failure raised instead of
|
||||
|
||||
Reference in New Issue
Block a user