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:
@@ -168,6 +168,27 @@ public enum BoardLoader: Sendable {
|
||||
// views over it (`LoadResult.looseCardFiles`, `.legacyTombstones`).
|
||||
var defects: [IntegrityRules.Defect] = []
|
||||
|
||||
// **The coerce tier's trace** (01-storage-format.md § Frontmatter, ruled 2026-07-29: "A
|
||||
// no-sensible-reading fallback logs: field, path, and raw text, carried as coerce-tier entries
|
||||
// in the integrity service's Defect stream"). Called once per document this walk parses, at
|
||||
// every level, because the rule is about *fields* and every level has them — and called here
|
||||
// rather than inside `readDocument` for the reason the whole defect stream lives in `load`: the
|
||||
// reading functions are pure and total, and the walk is what owns what it found.
|
||||
//
|
||||
// `logger.info`, not `warning`: the value rendered as its default, nothing is degraded, and the
|
||||
// line exists to be findable later rather than to be noticed now ("no banner, no behavior
|
||||
// change").
|
||||
func noteCoercions(in document: FrontmatterDocument, at path: String) {
|
||||
let fields = document.coercedFields
|
||||
guard !fields.isEmpty else { return }
|
||||
defects.append(.coercedFrontmatter(CoercedFrontmatter(path: path, fields: fields)))
|
||||
for field in fields {
|
||||
logger.info(
|
||||
"\(path, privacy: .public): '\(field.key, privacy: .public)' has no sensible reading — \(field.raw, privacy: .public) — rendering the field's default"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Detected before the walk, so a board whose `.trash` is squatted reports it even though
|
||||
// the trash read below finds nothing to parse. Read-only here, like every other detection:
|
||||
// the displacement is the store's, through the Writer (the Repair precedent).
|
||||
@@ -187,6 +208,8 @@ public enum BoardLoader: Sendable {
|
||||
warn(.boardLevelDeletedIgnored)
|
||||
}
|
||||
|
||||
noteCoercions(in: boardDocument, at: indexFileName)
|
||||
|
||||
// Every lane the walk read, in folder order — **not** `Lane` values yet. The board-wide
|
||||
// identity dedupe below decides which folders render at all, and a `Lane` is built only on
|
||||
// the far side of that decision, because a `Lane` carrying a withheld card would be exactly
|
||||
@@ -211,6 +234,7 @@ public enum BoardLoader: Sendable {
|
||||
let laneDocument = try readDocument(at: laneURL.appendingPathComponent(indexFileName), path: lanePath)
|
||||
let laneSchema = try validatedSchema(in: laneDocument, path: lanePath)
|
||||
let laneOrder = try validatedOrder(in: laneDocument, path: lanePath)
|
||||
noteCoercions(in: laneDocument, at: lanePath)
|
||||
|
||||
var cards: [Card] = []
|
||||
for cardURL in try directoryCandidates(in: laneURL) {
|
||||
@@ -226,6 +250,20 @@ public enum BoardLoader: Sendable {
|
||||
}
|
||||
|
||||
let card = try parseCard(at: cardURL, path: cardRelPath)
|
||||
noteCoercions(in: card.document, at: cardRelPath + "/" + indexFileName)
|
||||
|
||||
// **The card-level claimed name** (01-storage-format.md § Fractal layout ▸ Rules,
|
||||
// extended 2026-07-29 — "the rule is level-uniform"): a file or symlink wearing
|
||||
// `attachments` breaks every import into this card, every Finder drop on it and the
|
||||
// window's listing for as long as it stands, so it is scheduled work exactly as a
|
||||
// squatted `.trash` is. Detection is read-only here, like every other defect; the
|
||||
// displacement is the store's, through the Writer.
|
||||
for squatter in IntegrityRules.squattedClaimedNames(inCardAt: cardURL, path: cardRelPath) {
|
||||
defects.append(.claimedNameSquatted(squatter))
|
||||
logger.warning(
|
||||
"\(cardRelPath, privacy: .public)/\(squatter.name, privacy: .public): claimed name held by \(squatter.found.description, privacy: .public) — to be displaced"
|
||||
)
|
||||
}
|
||||
|
||||
// Noticed, never acted on: the relocation is the store's, through the Writer.
|
||||
let loose = looseFileNames(in: cardURL)
|
||||
@@ -287,6 +325,7 @@ public enum BoardLoader: Sendable {
|
||||
continue
|
||||
}
|
||||
let entry = try parseCard(at: cardURL, path: cardRelPath)
|
||||
noteCoercions(in: entry.document, at: cardRelPath + "/" + indexFileName)
|
||||
// **The trash's discriminator, applied where the flat container needs it**
|
||||
// (01-storage-format.md § Deletion, re-ruled 2026-07-29): the *value* is trusted
|
||||
// outright, and only an unrecognized value or no key at all falls through to shape.
|
||||
@@ -944,6 +983,17 @@ public struct LoadResult: Sendable {
|
||||
public var duplicateIdentities: [DuplicateIdentity] {
|
||||
defects.compactMap { if case let .duplicateIdentity(work) = $0 { work } else { nil } }
|
||||
}
|
||||
|
||||
/// Every file whose lenient fields fell back to their defaults — a **view over `defects`**
|
||||
/// (01-storage-format.md § Frontmatter, ruled 2026-07-29).
|
||||
///
|
||||
/// **Nothing consumes it, and that is the point**: the coerce tier changes no behavior, so this is
|
||||
/// the observability handle — a suite pins it, a developer reads the log lines it produced, and the
|
||||
/// day a shape shows up often enough to deserve a heuristic heal, this is where the evidence
|
||||
/// already is. In walk order: the board, then each lane and its cards, then the trash.
|
||||
public var coercedFrontmatter: [CoercedFrontmatter] {
|
||||
defects.compactMap { if case let .coercedFrontmatter(work) = $0 { work } else { nil } }
|
||||
}
|
||||
}
|
||||
|
||||
/// A tolerated anomaly the loader kept going past. Never blocks a load — see `BoardLoadError`
|
||||
|
||||
Reference in New Issue
Block a user