Task files, the files they copy in (byte-identical to the reference on m3a-ref), each area's check record, and a README with the per-task table of what each check exposed. The handoff note is done with. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
5.2 KiB
M3a task 21: the runbook gate check
Branch: m3a (run git switch m3a; git status --short must be empty, otherwise stop)
Commit subject: Check that every runbook pointer has an entry
Goal
Every message for a fail-closed state ends with see docs/runbook.md#<anchor>. A new gate script
fails if the code names an entry that docs/runbook.md does not have.
Files
- Copy:
scripts/test-gate-scripts.sh, andMakefilefromdocs/plans/M3a/files/Makefile-task21(files/Makefileis task 22's, with a line that needs a test this task does not have yet) - Create:
scripts/check-runbook.sh - Modify:
docs/implementer-log.md
You never edit docs/runbook.md. If the script finds a missing entry in the real tree, the
pointer in the code is wrong, or the entry is missing: stop and report which.
What the script does
sh scripts/check-runbook.sh [ROOT], ROOT defaulting to ., in POSIX sh like the other
scripts in scripts/. Read scripts/check-lines.sh first for the style.
- A pointer is the text
docs/runbook.md#followed by zero or more characters of[A-Za-z0-9_-], anywhere in a*.rsfile underROOT/crates. Files under anytarget/directory are ignored (find ... -type d -name target -prune -o -type f -name '*.rs' -print). Test files count. A line can hold more than one pointer, and each counts. - The pointer's anchor is what follows the
#. Its entry is a line ofROOT/docs/runbook.mdthat is exactly## <anchor>: the whole line, nothing before or after it (grep -q -x -F -e "## $anchor").### grants-invalid,## grants-invalid and more, a mention in prose and## Grants-Invalidare not the entry forgrants-invalid. - Exit status 0 if every anchor has its entry. Otherwise 1.
Every way it can fail, each with a message on stderr that starts with check-runbook::
| Condition | Why |
|---|---|
ROOT/crates is not a directory |
nothing to check: fail closed |
ROOT/docs/runbook.md is not a file |
nothing to check against |
find, awk, sort or mktemp fails, or a file cannot be read |
a check that cannot do its job fails |
grep exits with a status other than 0 or 1 |
1 means "no such line"; 2 means grep itself failed |
| no pointer is found anywhere | the harness has fail-closed states, so the search is broken |
an anchor is empty, as in docs/runbook.md#{anchor} or docs/runbook.md#<anchor> |
the script cannot read an anchor that is not written out |
| an anchor has no entry | the point of the script |
Rules from earlier reviews, which the self-test checks:
- Report every problem, not only the first. Go through all the anchors, print a message for
each one that is missing, and exit 1 at the end. Do not
exitinside the loop over anchors. For each missing anchor, name the files that point to it. - Never hide an error. No
2>/dev/null, no|| true. Test each command's exit status. - A
while readloop at the end of a pipeline runs in a subshell, and anexitor a variable set inside it is lost. Write the list to a file made withmktemp -dand read it withwhile IFS= read -r line; do ...; done < "$file". Remove the directory with atrap ... EXIT. grep -ois not POSIX. Take the pointers out of a line withawk:
awk '{
s = $0
while (match(s, /docs\/runbook\.md#[A-Za-z0-9_-]*/)) {
printf "%s\t%s\n", substr(s, RSTART + 16, RLENGTH - 16), FILENAME
s = substr(s, RSTART + RLENGTH)
}
}' "$f"
docs/runbook.md# is 16 characters, so this prints the anchor, a tab, and the file.
The Makefile
The copied Makefile runs sh scripts/check-runbook.sh in gate, after check-dep-docs.sh.
Steps
- 1. Copy.
git switch m3a, thencp docs/plans/M3a/files/scripts/test-gate-scripts.sh scripts/ && cp docs/plans/M3a/files/Makefile-task21 Makefile - 2. See the self-test fail.
sh scripts/test-gate-scripts.sh. Expected: failures that namecheck-runbook.shcases, and exit status 1. - 3. Write
scripts/check-runbook.sh. - 4. See the self-test pass.
sh scripts/test-gate-scripts.sh. Expected:test-gate-scripts: ok. - 5. Prove the self-test has teeth. Change
-xto nothing in yourgrepline and run the self-test: it must fail with "the entry is the whole line, at level two". Change it back. Then putexit 1where a missing anchor is reported and run it: it must fail with "both missing entries and their files are reported". Change it back. Write both results in the log's Notes. - 6. Run it on the real tree.
sh scripts/check-runbook.sh; echo $?. Expected: no output and0. If it names a missing entry, stop and report: do not edit the runbook, the pointer or a copied test. - 7. Run the gate.
make gate. Expected last line:gate: ok. - 8. Log and commit.
git add scripts/check-runbook.sh scripts/test-gate-scripts.sh Makefile docs/implementer-log.md && git commit
Done when
sh scripts/test-gate-scripts.shprintstest-gate-scripts: ok;sh scripts/check-runbook.shexits 0 on the repository;make gateprintsgate: ok.
Stop and report if
- Step 6 reports a missing entry or an empty anchor.
shon this machine lacks something the task relies on (mktemp -d,awk'smatch).