Plan: shared blocks for PLAN-TEMPLATE.md, and a managed sub-agent model roster¶
Context¶
This section describes the fleet as it stood in August 2026, when the plan was written. Twelve repositories carry a template today; see Verification.
Ten repositories carry a PLAN-TEMPLATE.md. Nothing audits any of
them, and they have drifted into three states:
| State | Repositories |
|---|---|
Full ## Agent guidance section |
shakenfist, ryll, kerbside, divergulent, instar |
| Truncated version of the same section | uncalibrated-sextant |
| No agent guidance at all | occystrap, client-python-k3s |
The six copies with agent guidance share an identical section
skeleton and near-identical prose. The differences are almost
entirely re-wrapping: ### Future work says the same thing in all
six, wrapped to a different column in each. There is one real
semantic split -- divergulent says "the project's issue tracker
(once one exists)" where the others say "the relevant github bug
tracker".
The drift matters because the content is wrong in ways nobody noticed:
- No repository lists
fable. The rosters offer opus, sonnet and haiku only, so a planner has no way to recommend the most capable model for a step that needs it. - The context-window claim is stale. Every copy says "opus has 1M tokens, sonnet and haiku have 200K". Sonnet is 1M now. That paragraph actively steers planners towards opus for a reason that no longer holds.
- The effort ladder stops at
high.xhighandmaxexist and are the right settings for hard agentic work.
Each of these is a fleet-wide edit that today would mean editing eight files in eight repositories by hand, which is exactly how the drift happened in the first place.
What "good" looks like¶
The mechanism already exists: templates/shared-blocks/ plus the
version-marker discipline the push-audit check enforces. Editing
a canonical block and bumping its version marks every lagging
repository non-compliant on the next daily run and files the issues
automatically. Adding fable should be one file edit, one version
bump, one commit.
The organising rule for deciding what becomes a block: a section
of the template is either wholly shared or wholly project-specific.
Generic rules and local examples are not interleaved within a
section. Where a section is a generic rule plus a local example, the
rule is the block and the example follows it in an
!!! note "In this project" admonition.
The template itself is top-level and not published, so on GitHub
that admonition shows as literal !!! note text. That is the right
trade: nobody reads the template much, whereas master plans written
from it live in docs/plans/ and are published through
mkdocs-material, where it renders properly. The material genuinely
does survive into those plans -- of shakenfist's 125 published
plans, 85 carry a Back brief, 76 carry Success criteria, and 44
carry Step-level guidance. A repeated ### In this project heading
would render in all of them too, but would mint duplicate anchor
IDs and duplicate table-of-contents entries each time.
Only the three short runs that trail a shared block are marked.
Whole project-specific sections (## Prompt, ### Success
criteria, ### Documentation index maintenance) already announce
themselves by their headings, and the ... placeholder sections are
specific to each plan rather than to the project.
That rule requires a restructure, not just markers. Today the
templates interleave at paragraph and bullet granularity --
### Planning effort opens with two generic sentences and then
talks about MariaDB accessors -- and ### Documentation index
maintenance (project-specific) sits between ### Bugs fixed during
this work and ### Back brief, so the close-out sections are not
even contiguous.
Execution¶
This plan was written before the Execution table convention, and
its phases are the prose sections below. The table was added when
the push audit was planned, on 2026-10-24, so the Merged cells of
phases 1 to 3 are reconstructed rather than recorded as each phase
landed; how is under Push audit.
| Phase | Status | Merged |
|---|---|---|
| 1. Implementation: blocks, check and spec | Complete | 2468dda, 5918f5b direct; 5b1fb74 (#49) and ff92357 (#50) are recorded and audited under PLAN-push-audit-phase |
| 2. Migration | Complete | shakenfist 0f21df0 (#3785), ryll 1a7b47e (#281), kerbside b29e01f (#327), divergulent 70b2a8f (#67), instar cbcdd2a (#500), uncalibrated-sextant d81eb69 (#5), occystrap b1438e9 (#115), client-python-k3s 12dbacd (#30) |
| 3. Verification | Complete | n/a -- confirmations against scratch copies, no commit |
| 4. Push audit | Complete | 1fc93d7 (#312) |
Implementation¶
The sections below describe the implementation as it landed, in
August 2026. PLAN-audit-scripts-restructure has since moved the
code: check_plan_template is now the PlanTemplate class in
scripts/audit/checks/plans.py, registered in
scripts/audit/registry.py, the spec is
docs/audits/plan-template.md, and PLAN_TEMPLATE_BLOCKS names
ten blocks since plan-phase-landing joined it on 2026-09-19.
1. Nine shared blocks -- templates/shared-blocks/¶
| Block | Covers | Churn driver |
|---|---|---|
plan-file-conventions |
where plans live, phase file naming, the phase tracking table, one commit per logical change | rare |
plan-status-vocabulary |
the fixed set of terms a status cell may hold | rare |
subagent-execution-model |
implementation happens in sub-agents; the plan/spawn/review/retry/commit loop; worktree isolation | rare |
plan-planning-effort |
master plan at high effort; each phase states its planning effort | rare |
subagent-step-guidance |
the step table, the effort ladder, what makes a good brief | rare |
subagent-model-roster |
which models exist, what each is for, context windows, skew-heavier rule | every model launch |
plan-review-checklist |
what the management session verifies after a sub-agent completes | rare |
plan-closeout-sections |
Future work, Bugs fixed, Back brief -- headings included | rare |
plan-push-audit-phase |
every master plan ends with a phase running PUSH-AUDIT.md over the whole plan's work |
rare |
This started as seven. plan-status-vocabulary was added when the
plan-index audit needed the same wording on both sides, and
plan-push-audit-phase by PLAN-push-audit-phase.md, which needed
somewhere to put the pre-push audit's trigger and found that this
plan's migration was the only pass that would carry it into eight
templates without editing them twice. The migration below carried
the seven that existed on 2026-08-16; the other two reached the
templates afterwards through the issues the audit files.
subagent-model-roster is separate from subagent-step-guidance
on purpose. It churns whenever a model ships or retires while the
effort ladder sits still for months, and separating them means the
auto-filed issue says the model roster is stale rather than that
step guidance is stale. Verified: bumping the roster to v2 produces
shared block subagent-model-roster is stale (v1 embedded, v2
current) and nothing else.
plan-closeout-sections includes its own ### headings and the
... placeholders beneath them, because those are sections the plan
author fills in rather than instructions to read. That is safe
because real plans in docs/plans/ adapt the template rather than
copying it byte-for-byte -- checked against shakenfist's existing
plans, which diverge substantially from the template's headings.
Content corrections folded into the blocks: fable added to the
roster with guidance to reserve it for steps that have defeated
opus; the context-window sentence corrected (fable, opus and sonnet
1M, haiku 200K); the effort ladder extended through xhigh and
max; and divergulent's better "the project's issue tracker, where
one exists" wording adopted fleet-wide over "the relevant github
bug tracker".
2. Automated check -- scripts/audit-check.py¶
check_plan_template mirrors check_push_audit: repositories with
no PLAN-TEMPLATE.md are not_applicable; those that have one must
carry all nine blocks, current and verbatim. Registered in
check_calls() and in audit_common.py (AUDIT_METADATA and
ISSUE_TITLES).
Whether every repository should have a plan template is deliberately left as a separate decision. Eight of the sixteen audited repositories have none, and a template's project-specific half has to be written with real knowledge of the project -- an empty one is worse than none.
3. Audit spec -- audits/plan-template.md¶
Documents what is checked, which blocks are required, why the model
roster is separate, and what stays project-specific. Registered in
audits/README.md and PROJECT-CONSISTENCY-AUDITS.md.
Migration (separate commits, one per repository)¶
Landed in all eight repositories, each as one pull request merged
on 2026-08-16 and touching PLAN-TEMPLATE.md alone; the record is
under Push audit below. Those carried the restructure and the
seven blocks that existed that day. plan-status-vocabulary
(2026-08-19) and plan-push-audit-phase (2026-08-24) reached the
templates afterwards through the issues the audit files, which is
the steady state working rather than more migration.
An earlier revision of this paragraph said five had landed and
occystrap was outstanding, which was never true -- occystrap#115
merged the same day as the rest -- and that shakenfist was
non-compliant over plan-push-audit-phase (shakenfist#3892), which
shakenfist#4160 fixed and the audit closed on 2026-09-13. Where each
repository stands today is under Verification. Each migration
followed these steps:
- Restructure
PLAN-TEMPLATE.mdso no section mixes generic and project-specific text. In practice: move the project-specific examples out of### Planning effortand### Step-level guidanceto sit after the block, wrapped in an!!! note "In this project"admonition; promote the model roster to its own### Model choiceheading; move### Documentation index maintenanceup next to### Success criteriaso the close-out sections become contiguous. - Embed every block that exists at the time, verbatim.
- Confirm
audit-check.pyreportsplan-templateas pass.
occystrap and client-python-k3s have no ## Agent guidance section
at all, so they gain the whole sub-agent workflow rather than having
it rewritten.
A reference restructure of shakenfist's template was assembled and verified to pass, then reverted -- the canonical wording needs review before eight copies of it exist.
Verification¶
check_plan_templatereturnsnot_applicablefor clingwrap (no template) andfailwith per-block detail for shakenfist (template present, no blocks yet). Confirmed.- A restructured shakenfist template carrying all seven blocks
returns
pass. Confirmed against a scratch copy -- at seven, beforeplan-status-vocabularyandplan-push-audit-phasewere added. Re-confirmed on 2026-10-24 at ten blocks, afterplan-phase-landingjoinedPLAN_TEMPLATE_BLOCKS: run against fresh clones of all eleven other repositories carrying a template. Of those, andris, kerbside-patches, occystrap, private-ci and shakenfist pass, as does this repository; the other six are covered by the next two bullets. - Bumping a block version marks the embedding repository stale, and
names the block that moved. Confirmed, and since confirmed in the
field twice: the
plan-file-conventionsv2 bump (d0ab9d4, 2026-10-06) and theplan-push-audit-phasev4 bump (3d35d34, 2026-10-13) each producedshared block <name> is stale (vN embedded, vM current)naming that block and nothing else. client-python-k3s, divergulent, instar, kerbside and ryll fail on 2026-10-24 on exactly those lines and no others. An earlier revision of this bullet dated the v4 bump tod502250, which reworded the block a day later and left it at v4. No repository reports that block as drifted on 2026-10-24. - uncalibrated-sextant is the one repository missing blocks rather
than lagging a version:
plan-status-vocabularyandplan-push-audit-phase, both written after its migration, andplan-phase-landing, which is not this plan's. That is uncalibrated-sextant#12, open since 2026-09-04 in a repository with no push since 2026-09-03. It is consistency-fix work rather than migration; see Future work under Push audit.
Push audit¶
This is the last phase of the plan and it is not optional. It comes after Migration and Verification because those are where the bulk of the work is: a push audit run at the end of Implementation would cover none of the eight repositories the migration touches.
There are two obligations here and they are not the same one:
- Each migration pull request runs its own repository's
PUSH-AUDIT.mdover that pull request, against that repository's default branch, as part of that pull request. That is a per-repo obligation discharged eight times. - This plan's final phase runs this repository's
PUSH-AUDIT.mdover the accumulated diff of the whole plan -- the shared blocks,check_plan_templateand the audit spec -- againstmain, not over the last commit alone. It runs once the migration is complete.
The blocks half of that diff did not land in one place, so the range
is recorded here rather than derived when the phase runs.
plan-push-audit-phase asks for a Merged: line per phase where a
plan's phases are prose sections rather than a table; this plan's
implementation sections were built in four landings, so the line
names the set:
Merged: 2468dda and 5918f5b direct to main; 5b1fb74 (#49)
and ff92357 (#50). Between them those cover all nine blocks,
check_plan_template, and docs/audits/plan-template.md --
2468dda carried seven blocks plus the check and the spec page,
5918f5b added plan-status-vocabulary, and #49 and #50 added and
then revised plan-push-audit-phase. The eight migration pull
requests are listed in the Landing record below. None of them ran a
push audit, because the obligation postdates them, and step 4b runs
the floor checklist over them instead. An earlier revision of this
paragraph said the migration was in four other repositories and was
audited there; neither was true.
Two corrections to that line, both worth reading before doing this
for another plan. It first read as seven commits "all direct to
main", derived from git log -- templates/shared-blocks/. That
was wrong twice over. Two of the seven were not direct: 51d872f
arrived inside #21 and e2585e3 inside #49, so anchoring on either
would have audited one commit of a pull request rather than the pull
request, and e2585e3 was double-counted against 5b1fb74 besides.
A path-filtered log does not distinguish the two cases;
git rev-list --first-parent does, and is now what the block tells
you to use.
The larger error was scope. Four of the seven -- 9a74046
(readme-discipline), fbcd759 (comment-proportion), 3abb973
(plan-phase-references) and 51d872f (llm-doc-discipline) --
are not this plan's work at all. They touch
templates/shared-blocks/ but none of them is one of this plan's
nine blocks, so an audit anchored on them
would have swept in four unrelated consistency audits. Recording a
range by the directory a plan happens to touch is the same mistake
as deriving it from a date: both answer "what else was going on"
rather than "what did this plan do".
Findings from either 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 an audit finds nothing, say so in one sentence.
Planning effort¶
High effort. The work is mechanical once scoped, but every decision below is about what the audit reads, and the two previous attempts to record this plan's range (above) both went wrong on exactly that.
Scope¶
In: wave 1 and wave 2 of this repository's PUSH-AUDIT.md over
this plan's two direct landings and the code that succeeds them; the
push-audit floor checklist over the eight migration pull requests;
the triage and outcome record; and closing the plan out. The
subject widened during execution to the whole of PlanIndex,
docs/audits/plan-index.md and their tests, once wave 1 showed
that 5918f5b created that criterion; see Execution notes.
Out: 5b1fb74 (#49) and ff92357 (#50), already audited (decision
2); plan-phase-landing, the v2 and v4 bumps and every
consistency-fix re-sync since the migration, which are other plans'
work or the steady state (decision 5); and fixing
uncalibrated-sextant#12 (decision 6). Findings are fixed in their
own pull request, not on this branch.
What the survey found¶
The survey ran on 2026-10-24 against main at aa8d259 and fresh
clones of the eleven other repositories that carry a
PLAN-TEMPLATE.md. The false claims it found have been corrected
at their source -- in Migration, Verification, the new
Execution table, the note at the head of Implementation, and
the plan's row in docs/plans/index.md -- so they are not repeated
as work below.
- The migration was complete, not outstanding. All eight
repositories merged a migration pull request on 2026-08-16, each
touching
PLAN-TEMPLATE.mdonly. occystrap's (#115) was among them. shakenfist#3892 closed on 2026-09-13. - None of the eight ran a push audit. No body or comment on any
of them mentions one, which is expected: the per-repository
obligation arrived with
plan-push-audit-phaseon 2026-08-24, eight days later. - The implementation's code no longer exists where this plan
says.
check_plan_templateinscripts/audit-check.pyis nowPlanTemplateatscripts/audit/checks/plans.py:1672; the block list isPLAN_TEMPLATE_BLOCKSatplans.py:1081; the validator isvalidate_shared_blocksatscripts/audit/text/shared_blocks.py:84; the spec moved fromaudits/todocs/audits/plan-template.md; andAUDIT_METADATAandISSUE_TITLESare now derived from the registry, with the titles frozen atscripts/tests/test_metadata.py:359and:362.PLAN-audit-scripts-restructuredid that move and isComplete. - The block list is ten, not nine.
plan-phase-landingwas added on 2026-09-19 (9bfc0e8), byPLAN-review-unit-size. - #49 and #50 are already audited. They are phases 1 and 2 of
PLAN-push-audit-phase, whose own push audit read both (PLAN-push-audit-phase.md:239-240and its decision 1). - Fleet state on 2026-10-24, from
audit-check.pyagainst the clones: andris, development, kerbside-patches, occystrap, private-ci and shakenfist passplan-template; client-python-k3s, instar, kerbside and ryll fail only onplan-push-audit-phasev3-of-v4; divergulent fails on that andplan-file-conventionsv1-of-v2; uncalibrated-sextant is missing three blocks (uncalibrated-sextant#12). The weeklydocs/audits/compliance.mdlags this -- it still shows occystrap non-compliant over #157, which closed on 2026-10-17. - development's own template marks its project-specific runs
with a bold
**In this repository.**paragraph, where the other eleven use the!!! note "In this project"admonition that this plan anddocs/audits/plan-template.md:80prescribe. development publishes nothing through mkdocs, so the rendering argument for the admonition does not reach it; whether the spec should say so is a question for 4e.
Decisions¶
- The phase lives in this section, not a phase file. This
repository keeps phases inline (
docs/plans/index.md, and the note underplan-file-conventionsinPLAN-TEMPLATE.md), and a seven-step audit is not large enough to argue with that. - #49 and #50 are cited, not re-audited.
PLAN-push-audit-phaseran this repository's runbook over both. Running it again would spend four wave 2 agents re-readingplan-push-audit-phaseandplan-audit-phase, which are that plan's work rather than this one's. The interaction the accumulated-diff rule exists to catch -- what #49 and #50 did to the blocks and check that2468ddacreated -- is covered by decision 3, because the code wave 2 reads is what all four landings and everything since left behind. - Wave 1 reads the historical diffs; wave 2 reads the code that
succeeds them, at
HEAD.2468ddaand5918f5badded code to ascripts/audit-check.pythat no longer exists in that shape, and a spec under anaudits/directory that has moved. A code review of deleted functions finds nothing anyone can fix. The historical diffs still matter for the questions wave 1 asks -- was a version marker moved with its wording, was an issue title introduced that the frozen table now has to keep, did anything private enter the repository -- so wave 1 runs over them as recorded, and wave 2 gets them as context and the live files as its subject. This is the decision a reviewer is most likely to dispute, because it means no agent reviews the originalcheck_plan_templateas written. The answer is thatPLAN-audit-scripts-restructurealready reviewed its translation into the class, and the class is what runs every morning. - The eight migration pull requests get the floor checklist,
not eight runbooks. They predate the per-repository obligation,
each changed one prose file, and every one of those files has
since been rewritten by at least one re-sync, so a
retrospective
PUSH-AUDIT.mdrun per repository would audit text that no longer exists, through eight runbooks written for other blast radii. The floor -- items 1, 3 and 4 of the list inplan-push-audit-phase-- asks the questions that still have consequences: is the record well-formed, did anything secret or private enter a public repository, and did a removed heading leave a consumer behind. Items 2 and 5 do not apply to a prose file and 4b says so. - Scope is this plan's blocks and this plan's commits. The
Push audit text above already learned that a range drawn by
directory sweeps in other plans' work. The same holds for later
versions of these blocks: a bump to
plan-file-conventionsv2 belongs to whoever made it. - uncalibrated-sextant#12 does not hold the plan open. The migration landed there (#5) with every block that then existed. What is missing is two blocks written afterwards and one from another plan, the issue the mechanism exists to file has been filed, and fixing it is consistency-fix work in a repository nobody has pushed to since September. It goes in Future work.
- Close-out follows
plan-phase-landing. If the audit raises findings, their pull request carries the close-out: row 4 toComplete, with the merge commit of the pull request that lands this record as itsMergedcell, and the index row toComplete. The findings pull request's own merge is not recorded in row 4; it is cited in the Outcome. If it raises none, this phase's own pull request sets both and row 4 records noMergedcell.
Landing record¶
Reconstructed on 2026-10-24 from gh pr view and git cat-file,
not from a path-filtered log. Each direct landing is one commit
with one parent, read as <sha>^..<sha>; the two are not
contiguous, so there is no single range.
| Landing | Form | Diff | Audit |
|---|---|---|---|
2468dda |
direct, 2026-08-16 | +628 -4, 14 files | this phase, 4a and wave 2 |
5918f5b |
direct, 2026-08-19 | +696 -4, 9 files | this phase, 4a and wave 2 |
5b1fb74 (#49) |
merge | PLAN-push-audit-phase phase 5 |
|
ff92357 (#50) |
merge | PLAN-push-audit-phase phase 5 |
|
shakenfist 0f21df0 (#3785) |
merge | +230 -167, 1 file | none ran; 4b |
ryll 1a7b47e (#281) |
merge | +225 -168, 1 file | none ran; 4b |
kerbside b29e01f (#327) |
merge | +235 -174, 1 file | none ran; 4b |
divergulent 70b2a8f (#67) |
merge | +236 -166, 1 file | none ran; 4b |
instar cbcdd2a (#500) |
merge | +243 -181, 1 file | none ran; 4b |
uncalibrated-sextant d81eb69 (#5) |
merge | +223 -124, 1 file | none ran; 4b |
occystrap b1438e9 (#115) |
merge | +250 -29, 1 file | none ran; 4b |
client-python-k3s 12dbacd (#30) |
merge | +263 -34, 1 file | none ran; 4b |
The two direct landings touch 17 distinct paths between them.
Step plan¶
| Step | Effort | Model | Isolation | Brief for sub-agent |
|---|---|---|---|---|
| 4a | medium | sonnet | none | Wave 1 of PUSH-AUDIT.md, run twice: AUDIT_RANGE=2468dda^..2468dda, then AUDIT_RANGE=5918f5b^..5918f5b. git fetch origin first. Run pre-commit run --all-files once, on a clean tree (git status --short empty), because review-tracking.py stamp reads SHAs from the index. Before the greps for each range run git diff --numstat "$AUDIT_RANGE" \| awk '{i+=$1; d+=$2} END {print i, d, NR}': expect 628 4 14 and 696 4 9, and a union of 17 paths from for r in 2468dda^..2468dda 5918f5b^..5918f5b; do git diff --numstat $r; done \| cut -f3 \| sort -u \| wc -l. Anything else means the wrong range: stop and report rather than auditing it. These commits predate PLAN-audit-scripts-restructure, so several greps name paths that did not exist yet (scripts/tests/test_metadata.py, docs/audits/) and will be empty by construction. Say so per grep, and run the question the grep was asking against the paths that did exist: for the issue-title check, read what each commit added to ISSUE_TITLES in scripts/audit_common.py and confirm the titles (Plan template, Plan index) match FROZEN_ISSUE_TITLES at scripts/tests/test_metadata.py:359 and :362 today; for the shared-block check, 2468dda creates seven blocks at v1, which is not an edit, and 5918f5b creates plan-status-vocabulary and edits templates/shared-blocks/README.md and audits/plan-template.md, neither of which carries a marker -- confirm both rather than assuming. Report every hit with a verdict (looked at, accepted or blocking, and why) and name the checks that were empty in both ranges. Report, do not fix. No commit. |
| 4b | medium | sonnet | none | Push-audit floor over the eight migration pull requests in the Landing record table, read with gh pr diff <n> -R shakenfist/<repo> -- no clone needed, and do not push or comment anywhere. For each: (1) confirm gh pr view <n> -R shakenfist/<repo> --json mergeCommit,additions,deletions,changedFiles matches the table row; (3) grep the added lines for anything resembling a token or key, a private hostname (*.home.stillhq.com, *.local, an internal-only name) or an RFC 1918 address, and report each with a verdict -- these are public repositories; (4) list every #, ## and ### heading the diff removed or renamed, then grep that repository's current default branch (gh api repos/shakenfist/<repo>/contents/... or a shallow clone into the scratchpad) for references to each, including AGENTS.md, docs/ and .claude/skills/, and also grep shakenfist/development's scripts/ and templates/ for any that the audit tooling parses by name. State the greps you ran. Items 2 (unattended jobs) and 5 (publishing) do not apply to a prose file: say so in one sentence each. Report, do not fix. No commit. |
| 4c | high | sonnet | none | Wave 2a, code quality, per the 2a brief in PUSH-AUDIT.md. Subject: the live code at HEAD, not the historical diff -- PlanTemplate (scripts/audit/checks/plans.py:1672-1710), PLAN_TEMPLATE_BLOCKS (plans.py:1081) and its comment, load_canonical_block and validate_shared_blocks (scripts/audit/text/shared_blocks.py:67 onward), and the status-vocabulary path of PlanIndex (plans.py:1256 onward). Read git show 2468dda and git show 5918f5b (+628 -4 and +696 -4; state those totals in your report) for what the code was meant to do. Questions to answer: does the validator distinguish a stale version, an edited body at the current version, a missing end marker, a block embedded twice and a block nested inside another, and does each produce a detail string that tells a maintainer in another repository what to do; is PLAN_TEMPLATE_BLOCKS the only list of these names, or does a second copy exist in docs/audits/plan-template.md, templates/shared-blocks/README.md or elsewhere that could drift from it; and does the comment above it still describe the list. Apply the source-file-size judgement to plans.py (1,710 lines). Report, do not fix. No commit. |
| 4d | high | sonnet | none | Wave 2b, test review, per the 2b brief in PUSH-AUDIT.md, over the same live subject as 4c. Read PlanTemplateTest (scripts/tests/test_plans.py:3162 onward), the PlanIndexTest vocabulary cases (test_plans.py:826 onward), and the validator tests in scripts/tests/test_containment.py. For each behaviour 4c's brief lists -- stale version, edited body, missing end marker, duplicate, nested, not-applicable when there is no template, and every name in PLAN_TEMPLATE_BLOCKS having a canonical file -- say whether a test exists and whether it would fail if the behaviour broke, naming the test. A test that asserts only that fail was returned, not which block the detail names, does not prove the roster-separation claim in Implementation. Report, do not fix. No commit. |
| 4e | high | opus | none | Wave 2c, documentation review, per the 2c brief in PUSH-AUDIT.md. Read the ten canonical blocks in templates/shared-blocks/ named in PLAN_TEMPLATE_BLOCKS together, in the order PLAN-TEMPLATE.md embeds them, as the one document a planner in another repository reads, and report any two that contradict each other -- the likeliest place is the Merged column, which plan-file-conventions, plan-push-audit-phase and plan-phase-landing all describe. Check subagent-model-roster against the models actually available today: report any model it omits or names wrongly and any context-window figure you cannot confirm, saying which you could not confirm rather than guessing. Then check docs/audits/plan-template.md, templates/shared-blocks/README.md and docs/audits/README.md:229 against the code -- block count, block names, and what a maintainer is told to do -- and judge whether docs/audits/plan-template.md:80 should acknowledge that a repository which publishes nothing through mkdocs (this one) may mark its project-specific runs another way. Do not edit any file under templates/shared-blocks/: a version bump files issues in eleven repositories, so a finding there is reported with the bump it would need. Report, do not fix. No commit. |
| 4f | high | opus | none | Wave 2d, security review, per the 2d brief in PUSH-AUDIT.md, over the same live subject as 4c, with the historical diffs as context. The blast radius is other repositories' issue trackers: validate_shared_blocks reads PLAN-TEMPLATE.md from repositories this one does not control, and its detail strings go into issue bodies filed and rewritten by audit-manage-issues.py. Establish whether any part of a detail string is taken from the scanned file rather than from PLAN_TEMPLATE_BLOCKS -- a block name or version read from a crafted marker -- and if so whether it can carry an @ mention, a closing keyword, a link or markdown that changes the issue body. Establish whether load_canonical_block can ever be called with a name from scanned content, and if so whether ../ escapes templates/shared-blocks/. Consider what a very large or pathological template does to the marker regex. Apply the path-traversal-review shared block. Report, do not fix. No commit. |
| 4g | high | opus | none | Management triage, in the session rather than a sub-agent. First check each report states the totals it read, and that 4a's match the Landing record; re-run any that do not. Decide each finding blocking, advisory or declined, and write an Outcome subsection here: what 4a and 4b found, what each wave 2 agent found, and for every finding where it was fixed or why it was declined. If all are clean, one sentence saying so. Work the PUSH-AUDIT.md management checklist. Fixes land as their own pull request against main. Close out per decision 7. Commit subject: "Record the plan template blocks push audit." |
Risks and mitigations¶
- An empty diff read as a clean audit. Already-merged work
under the runbook's
origin/main...HEADdefault diffs nothing. Mitigation: every brief carries its concrete range, every report states its totals, and 4g refuses a report whose totals do not match the table. - Wave 1 greps that cannot fire, reported as clean. Several greps name paths that postdate these commits. Mitigation: 4a must say per grep whether it could have fired, and run the underlying question against the paths that existed; 4g checks that it did.
- Decision 3 misses a defect that existed only in the original code. If it was translated faithfully it is in the class and wave 2 sees it; if it was not, the restructure fixed it. Mitigation: 4c reads both commits for intent, and a behaviour the original promised that the class does not deliver is itself a finding.
- A fix to a canonical block files issues fleet-wide.
Mitigation: no step edits
templates/shared-blocks/; 4g decides any bump deliberately, in the findings pull request, and states how many repositories it newly fails.
Definition of done¶
- Every report from 4a to 4f states the totals it read, and 4a's
are
628 4 14and696 4 9with a 17-path union. Reproducible with:
for r in 2468dda^..2468dda 5918f5b^..5918f5b; do
git diff --numstat $r | awk '{i+=$1; d+=$2} END {print i, d, NR}'
done
for r in 2468dda^..2468dda 5918f5b^..5918f5b; do
git diff --numstat $r; done | cut -f3 | sort -u | wc -l
- This section has an Outcome subsection giving a verdict for every finding, or one sentence saying there were none.
- Every row of the Landing record matches
gh pr vieworgit cat-file -p, and 4b's report covers all eight pull requests. - Row 4 of Execution and the plan's row in
docs/plans/index.mdareComplete, set as decision 7 says and not before. python3 scripts/audit-check.py --repo-path . --repo-name developmentreports everyplan-*check as it did on 2026-10-24 (pass, ornot_applicableforplan-source-references).pre-commit run --all-filespasses.
Future work¶
- uncalibrated-sextant#12: three blocks missing from its
PLAN-TEMPLATE.md, and four other consistency issues open there since September. consistency-fix work, with no owner while the repository is dormant. docs/audits/compliance.mdis regenerated weekly and lags the daily issue state, so it showed occystrap non-compliant a week after occystrap#157 closed. Worth knowing before trusting it as a survey source; not this plan's to fix.
Back brief¶
Before running 4a, back brief the operator: which ranges each step reads, why #49 and #50 are cited rather than re-run, and why wave 2 reads the live code rather than the historical diff. Decisions 2, 3 and 4 narrow the audit, and a narrowing is cheap to agree before the agents run and expensive to discover afterwards.
Execution notes¶
Run on 2026-10-24 between 02:20 and 02:50 UTC, from the management session, with the back brief agreed as written: decisions 2, 3 and 4 stand, and this session may push and open the pull request.
- 4a and 4b ran first, in parallel, both read-only. 4c to 4f ran in
parallel once wave 1 had passed, as
PUSH-AUDIT.mdasks. 4g is this record. - The survey undercounted
5918f5b. It did not only createplan-status-vocabulary: it created the wholeplan-indexcriterion, with its check,audits/plan-index.mdand 263 lines of tests. 4a found this; 4c to 4f were told, andPlanIndexwas reviewed in full rather than only its vocabulary path. The 4a brief above is left as it ran. - The GitHub core API quota ran out during 4b, so
gh pr difffailed for all eight pull requests. 4b took the diffs from blobless bare clones instead (git diff <merge>^1 <merge>), and thegh pr viewfigures it checked against had already been fetched. The issues below were filed after the quota reset. - Every report stated its totals, and all matched the Landing
record:
628 4 14and696 4 9, 17 paths in the union, and the eight pull requests' figures. No report was re-run.
Outcome¶
Line references in this section, the survey and the step plan are as
of aa8d259, the main this phase branched from; the findings pull
request will move many of them.
Nothing blocking. Wave 1 passed. The floor checklist over the eight
migration pull requests found nothing. Wave 2 raised one
medium-severity defect and a set of advisory ones, most of them in
the shared-block validator that 2468dda pointed at
PLAN-TEMPLATE.md. The fixes are listed below and land as one
findings pull request against main. That pull request also carries
the close-out under decision 7. Blocking here would mean holding a
push, and the code under audit has been on main since August, so
severity decides the order of the fixes, not whether this record
merges. Problems that predate this plan, or that belong to another
plan's blocks, are filed as issues rather than treated as findings.
4a, wave 1. pre-commit run --all-files passed. Every grep was
either empty or a hit that was looked at and accepted:
- Seven blocks created at v1 by
2468dda, andplan-status-vocabularyat v1 by5918f5b. Neither commit edits an existing block. +import re, which is stdlib.- The empty
consistency-auditanchor pair in each new spec. PLAN-TEMPLATE.mdnamed as a file rather than as a plan.
The titles Plan template and Plan index match
FROZEN_ISSUE_TITLES today. scripts/audit-check.py was 3,276 and
then 3,658 lines after these commits, which is well past the size
mark. The restructure has since split it, so that is history rather
than a finding. One defect in the runbook itself:
PUSH-AUDIT.md:150 puts --name-only after the --, so git reads
it as a path and prints the whole diff. It predates this plan, and is
issue development#309.
4b, migration floor. All eight pull requests match the Landing record.
- No secret, private hostname or address was added.
- The only removed headings are
### Future workand### Bugs fixed during this work, in six templates. Both are re-added in the same diff below the new### Model choice. Both are present on every default branch today, so no consumer was stranded. - Items 2 and 5 of the floor checklist do not apply to a prose file.
Fixed in development#317, the findings pull request, which
closes development#313. Findings 1 to 6 are d0e26ce, 7 to 9 are
a6af4a5 and 10 is f66c59c; the descriptions below are as the
audit found them. Every finding is fixed, with one part of finding 7
declined: a subdirectory link such as completed/PLAN-a.md still
registers by basename, because repositories file finished plans under
docs/plans/completed/ and plan_index_entries has always read them
that way. The fixes moved no plan-template, push-audit,
plan-index or plan-audit-phase verdict for any of the twelve
repositories carrying a template, measured against fresh clones. Two
fixes went wider than the finding:
- The unreadable-file fix applies to
PushAuditas well asPlanTemplate. The same pattern there also let an unreadablePUSH-AUDIT.mdfall through to auditingPUSH-TEMPLATE.md. -
plan_index_entrieshad the same blind spot for titled and angle-bracketed links, and both readers now share one helper. -
The block extractor uses memory that grows with the square of its input. This is medium severity, from 4f, reproduced here.
extract_shared_blocks(scripts/audit/text/shared_blocks.py:54) slices from every begin marker to the next end marker and keeps every slice, and runs onefindper begin marker, so time grows with the square of the input as well as memory. A file of 200 KB of begin markers and one end marker took 428 MB, and the read cap allows 2 MB, on a shared self-hosted runner. The extractor predates this plan (9a74046). It is fixed here anyway:2468ddagave it its largest untrusted input, and the same rewrite closes the next three items. The fix is a single linear pass that pairs begin and end markers in order, with a test on the pathological input that bounds both. - The validator's account of malformed blocks is wrong. From 4c, 4d and 4e.
- A block missing its end marker borrows the next block's marker
and is reported as
drifted. That contradictsdocs/audits/plan-template.md:111. - A block nested inside another is reported the same way.
- A block embedded twice passes silently. Reproduced here: two
copies of
plan-file-conventionsreturn no problems. - A version marker of more than 4,300 digits raises
ValueErrorand turns the check into anerror, which freezes the issue and skips the fleet's compliance update.
The rewrite pairs each begin marker with its own end marker,
reports nesting, duplicates and a missing end marker by name, and
bounds the version.
3. Smaller validator fixes.
* stale and drifted should tell a maintainer in another
repository where to copy the block from, as missing already
does.
* load_canonical_block should open its file as UTF-8.
* load_canonical_block should say in a comment why its bare
os.path.join is safe: the name regex admits no / or ..
The path-traversal-review block asks for exactly that comment.
4. The roster-separation claim in Implementation has no test.
From 4d, by mutation: a validator that marks every block stale
when one is stale passes the suite. Assert that a stale block is
the only block named, and add a case that stales
subagent-model-roster alone.
5. PLAN_TEMPLATE_BLOCKS is pinned for three of its ten names.
From 4d: dropping subagent-model-roster,
plan-closeout-sections or plan-file-conventions survives,
because the fixtures are built from the list. Pin the list
literally. That change is the one that re-marks the fleet, which
is what the test class's docstring says it is for.
6. Validator behaviours are tested only through PushAuditTest.
From 4d: a drifted body, a missing end marker and an unknown
block have no test through PlanTemplate. Duplicate and nested
blocks have no test at all. Add them with item 2.
7. PlanIndex registers a local plan from any link that ends in
its filename. From 4c, reproduced here. [a](/components/development/plans/sub/PLAN-a/)
and an absolute URL to another repository's PLAN-a.md both
satisfy the row for a local PLAN-a.md (plans.py:1320). A
titled link, [a](PLAN-a.md "t"), and an angle-bracketed one are
not recognised at all.
8. Two PlanIndex guards survive mutation. From 4d:
PLAN_INDEX_MAX_SHOWN and the repo.isfile filter on master
plans. The second is the earlier symlink-escape fix, so a
surviving mutant there is worth a test. Two assert_skip calls
also lack containing, so they pass on either skip message.
9. An unreadable PLAN-TEMPLATE.md is reported as absent. From
4c: a template over the read cap, or a symlink out of the clone,
gives No PLAN-TEMPLATE.md, and not_applicable then closes an
open issue. Report it as unreadable.
10. Spec corrections.
* docs/audits/plan-index.md:73 says the criterion has no
template, but PlanIndex.template names
templates/shared-blocks/, so every plan-index issue links
one. Correct the spec.
* docs/audits/plan-template.md:80 should say that the
admonition is for repositories whose plans publish through
mkdocs. Elsewhere any non-heading marker will do, and the check
does not test for either.
* docs/audits/plan-template.md:35-48 describes
plan-push-audit-phase "since v2" and is silent on v3 and v4.
* The module docstring of plans.py justifies its 1,710 lines by
a validator that has since moved to shared_blocks.py. Restate
the reason, or name the seam (PushAudit, PlanTemplate and
their block lists).
Filed as issues:
- development#309:
PUSH-AUDIT.md:150's--name-only(4a), andPLAN-TEMPLATE.md:230-233, which still says every diff command is written againstmain...HEAD(4e). - development#310: three places where the blocks leave a planner unsure what to do, all in wording other plans own (4e):
plan-push-audit-phaserecordsfirst..lastwithout the caret warningPUSH-AUDIT.md:37-43gives. That needs a v5.templates/shared-blocks/README.md:63-65states the push-audit row's missingMergedcell without the conditionplan-phase-landingattaches to it.plan-file-conventionsdoes not say whether a phase may be a prose section, whichplan-push-audit-phaseassumes.- development#311:
MD_HEADING_CLOSING_RE(scripts/audit/text/markdown.py:105) is quadratic on a line of spaces: 1.35 s at 20,000 characters, reproduced here. It is called from six places underscripts/audit/checks/, and a 2 MB line would exceed the leg timeout. This is outside this plan's subject (4f). - uncalibrated-sextant#12 already exists; see Future work.
Declined:
- 4f, injected links on the compliance page
(
scripts/audit-update-docs.py:264).defusedocuments rendering details as prose there as a deliberate trade. A link in a committed page notifies nobody, and no newline survives to break the page's structure. - 4e, the roster's context windows. "The model also determines
the context window" describes a model's capacity, which is what a
planner choosing a model needs. A session configured below that
capacity does not make it wrong. The figures could not be confirmed
from anything in reach, and that is recorded here rather than
bumping
subagent-model-rosteracross eleven repositories on a guess. - 4c, literal markers in fenced examples. No
PLAN-TEMPLATE.mddocuments the marker syntax. The mechanism is documented intemplates/shared-blocks/README.md, which no check reads as a template. - 4c, the smaller
PlanIndexmessage defects. These are a symlinked index reported as missing, decoration stripping that removes_from inside words, an escaped\|shifting columns, a table with no Status column passing "with statuses from the shared vocabulary",2026-99-99passing the date check, andhas_plansbeing set by any link. Each is a message or an edge case, and no report saw any of them give a wrong verdict on a real index. They are recorded here so that a later change to the reader can pick them up.