Plan: split the generated compliance tables out of the audit specifications¶
Context¶
docs/audits/ holds 35 criterion specifications plus an index. Each
specification is hand-written prose -- what we check, why the rule
exists, what it deliberately does not cover, which template
implements it -- and 34 of the 35 also carry a machine-regenerated
per-project compliance table between <!-- consistency-audit:begin
--> and <!-- consistency-audit:end --> markers. test-coverage.md
is the exception: its criterion is delegated to the pre-push review
and has no check.
The generated block opens with a line that moves on every run:
That timestamp is deliberate. It is how a reader tells a current
verdict from a stale one when the audit has silently stopped running,
and .github/workflows/consistency-audit.yml files an issue on a
failed run saying exactly that: "the tables still show the previous
run's verdicts, so the audit looks healthy from the outside".
But a whole-file review mark attests to file content by blob SHA, and
a file carrying a line that changes daily can never hold one. So
.vscode/review-scope.toml excludes the whole directory bar the
index:
exclude = [
'docs/plans/*',
'PLAN-*.md', # unmatched-by-design: a guard against plans returning to the root
'docs/audits/*',
'!docs/audits/README.md',
]
The cost of that exclusion, measured:
| Lines | |
|---|---|
| The 35 specification files | 3,466 |
Inside consistency-audit markers (generated) |
947 |
| Hand-written prose | 2,519 |
27% of the directory is generated, and it is why the other 73% has
never been reviewable. Those 2,519 lines are the statement of what
the fleet is held to -- the most consequential prose in the
repository after docs/audits/README.md and
docs/consistency-audits.md -- and human review cannot see any of
it. Mikal found this while trying to review it.
The exclusion was the right call when it was written; the comment
above it reasons the trade-off out at length and correctly concludes
that including the files as they stand would put permanently-stale
entries in the queue and hold a review-coverage issue open that no
amount of reviewing could close. The mistake is not the exclusion. It
is that two artifacts with opposite lifecycles -- prose that changes
when someone decides something, and a table that changes every
morning -- were put in one file, so the file inherits the churn of
its worst part.
What "good" looks like¶
- Every criterion specification is hand-written from the first line to the last, changes only when a human changes it, and can hold a review mark.
- Per-project compliance is still published on shakenfist.com, still regenerated every morning, still carries the staleness timestamp.
- The daily bot commit touches one file, not 27.
- Nothing in the fleet has to learn a new format or a new location it cannot discover from the page it is already reading.
Decisions¶
D1. Rendered markdown, not a JSON sidecar¶
The obvious move -- and the one that prompted this plan -- is to put
each table in docs/audits/<check-id>.json beside its markdown.
Rejected, for one reason.
docs/audits/README.md justifies the directory's location this way:
It sits under
docs/so that it publishes to shakenfist.com with everything else. What we hold a project to is documentation, and a criterion nobody outside the fleet can read is a criterion nobody outside the fleet can meet.
A JSON file renders as nothing. Today a reader of the published
export-repo-config page sees which projects comply and which issue
tracks each failure; with the data in JSON, either the compliance
information leaves the published documentation entirely, or the
website repository grows a renderer for it. The website is
excluded from the consistency audits, so that would put a schema
contract across a repository boundary with nothing checking either
side of it -- and the format would then be load-bearing for
publishing rather than an implementation detail.
The generated output stays markdown. What changes is which file it lives in.
There is no loss here. The machine-readable form already exists:
every run uploads audit-result-<repo>.json per repository as a
workflow artifact, and audit-manage-issues.py consumes those
directly. A committed JSON copy would be a third representation of
the same data with no reader. If one is ever wanted, see Future work.
D2. One page, not 34 generated files¶
docs/audits/compliance.md, one page, one section per specification.
The alternative -- 34 generated markdown files under
docs/audits/generated/ -- preserves one-page-per-criterion and
needs a single exclusion glob, but it does not fix the churn: the bot
would still rewrite 27 files most mornings, which is what makes the
git log of this repository hard to read.
One page also has a per-run timestamp problem that resolves in its
favour. All 34 blocks currently carry the same timestamp -- 34
copies of one string, because render_section takes the maximum
timestamp across the whole result set and every spec sees the same
set. One page needs one note at the top, and nothing is lost.
The cost is one click from a specification to its compliance table when you want both. Accepted, in exchange for a page that answers "what is the fleet failing right now" in one place, which nothing answers today.
D3. What replaces the marker block in a specification¶
The ## Projects section stays, and becomes a static line:
## Projects
Per-project compliance for this criterion is regenerated every
morning by the consistency audit:
[compliance.md#export-repo-config](/components/development/plans/compliance/#export-repo-config).
Static, hand-written, reviewable, and it keeps the specification a complete answer to "where do I find out who is failing this". The anchor is the specification's basename, so it is derivable from the filename and a test can assert every one of them resolves.
D4. The new tell for "this criterion has no check"¶
docs/consistency-audits.md currently says marker-block absence is
how to find criteria with no automated check:
A criterion with no check has no
consistency-auditmarker block in its spec file, which is how to find the current set -- at the time of writing,test-coverage.
With the markers gone from every specification that tell dies, and it has to be replaced rather than dropped: it is the only thing in the documentation that distinguishes "this rule is measured" from "this rule is written down and judged by a human".
The replacement is better than what it replaces, because it is
visible to a reader rather than requiring a grep.
compliance.md grows a closing section naming the criteria that have
no check and why, and a specification with no check says so where its
compliance link would be. AUDIT_METADATA in
scripts/audit_common.py remains the machine-readable source of
truth -- it always was -- and a test asserts the three agree:
every spec in AUDIT_METADATA carries a compliance link and has a
section on the page, and every spec that is not in AUDIT_METADATA
carries neither and is named in the no-check section.
D5. The specifications come into scope, and the coverage number moves¶
Measured on this branch by editing the scope file and running
scripts/review-tracking.py status:
| In scope | Reviewed | Needing review | |
|---|---|---|---|
| Today | 77 | 14 | 63 |
| After | 112 | 10 | 102 |
The review-coverage threshold is 5 and development is already
non-compliant against it (shakenfist/development#45, open). So this
files no new issue and changes no cell in any compliance table -- only
the count in the body of an issue that is already open. The backlog
this creates is the backlog that already existed and was not being
counted.
Outcome: 10 of 112, 102 needing review, four worse than predicted, because the prediction only counted files entering scope and not the marks this work would invalidate on the way. Three were pruned, each for the same reason -- the branch edited a file somebody had reviewed, and a mark attests to exact content:
| Pruned mark | Edited by |
|---|---|
.github/workflows/consistency-audit.yml |
Phase 1, the update-docs job's comments |
.github/workflows/prune-reviews.yml |
Phase 3, one comment line |
.claude/skills/standards-alignment/SKILL.md |
The push-audit fixes |
The fourth is not a prune: rebasing onto main late in the work
brought in two upstream prunes of its own, which moved the count again.
That is the tracking working rather than a miscount, and it is worth
predicting next time -- a change that sweeps wording across a
repository under review invalidates marks in proportion to how many
files it touches.
The new exclude list:
exclude = [
'docs/plans/*',
'PLAN-*.md', # unmatched-by-design: a guard against plans returning to the root
'docs/audits/compliance.md',
]
!docs/audits/README.md goes away with the glob that needed it, and
the negation support in in_scope() stays -- it is general
machinery, not scaffolding for this one case.
Implementation¶
Work happens in a worktree off shakenfist/development; this plan
file lands with the change (per CLAUDE.md).
Execution¶
Phases are the sections below rather than separate files, following this repository's convention.
| Phase | Status | Merged |
|---|---|---|
| 1. Split the generated output out | Complete | |
| 2. Bring the specifications into review scope | Complete | |
| 3. Documentation and runbooks | Complete | |
| 4. Push audit | Complete |
The Merged column stays empty until the branch lands; all four
phases ship as one pull request rather than one each, because phase 2
cannot be reviewed without phase 1 in the tree and phase 3 corrects
prose that phase 1 makes wrong.
Phases 1 and 2 have a hard ordering dependency:
test_review_tracking.py asserts that every pattern in
review-scope.toml matches a path that exists unless it carries an
unmatched-by-design comment, so docs/audits/compliance.md has to
be committed before it can be named in the exclude list.
Phase 1 is one commit and cannot be smaller. The daily workflow rewrites whatever the code tells it to rewrite, so the code change, the removal of the marker blocks from 34 specifications, and the new page have to land together or the next 06:00 UTC run either reverts half of it or leaves 34 files carrying a stale table nothing updates.
1. Split the generated output out¶
-
docs/audits/compliance.md(new). One page. A short hand-written preamble explaining what it is and that it is generated, then a single generated block between the existing markers containing: -
the
*Generated <timestamp> from scripts/audit-check.py; do not edit.*note, once, at the top; - one
## <spec-basename>section per spec inAUDIT_METADATA, in the orderchecks_by_spec()yields sorted, each holding a link back to its specification and the tablerender_sectionrenders today, unchanged in format; - a closing section naming the criteria with no automated check.
The preamble sits outside the markers so it survives regeneration, the same way every specification's prose does today.
The initial content is a faithful transcription of the 34 blocks
currently in the specification files, not a fresh audit run.
Generating it properly needs 17 repository clones and a gh token;
transcription is exact, verifiable by diffing the moved blocks
against what was removed, and it is replaced by real output at the
next 06:00 UTC run anyway. A one-shot migration script does the
move; it is not committed.
scripts/audit-update-docs.py.render_sectionkeeps its table-rendering logic and loses its per-spec framing: the markers, and the timestamp note, move up to a new page-level renderer.update_spec_filebecomesupdate_compliance_pageand writes one file.mainstops iterating over spec files, so themissing_markerserror path -- which exists to catch a spec whose markers were deleted -- collapses to a single check on the one page.
Keep the column_name fallback and its warning exactly as they
are. The comment on it records that one missing heading once
stopped every project's table from publishing, and consolidating to
one page raises that stake rather than lowering it: one page is one
write, so a failure now takes out the whole fleet's compliance
output in a single file rather than 34.
-
scripts/audit_common.py.AUDIT_METADATA[*]['spec']still names the specification file -- it is what the compliance page links back to, what issue bodies point at, and whattest_audit_update_docs.pychecks the existence of. The markers stay where they are defined; only their number of uses changes. -
scripts/commit-audit-docs.sh. Narrow the diff check, thegit addand the commit message fromdocs/audits/todocs/audits/compliance.md. This is a safety improvement beyond the tidying: today the bot runsgit add docs/audits/on a checkout, so any other change present in that tree would be committed tomainunder the bot's name and the message "Regenerate audit compliance tables." -
The 34 specification files. Marker block out, static compliance link in, per D3.
-
docs/audits/test-coverage.md. Already explains at length why it has no table. Reword the "There is no per-repository table on this page" sentence so it reads correctly now that no specification has one, and point at the no-check section of the compliance page. -
scripts/test_audit_update_docs.py. The existing tests keep their intent and change their target: -
test_every_generated_block_opens_with_the_current_note-- currently walks every spec, asserts the note format, and requires it checked more than 20 files so that a pass on nothing is impossible. Retarget to the single page; keep the guard by asserting the page's section count equalslen(checks_by_spec()), which is the same protection against a silent pass on nothing. test_every_spec_file_is_named_in_the_index-- unchanged, andcompliance.mdmust not trip it. It lists every*.mdbarREADME.mdand requires each to be linked from the index, so the new page is either excluded alongsideREADME.mdor linked from the index. Link it: it should be discoverable there anyway.- New: every spec in
AUDIT_METADATAcarries a compliance link whose anchor matches its basename and has a matching section on the page; every spec file not inAUDIT_METADATAcarries no such link and is named in the no-check section. This is what keeps D4's tell honest. - New: no specification file contains a
consistency-auditmarker. Cheap, and it catches the reintroduction this plan exists to prevent.
2. Bring the specifications into review scope¶
.vscode/review-scope.toml. The exclude list per D5. The long prose comment above it is the substance of this phase, not an afterthought: most of it argues for an exclusion that no longer applies, and it should be rewritten to record what the directory now looks like and why one generated page is excluded -- keeping the paragraph about why the timestamp exists, which is the part that stays true and the part a future reader will otherwise re-derive.
Delete the paragraph about audits/* having silently matched
nothing after the tree moved under docs/, and the one about
test-coverage being left excluded rather than named as an
exception. Both describe a bookkeeping problem that this change
removes.
-
REVIEWS.md. Regenerated, not hand-edited. Expect112 in-scope filesand no change to the reviewed list. -
Confirm
scripts/review-tracking.py statusreports 112 files in scope and thatcompliance.mdis not among them. The reviewed count is not fixed in advance: it falls as later phases edit files that carry marks. It ended at 10 of 112 -- see D5.
3. Documentation and runbooks¶
-
docs/audits/README.md. The "File structure" section shows a specification's skeleton including the markers; replace the marker block with the static compliance link. Addcompliance.mdto the audit index, or to the prose above it, so it is discoverable from the directory's front page. The opening paragraph says each file "carr[ies] a per-project compliance table regenerated every morning" -- reword. -
docs/consistency-audits.md. The "two layers" table describes the specification layer as holding "a generated per-project compliance table"; that becomes a link, and the generated page is arguably a third row. Replace the marker-absence tell per D4. Check the "how to add a criterion" list, which the README says touches five files: adding a criterion no longer means adding a marker block, and the count may change. -
PUSH-AUDIT.md. The wave 1 grep for hand-edited compliance tables is keyed ondocs/audits/*.mdand matches marker lines and status rows. Retarget it: a status row appearing in a specification is now the defect worth grepping for, and an edit tocompliance.mdis the other. Both are worth a line. -
ARCHITECTURE.mdandAGENTS.md. Check only. The component inventory does not change and no convention changes, so the expected outcome is no edit -- butdocs/audits/is prominent enough in both that a stale sentence about tables living in the specifications is likely, and a grep forconsistency-auditand fordocs/auditsacross the repository is the cheap way to find every remaining one.
Outcome: both needed an edit, and the grep found eleven more
files. ARCHITECTURE.md described every spec as carrying a table;
AGENTS.md said the tables sit in docs/audits/*.md. The rest were
one-line wording fixes plus four pointers that named a spec file as
the place to read fleet state, which now name the compliance page
and its anchor: README.md, docs/automated-pr-review.md,
docs/ci-review-automation.md and
templates/ci-review-automation/README.md.
blank_generated_blocks in scripts/audit-check.py was the one
worth reading rather than editing. It blanks generated blocks before
the docs-external-links and plan-phase-references checks scan
this repository's own markdown, so harvested detail strings from
other repositories are not judged as our prose. It still does
exactly that, over one file instead of thirty-four; only its
docstring needed the correction.
One inconsistency turned up that predates this work.
docs/audits/README.md said a new criterion touches "five files
(six if it shares a spec file)", while the numbered list in
docs/consistency-audits.md gives four plus a conditional fifth.
The list is right, so the README now says four. Neither count
changes as a result of the split: a criterion still gets one spec
file, and it is the compliance page that gains a section
automatically.
- Run
pre-commit run --all-files-- actionlint, shellcheck, flake8, skillsaw and the four Python suites -- which is the whole of lint and test here.
4. Push audit¶
Run PUSH-AUDIT.md over the accumulated diff of the first three
phases against main. Findings land as their own pull request; the
plan is not complete until each is resolved or declined in writing,
with the reason recorded here. If the audit finds nothing, say so in
one sentence.
The interesting brief for wave 2 here is the documentation one: this change edits 34 specification files mechanically and rewrites prose in five more, and a mechanical edit repeated 34 times is exactly where a wrong anchor or a dropped section heading hides.
Outcome. Wave 1 passed: pre-commit clean at the time, all seven
greps empty. One correction to the runbook is owed and is recorded
under Future work -- every diff command in PUSH-AUDIT.md is written
against main...HEAD, and a stale local main silently widens the
diff to unrelated history. The first run of the greps here reported
hits from commits that were already on origin/main.
Wave 2 found no critical or high security findings and six things worth fixing. All six are fixed on this branch rather than deferred, because five of them were introduced by phases 1 to 3 and the sixth is a two-line change to code this plan already touches.
2c-1 / 2d-1: a stale review attestation, shipped. Phase 3 edited
.github/workflows/prune-reviews.yml, which carried a review mark, and
no prune followed -- so REVIEWS.md published it as reviewed at
content nobody had read, and test_every_stamp_matches_the_content_it_
attests_to failed. Pruned, and REVIEWS.md regenerated.
Worth recording why this got past the pre-commit run at the end of
phase 3, because it will happen again. That test compares the stamp
against blob_sha(':<path>'), which reads the git index. The file
was edited but not staged, so the test compared the stamp against the
old staged blob and passed; git add moved the new content into the
index and the same test then failed. Running pre-commit
run --all-files on an unstaged tree gives a false pass for exactly
the check that guards the attestations.
2c-2: a file the phase 3 sweep missed.
.claude/skills/standards-alignment/SKILL.md said "The per-audit
status tables in docs/audits/*.md regenerate daily". The sweep
grepped for "compliance table"; this file says "status tables". Fixed.
2c-3: the index row. docs/plans/index.md registered this plan as
Not started while its own Execution table had three phases
Complete. plan-index checks the vocabulary, not the accuracy, so
CI passed on a visibly wrong row. Fixed.
2b-1: the new link test broke the documented "Adding a criterion"
recipe. test_measured_specs_link_a_section_that_exists required a
spec's anchor to already have a section on compliance.md. But the
page is generated daily and lags the specs by one run, so a criterion
registered today has no section until tomorrow -- which is precisely
what step 3 of that recipe produces. The test would have failed the
first commit of every future criterion pull request, and the class it
replaced had explicitly tolerated the transitional state.
Fixed by deleting the wrong half of the assertion rather than adding a
skip. The anchor must match the spec's own basename, which is the typo
this can actually catch; that the section will exist is guaranteed by
test_the_page_has_a_section_for_every_spec, which asserts against the
renderer instead of against yesterday's file. test_the_page_names_
every_unmeasured_spec had the same coupling and moved to the renderer
for the same reason. Verified by deleting a section from the page and
confirming the suite still passes.
2d-2: a harvested detail string could restructure the page.
Detail strings are written out of what a check found in an audited
repository and rendered as bare prose. update_compliance_page found
the end marker with a substring search, so a detail carrying that
marker truncated the next run's splice -- leaving that run's tables
outside the block, where every later run preserved them again. The
page grows without bound, publishing stale verdicts that
blank_generated_blocks no longer exempts from this repository's own
docs-external-links and plan-phase-references checks. A merged
workflow file named for the marker is enough to trigger it, so it
takes commit access to a fleet repository.
Pre-existing, and the consolidation escalated it from corrupting one
spec's block to corrupting the single page carrying all 34 tables.
Fixed at both ends: defuse() collapses newlines and neutralises the
HTML comment opener before interpolation, and the splice is now
anchored to whole lines the way blank_generated_blocks already was,
so it does not depend on the defusing having worked. Newline collapsing
also fixes the accidental route, a subprocess traceback reaching a
detail string.
2d-3: the documented local-test recipe destroyed hand-written work.
docs/consistency-audits.md told the reader to clean up with git
restore docs/audits/. That was safe when the directory was generated;
it now throws away edits to any of the 35 hand-written specifications.
The recipe uses --page /tmp/compliance.md instead.
2a-1 and 2d-3 disagreed, and the disagreement was productive. 2a
called the new --page flag dead, untested surface whose value was
decoupled from AUDITS_DIR -- so unmeasured_specs() would describe
the real docs/audits/ even when writing elsewhere. 2d found the
recipe above, for which --page is exactly the right answer. Keeping
it satisfies both: the flag now has a documented caller and CLI
coverage, and is named in the module's usage block.
The decoupling 2a complained about turned out to be correct, and "fixing" it was a mistake -- see the review round below.
Coverage added, per 2b and 2d: an isolated unit test of
unmeasured_specs() against a fabricated directory, which nothing
pinned before -- all three callers treated its output as ground truth;
update_compliance_page() and main(), which had no coverage on
either side of the split and are the only path that writes the file
the privileged bot commits, including the missing-marker and
markers-out-of-order guards; and defuse(). The suite goes from 20
tests to 33.
Declined, with reasons. render_table's unknown cell,
load_results on an empty directory, and render_page's
zero-unmeasured branch are untested. All three predate this change and
none is touched by it; naming them here is the functional-test-coverage
block's instruction rather than a reason to widen the change. Two
informational findings about harvested detail strings reaching other
sinks are recorded under Future work instead of fixed, because they are
outside this page.
The review round on pull request #57¶
The automated reviewer raised four fix items, two consider and two
informational. All eight are addressed; the two consider items were
taken because one is a defect this branch introduced and the other is
its comment.
The interesting one is that it caught a fix that made things worse.
Responding to 2a-1 above, render_page was changed to derive the
audits directory from the --page path, so that a redirected page
would be "self-consistent". The reviewer ran the recipe this branch
documents, --page /tmp/compliance.md, and found it publishing two
unrelated scratch files as criteria nobody measures; in a clean output
directory the section vanishes instead. Which criteria have no check is
a property of this repository, not of wherever the output happens to
go, so the scan is anchored back at AUDITS_DIR and a test renders
into a directory seeded with unrelated .md files to pin it. Nothing
reached the published page -- the workflow never passes --page -- but
it defeated the mitigation this plan names for the risky unattended
06:00 run, which is to render locally and diff before landing.
The other three fix items were documentation the phase 3 sweep
missed: two specs (python-version, security-sanitization) kept a
sentence introducing "the table below" with no table below it, which
was the only thing telling a reader that just one of the standards on
each page is measured; a second stale pointer in
templates/ci-review-automation/README.md, in a section that did
predate the sweep; and a comment in scripts/audit_common.py whose
edit dropped the noun its preposition governed. Plus the stale numbers
in D5, corrected above.
Risks and mitigations¶
-
The next daily run publishes nothing, or publishes to the wrong place. The real risk of the phase. The
update-docsjob runs at 06:00 UTC unattended and pushes tomain; a mistake is discovered by a missing table, not by a red run. Mitigation: runaudit-update-docs.py --no-issuesagainst a saved results directory before the phase lands and diff the output, and watch the first real run rather than assuming it. -
The website does not render the anchors. The compliance links are the whole of the connection between a specification and its table, and they are markdown heading anchors. Mitigation: check one published page after the first deploy. If the site generator does not emit heading anchors, the links still resolve to the page and degrade to a scroll, which is the acceptable failure.
-
The 35 files arrive in the review queue and nothing reviews them. The exclusion at least was honest about not covering them; a queue entry that sits for a year is worse than an exclusion, because it makes the coverage number a number nobody acts on. Mitigation: none in tooling --
review-coveragealready files the nudge anddevelopment#45is already open. This is the phase making a real backlog visible, which is what was asked for. -
A specification regrows a table. An agent picking up an audit issue, or a future version of this repository's own tooling, adds one back. Mitigation: the phase 1 test asserting no specification contains a marker, plus the retargeted
PUSH-AUDIT.mdgrep.
Definition of done¶
- No file in
docs/audits/other thancompliance.mdcontains aconsistency-auditmarker, and every specification links to its section on the compliance page. compliance.mdcarries one timestamp note, a section for each of the 34 specifications with a check, and a section naming the ones without..vscode/review-scope.tomlexcludesdocs/audits/compliance.mdand nothing else in that directory;review-tracking.py statusreports 112 in scope.pre-commit run --all-filespasses.- One 06:00 UTC run has completed after the change, its commit
touched only
docs/audits/compliance.md, and the published page on shakenfist.com shows the tables with working anchors from at least one specification. - Phase 4's findings are resolved or declined in writing.
Future work¶
-
compliance.json. If something ever wants the fleet-wide verdicts machine-readably from a checkout rather than from workflow artifacts,audit-update-docs.pycan emit it alongside the page from the same in-memory results, excluded from review by the same pattern extended todocs/audits/compliance.*. Not now: it would have no reader, and an unread generated file drifts. -
Reviewing the 35 specifications. The point of the change, and not part of it. The first session on them is where we find out whether the prose in a directory nobody could review has the problems that suggests.
-
The same shape elsewhere in the fleet. If any other repository has generated content interleaved with reviewable prose, it has this problem too. Worth a look during the next review session rather than a check of its own -- one instance is not a pattern.
-
PUSH-AUDIT.mdis written againstmain...HEAD. Every diff command in it names the localmain, which is whatever was last fetched into the working clone. A stalemainsilently widens the audit's diff to unrelated history, which is what happened on the first run of wave 1 here. It should beorigin/main...HEAD, or the runbook should open by asserting the two agree. This is a change to the runbook rather than to this plan's subject, and it affects the eight repositories that carry one, so it belongs in its own change. -
Detail strings reach two sinks this change does not cover.
defuse()protects the compliance page. The same unsanitised strings go into issue bodies filed fleet-wide withAUDIT_TOKENbyaudit-manage-issues.py, where an@mentionharvested out of an audited repository notifies a real person; andcheck_review_coveragecan put a multi-line subprocess traceback into a detail string. Both predate this change and neither is on the page, so both are left alone here. The issue-body one is the more interesting of the two and should probably reusedefuse().
Back brief¶
Before phase 1 begins, the management session confirms with Mikal: the single-page layout and its anchor scheme (D2, D3), and the wording that replaces the marker-absence tell (D4). Both are cheap to propose now and expensive to redo across 39 files.