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 by 16ef377:

- 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:
2026-08-01 07:43:45 -04:00
parent 16ef3779e8
commit 274ccd9ff5
75 changed files with 5619 additions and 791 deletions
+50 -18
View File
@@ -76,8 +76,15 @@ enum AgentGuide {
/// defect was a missing second line, and the parser's complaint pointed at the *last* key it
/// swallowed rather than the title. The guide now teaches long values as one long line, says
/// why the wrapped shape is the dangerous one to copy, and names the unterminated quote beside
/// the unquoted colon in Hard rules.
static let version = 9
/// the unquoted colon in Hard rules. **v10 makes `order` and `schema` optional below the board
/// root** (01-storage-format.md § Frontmatter and § Ordering, re-ruled 2026-07-31): the guide's
/// whole point is that filing a card must need nothing but the schema
/// (08-agent-integration.md's masterplan requirement), and until now that was untrue a card
/// needed a rank, and a rank needed a scan of every sibling in the lane. The guide now teaches
/// the zero-read minimum (`mkdir` plus one `index.md`, no `order`, no `schema`; it lands at the
/// lane's bottom and the app stamps a real rank on its first touch) while still teaching
/// *writing* `order` as the way to control position, which is the only way to control it.
static let version = 10
// MARK: - The version marker
@@ -367,7 +374,8 @@ enum AgentGuide {
## Reading the board
- Lanes run left→right by ascending `order`; cards top→bottom by ascending
`order` within their lane. Ties break by folder name.
`order` within their lane. Ties break by folder name. An item with no
`order` sorts after every item that has one — see Creating a card.
- Lane titles carry the workflow semantics (e.g. To Do → In Progress →
Done). Read the board's and lanes' index.md bodies for descriptions and
per-lane policy before deciding where a card belongs.
@@ -376,13 +384,15 @@ enum AgentGuide {
## Frontmatter
All levels: `schema` (required, always `1`), `title` (optional — an item
without one renders as untitled, so give cards real titles), `created` and
`modified` (ISO-8601 with timezone, e.g. `2026-07-24T18:00:00Z`),
`background` (color), `icon` (SF Symbol name), `iconColor` (color, tints
`icon`). Lanes and cards additionally require `order` (a number; floats are
fine). Lanes may set `width` (integer ≥ 1, multiplier of the standard lane
width).
All levels: `schema` (always `1`; **required at the board's own `index.md`**,
optional below it — a lane or card without one is read as schema 1), `title`
(optional — an item without one renders as untitled, so give cards real
titles), `created` and `modified` (ISO-8601 with timezone, e.g.
`2026-07-24T18:00:00Z`), `background` (color), `icon` (SF Symbol name),
`iconColor` (color, tints `icon`). Lanes and cards may set `order` (a number;
floats are fine) — **optional, and the way to control position**: an item
without one goes last. Lanes may set `width` (integer ≥ 1, multiplier of the
standard lane width).
**Quote any `title` containing a colon** — `title: Fix: the thing` is
invalid YAML; write `title: "Fix: the thing"`. The same goes for any value
@@ -418,9 +428,7 @@ enum AgentGuide {
## Creating a card
1. Pick the lane folder. Compute `order`: bottom of the lane = max existing
card `order` + 1024; top = min 1024; between two cards = their
midpoint. (Empty lane: any number, conventionally 1024.)
1. Pick the lane folder.
2. Create a folder named a fresh lowercase UUID:
`id=$(uuidgen | tr 'A-Z' 'a-z')`.
3. Write `<lane>/$id/index.md` (timestamp: `date -u +%FT%TZ`):
@@ -438,8 +446,31 @@ enum AgentGuide {
The card's content — any Markdown.
```
**`order` is what places the card, and computing it means reading the
lane**: bottom of the lane = max existing card `order` + 1024; top =
min 1024; between two cards = their midpoint. (Empty lane: any number,
conventionally 1024.) Write it whenever the position matters.
**You can also file a card without reading the lane at all.** The minimum
legal card is a `mkdir` and one `index.md` containing nothing but a title —
no `order`, no `schema`:
```markdown
---
title: Short imperative card title
---
The card's content.
```
It lands at the bottom of the lane (an item with no `order` sorts after
every item that has one; two such items sort by folder name), and the app
writes a real `order` into it the next time it rewrites that file. Prefer
the full frontmatter above — `kind`, the timestamps and `modified-by` are
all worth having — but when you are filing into a 200-card lane and the
position doesn't matter, the short form costs one write and no reads.
Creating a lane is the same one level up (body optional; `kind: lane`;
`order` ranks lanes left→right).
`order` ranks lanes left→right, and is optional in the same way).
**Always write `kind`** at creation — `kind: card`, `kind: lane`,
`kind: board` at board root. Depth already says what an item is on the
@@ -508,10 +539,11 @@ enum AgentGuide {
## Hard rules (the app fails loudly on violations)
- Frontmatter must parse as YAML; `schema` (plus `order` on lanes and
cards) is required. Keep `schema: 1`. The classic violations are an
unquoted colon in a title and a quoted value left unclosed across a
line break (see Frontmatter above).
- Frontmatter must parse as YAML. The board's own `index.md` must carry
`schema: 1`; everywhere else `schema` and `order` are optional and a
missing one is read, never refused. Never write a `schema` other than
`1`. The classic violations are an unquoted colon in a title and a
quoted value left unclosed across a line break (see Frontmatter above).
- Files must be UTF-8 without BOM.
- Never create a card folder without an `index.md`.
- Never rename UUID folders.