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:
@@ -172,14 +172,6 @@ struct IntegrityValidationTests {
|
||||
|
||||
private func bytes(_ text: String) -> Data { Data(text.utf8) }
|
||||
|
||||
/// The per-kind field table: `order` is required on lanes and cards, **never** on the board.
|
||||
@Test("Order is required per kind")
|
||||
func orderIsRequiredPerKind() {
|
||||
#expect(!IntegrityRules.requiresOrder(.board))
|
||||
#expect(IntegrityRules.requiresOrder(.lane))
|
||||
#expect(IntegrityRules.requiresOrder(.card))
|
||||
}
|
||||
|
||||
@Test("A board index validates without an order")
|
||||
func boardValidatesWithoutOrder() throws {
|
||||
let document = try IntegrityRules.validateIndex(
|
||||
@@ -191,33 +183,71 @@ struct IntegrityValidationTests {
|
||||
#expect(document.title == .valid("Board"))
|
||||
}
|
||||
|
||||
@Test("A lane or card index without an order is refused")
|
||||
func laneAndCardRequireOrder() {
|
||||
/// **`order` is optional at every kind** (01-storage-format.md § Ordering, re-ruled 2026-07-31):
|
||||
/// a lane or card without one reads as append-at-end rather than being refused, so the validator
|
||||
/// has nothing to say about it.
|
||||
@Test("A lane or card index without an order validates")
|
||||
func laneAndCardDoNotRequireOrder() throws {
|
||||
for kind in [IntegrityRules.ObjectKind.lane, .card] {
|
||||
#expect(throws: BoardLoadError(path: "index.md", reason: .missingOrder)) {
|
||||
try IntegrityRules.validateIndex(
|
||||
bytes("---\nschema: 1\n---\nbody\n"),
|
||||
path: "index.md",
|
||||
kind: kind,
|
||||
supportedSchema: 1
|
||||
)
|
||||
}
|
||||
let document = try IntegrityRules.validateIndex(
|
||||
bytes("---\nschema: 1\n---\nbody\n"),
|
||||
path: "index.md",
|
||||
kind: kind,
|
||||
supportedSchema: 1
|
||||
)
|
||||
#expect(document.order.isMissing)
|
||||
}
|
||||
}
|
||||
|
||||
/// **The root's `schema` is required; below it, absence reads as 1** — the one per-kind
|
||||
/// difference the validator still draws.
|
||||
@Test("Schema is required at the board and optional below it")
|
||||
func schemaIsRequiredAtTheRootOnly() throws {
|
||||
let schemaless = bytes("---\ntitle: No Schema\n---\nbody\n")
|
||||
#expect(throws: BoardLoadError(path: "index.md", reason: .missingSchema)) {
|
||||
try IntegrityRules.validateIndex(
|
||||
schemaless, path: "index.md", kind: .board, supportedSchema: 1)
|
||||
}
|
||||
for kind in [IntegrityRules.ObjectKind.lane, .card, .comment] {
|
||||
let document = try IntegrityRules.validateIndex(
|
||||
schemaless, path: "index.md", kind: kind, supportedSchema: 1)
|
||||
#expect(document.schema.isMissing)
|
||||
}
|
||||
}
|
||||
|
||||
/// The card window's gate is this rule at `kind: .card` — one function, not a copy.
|
||||
@Test("validateCardIndex is validateIndex at card")
|
||||
func cardValidatorIsTheGeneralOne() {
|
||||
let missingOrder = bytes("---\nschema: 1\n---\nbody\n")
|
||||
#expect(throws: BoardLoadError(path: "index.md", reason: .missingOrder)) {
|
||||
try BoardLoader.validateCardIndex(missingOrder, path: "index.md")
|
||||
}
|
||||
func cardValidatorIsTheGeneralOne() throws {
|
||||
// No `order`, no `schema` — the minimum agent card, and a legal raw-source Apply since
|
||||
// 2026-07-31.
|
||||
let minimum = try BoardLoader.validateCardIndex(bytes("---\ntitle: Minimum\n---\nbody\n"), path: "index.md")
|
||||
#expect(minimum.title == .valid("Minimum"))
|
||||
|
||||
let newer = bytes("---\nschema: 99\norder: 1\n---\n")
|
||||
#expect(throws: BoardLoadError(path: "index.md", reason: .schemaNewerThanApp(found: 99))) {
|
||||
try BoardLoader.validateCardIndex(newer, path: "index.md")
|
||||
}
|
||||
}
|
||||
|
||||
/// The rulebook's own readings, without a filesystem in the way.
|
||||
@Test("The order reading is stated over usability")
|
||||
func orderReadingIsStatedOverUsability() throws {
|
||||
func reading(_ frontmatter: String) throws -> (order: Double?, coerced: CoercedField?) {
|
||||
IntegrityRules.resolvedOrder(in: try FrontmatterDocument.parse("---\n\(frontmatter)---\n"))
|
||||
}
|
||||
#expect(try reading("order: 1024\n").order == 1024)
|
||||
#expect(try reading("order: 1024\n").coerced == nil)
|
||||
|
||||
for (frontmatter, raw) in [
|
||||
("schema: 1\n", ""), ("order:\n", ""), ("order: null\n", "null"),
|
||||
("order: banana\n", "banana"), ("order: .nan\n", ".nan"), ("order: .inf\n", ".inf"),
|
||||
] {
|
||||
let read = try reading(frontmatter)
|
||||
#expect(read.order == nil, "\(frontmatter) should be unusable")
|
||||
#expect(read.coerced == CoercedField(key: "order", raw: raw), "\(frontmatter)")
|
||||
}
|
||||
}
|
||||
|
||||
/// The refuse-writes verdict's rule, named in the vocabulary rather than left as a property one
|
||||
/// call site happens to read.
|
||||
@Test("The uneditable shape is the document's, named here")
|
||||
@@ -275,6 +305,43 @@ struct IntegrityOnTouchTests {
|
||||
#expect(IntegrityRules.healOnTouch(&document, kind: nil).isEmpty)
|
||||
#expect(document.kind == .missing)
|
||||
}
|
||||
|
||||
/// **The rank materializes on touch** (01-storage-format.md § Ordering, re-ruled 2026-07-31):
|
||||
/// the value written is the append-at-end reading the board was already rendering, so nothing
|
||||
/// moves when the stamp lands.
|
||||
@Test("A missing order is stamped with the rank it read as")
|
||||
func missingOrderIsStamped() throws {
|
||||
var document = try FrontmatterDocument.parse("---\nschema: 1\ntitle: Minimum\n---\nbody\n")
|
||||
let heals = IntegrityRules.healOnTouch(&document, kind: .card, rank: 3072)
|
||||
#expect(heals == [.kindBackfilled(.card), .rankStamped(3072)])
|
||||
#expect(document.order == .valid(3072))
|
||||
#expect(document.keys == ["schema", "title", "kind", "order"])
|
||||
#expect(document.body == "body\n")
|
||||
}
|
||||
|
||||
/// **Unlike `kind`, an unusable *present* value is replaced**: a rank has to be a number for the
|
||||
/// midpoint math to mean anything, so `banana` and `.nan` heal exactly like an absent key.
|
||||
@Test("An unusable order is stamped too")
|
||||
func unusableOrderIsStamped() throws {
|
||||
for text in ["order: banana", "order: .nan", "order:"] {
|
||||
var document = try FrontmatterDocument.parse("---\nschema: 1\n\(text)\nkind: card\n---\n")
|
||||
#expect(IntegrityRules.healOnTouch(&document, kind: .card, rank: 2048) == [.rankStamped(2048)])
|
||||
#expect(document.order == .valid(2048), "\(text)")
|
||||
}
|
||||
}
|
||||
|
||||
/// A usable rank is never rewritten, and a caller with no rank to offer stamps nothing — the
|
||||
/// board root and a comment, which have no ladder to sit in.
|
||||
@Test("A present rank, and a nil rank, stamp nothing")
|
||||
func presentOrAbsentRankStampsNothing() throws {
|
||||
var ranked = try FrontmatterDocument.parse("---\nschema: 1\norder: 1024\nkind: card\n---\n")
|
||||
#expect(IntegrityRules.healOnTouch(&ranked, kind: .card, rank: 9999).isEmpty)
|
||||
#expect(ranked.order == .valid(1024))
|
||||
|
||||
var rankless = try FrontmatterDocument.parse("---\nschema: 1\nkind: board\n---\n")
|
||||
#expect(IntegrityRules.healOnTouch(&rankless, kind: .board, rank: nil).isEmpty)
|
||||
#expect(rankless.order == .missing)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - The defect vocabulary
|
||||
|
||||
Reference in New Issue
Block a user