Build the integrity service - IntegrityRules and the HealScheduler
The 2026-07-29 integrity design pass, consolidated (DESIGN/01 -
Validation and healing; DESIGN/02 - Components): IntegrityRules
(Storage, pure) is the one home for the identity predicate and
canonical form (BoardWriter.canonicalIdentity deleted, ItemID and the
loader forward to it), the per-field rulebook, uneditable shapes,
per-kind index validation, the reserved-name tables, and the trash
kind discriminator (values trusted - kind: lane/card explicit,
unrecognized falls to shape). LoadResult's ad-hoc channels fold into
one typed Defect stream (looseCardFiles / legacyTombstone /
claimedNameSquatted, per-defect heal signatures); the old accessors
survive as computed views.
HealScheduler (LiveStore) states the six-step heal pattern once -
resting-clear, lock gate, isWritableFile gate (now covering all four
heals), signature memo armed-before-attempt with explicit
clear-on-success, disk re-verify in each write half, one banner-posture
table (BannerCenter keeps all phrasing). The three hand-rolled healers
run on it with behavior preserved - including the
relocation-notice-despite-partial-failure quirk, deliberately. Heals
run at the reload tail AND at registry acquire, closing the
migration-never-fires-at-open asymmetry. Displacement runs first: a
squatted .trash would otherwise fail the migration and arm its memo
against an unchanged picture.
Claimed-name squatters (ruled today, 62c47a2) displace by the shared
Finder-style rename ladder - preserved verbatim, symlinks moved as
links, nothing stamped; AgentGuide's untouchable-skip upgrades to
displace-then-write, the CLAUDE.user.md-taken skip stands. kind stamps
on every create and backfills on any index rewrite via the on-touch
seam (placement resolver stamps nothing when the parent is unknown -
a guessed kind is worse than an absent one; board-root writers declare
theirs). Heal writes mark their EchoLedger receipts (inert in base;
pro-m1's committer will split them into their own commits). The
renumber ask-renumber-ask-again two-step is one shared helper, adopted
at all nine call sites.
69 tests added. 1738 green on both schemes.
Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -221,24 +221,25 @@ public final class BoardStoreRegistry {
|
||||
lastKnownRoot: rootURL
|
||||
)
|
||||
|
||||
// The loose-file carve-out's first firing (01-storage-format.md § Fractal layout ▸ Rules,
|
||||
// settled 2026-07-28): files an agent or a hand-editor left beside a card's `index.md`
|
||||
// while this board was closed are relocated into `attachments/` now, with the notice.
|
||||
// **The heal engine's open seam** (02-architecture.md ▸ Components ▸ HealScheduler: "fires
|
||||
// uniformly at the reload tail and at registry acquire"). Everything an agent or a
|
||||
// hand-editor left in this board while it was closed is healed now: loose files relocated
|
||||
// into `attachments/`, legacy `deleted:` keys migrated, a squatter moved off a claimed name,
|
||||
// the agent guide brought up to version — each with its own notice where it has one.
|
||||
//
|
||||
// **Here rather than in `BoardStore.init`**, and last rather than first: the store's own
|
||||
// init is one tree walk and no writes, and a relocation written before the brackets and the
|
||||
// init is one tree walk and no writes, and a heal written before the brackets and the
|
||||
// watcher exist would be a write nothing is watching — landing on disk with the snapshot
|
||||
// above it left one reload stale. By this line the pair is wired, so it is an ordinary
|
||||
// bracketed app write whose echo reload refreshes the board like any other. Every reload
|
||||
// thereafter re-fires it from `BoardStore.land`; this call is only the one the opening walk
|
||||
// would otherwise have no reload behind.
|
||||
store.relocateLooseCardFiles()
|
||||
// The agent guide's first firing (08-agent-integration.md ▸ The agent guide), here for the
|
||||
// same reason and with the same timing as the relocation above: a board opened with no
|
||||
// `CLAUDE.md`, or with one an older version wrote, gets the current guide now — as a
|
||||
// bracketed app write with a reload behind it, never as a write into a board nothing is
|
||||
// watching. Every reload thereafter re-checks it from `BoardStore.land`.
|
||||
store.refreshAgentGuide()
|
||||
// above it left one reload stale. By this line the pair is wired, so each heal is an
|
||||
// ordinary bracketed app write whose echo reload refreshes the board like any other. Every
|
||||
// reload thereafter re-fires them from `BoardStore.land`; this call is only the one the
|
||||
// opening walk would otherwise have no reload behind.
|
||||
//
|
||||
// **One call, all four** — before 2026-07-29 this seam named two of the three healers by
|
||||
// hand, which is how the legacy-tombstone migration came to be the one heal that never fired
|
||||
// at open. A board opened, migrated nothing, and waited for an unrelated filesystem event to
|
||||
// do what opening should have done.
|
||||
store.runScheduledHeals()
|
||||
return store
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user