Proposal (rationale)
The disposable Arena Lab canary run by WI-090 on
2026-09-06 (producer 675780c7, consumer f4ede793) failed one gate on an untouched, freshly
scaffolded consumer: adr-index-current reported that docs/adr/README.md “lists 1 row(s) for 0
source file(s)”. The one row is | — | *none yet* | | |, the placeholder the adr module’s own
template ships. The file-index rule WI-087
implemented counts every pipe line that is not a separator as a data row, so every consumer with the
module installed and no decision yet fails its first check, through the installed CLI and after
ejection alike. The producer never saw it: its own repository has eleven decision records, and the
fresh-scaffold test asserts only the findings gate.
Decision
accepted — 2026-09-06 under WI-085, as work discovered by WI-090’s canary rather than a widening of
WI-090. A candidate that fails a fresh consumer’s untouched scaffold cannot be handed to the release
flow; the fix is small and bounded.
Plan
Requirements
- A
file-indexblock whose only row is the—placeholder counts zero rows. renderon zero sources emits the placeholder row rather than a header alone, so an untouched scaffold and its first render agree byte for byte, and a second render changes nothing.- The shipped
adrtemplate block is exactly whatrenderwrites for zero sources. - A self-test fixture covers the placeholder-only, zero-source pass direction.
Impacts
src/engines2.ts(renderFreshness),src/render.ts(renderDerivedBlocks),src/selftest.ts(index builder),modules/adr/files/{{path}}/README.md,modules/adr/gates/adr.toml,modules/adr/module.toml(1.2.0 → 1.2.1),test/core.test.js, the site claims snapshot if it records module versions.
Approach
Exclude rows whose first cell is — from the count; emit that row from render when there are no
sources; make the template match. Alternatives: teach the template to ship an empty table (loses the
readable “none yet” line every consumer sees first) or special-case *none yet* text (fragile; the
first-cell dash is the convention the board and findings register already use).
Acceptance criteria / tests
- The engine test seeds a placeholder-only index beside zero records and asserts
pass; a placeholder beside one record still fails;renderon zero sources writes the placeholder and a second render writes nothing. - The new
adr-index-currentfixture (placeholder = true, source_files = 0, expectpass) executes in the inventory with no unrun or error outcome. - A fresh
init trackedscaffold passescheckwith norenderin between. - The serial suite,
rungs check, and the site claims check pass.
Out of scope
- Any other gate the canary exercised; the eject summary wording that still says only
checksurvives ejection is WI-092, opened separately from the same canary. - Re-running the canary; WI-090 does that against the tree that includes this fix.
Execution
Branch feature/WI-091-index-placeholder-rows from 675780c7, 2026-09-06. One commit. As planned,
with one addition forced by the repository’s own gate: module-commands-exist refused the template
block once it carried render’s generated comment, because that comment named bare rungs render
inside a module file. Rather than exempt it, render now names the consumer’s own command
(node .ai/rungs.mjs render) in the comment it writes, and this repository’s
docs/decisions/README.md was re-rendered so its comment matches. Also found on the way and recorded
as a finding, not fixed here: check full means tier full through the ejected launcher and path
full through the CLI (F-063).
Review
- Engine test:
node --test --test-name-pattern '^the table rules the fixtures exposed' test/core.test.jspasses (1/1, 2026-09-06). It seeds the real template with{{path}}/{{id_prefix}}substituted, assertspassbeside zero records, assertsrenderDerivedBlockswrites nothing, adds one record and asserts the fail messagelists 0 row(s) for 1 source file(s), renders, assertspass, removes the record, and asserts the rendered index is the template again. - Inventory (
node .scratch/fixture-inventory.mjs, 2026-09-06): 163 fixtures, 161 ok, 2 unrun (the two nameddesign-mirror-not-editedfixtures, F-062), 0 mismatch, 0 error — the two new fixtures execute. - Fresh scaffold:
git init+node src/cli.ts init <dir> tracked+node src/cli.ts check <dir>with no render in between reportspass adr-index-current · 1 examined(2026-09-06). The scaffold’s one failure wasbacklog-merged-statusbecause that scratch repository had nomainyet — the producer’s fresh-scaffold test commits first and is unchanged. - Serial suite
NODE_OPTIONS=--max-old-space-size=2048 node --test --test-concurrency=1 test/*.test.js: 152 tests, 149 pass, 0 fail, 3 skipped, 179 s (2026-09-06).node src/cli.ts check: 31 pass. Claims snapshot regenerated: 31 gates (+1 hook), run 31 pass 0 fail.