Running backlog for new features, design ideas, measurements, and rollout chores. A reproducible defect belongs in a GitHub issue rather than this file. Completed work is deleted after its remaining rollout or decision is recorded elsewhere.
Entries are grouped by the change that ships them. A ### heading under "Work Clusters" is one pull request. Open states a decision the session doing the work makes, and Settled states a finding that is not re-derived. Checked records the measurement status. A verified claim names its branch, commit, and date. An unmeasured or unverified claim says so explicitly.
A cluster's State is one of four. ready means every open question is answerable by the session doing the work. blocked names the cluster it waits on. decision needs the maintainer. measure means the first action is a count rather than an edit.
Adoption gaps for the skill-based fleet system are registered in docs/fleet-map.md rather than here, so the two files do not fork. A new observation about such a gap lands as a register row there, and this file carries only the pointer.
The steps below are followed in order rather than sampled.
- Check open pull requests before selecting new work. Finish, close, or state the current blocker on any pull request that is merely parked.
- Read the cluster headings and their
Statelines. Select one cluster per pull request. - Prefer
ready. Selectdecisiononly when the maintainer can answer, and selectmeasureknowing the deliverable is evidence rather than an edit. - Re-verify the selected cluster's
Checkedclaims against currentdevelopbefore editing. - File a reproducible defect as an issue and remove its implementation details from this file. Keep only a rollout or directional decision that remains after the defect is fixed.
- Delete completed entries when their pull request merges. Move unfinished work into a new cluster with its own state.
One pull request pointing a hub uses: at a hub-owned action, so that the resolvability pass added beside it has a reference under this owner to read. It is separated from that pass because it changes what a workflow runs, where the pass only changes what a gate reports.
State decision. Touches the hub's own workflows. Cost one hub edit, hub-only, and it changes a running workflow so it is not a paper change.
- Decide whether the hub consumes its own
prose-gateaction the way the fleet does. Today it callsprose_lint.pydirectly, so everyuses:in the tree is under another owner.- Blocked by - Nothing, though it is only worth doing on its own merits rather than to give a gate something to read.
- Issue - None filed.
- Checked -
developatdbd1cdcon 2026-08-07, where the tree carries 45 pinneduses:refs and not one of them names aptr727repository. - Open - Whether the hub gating itself through its own pinned action is desirable at all, given the action reads the rules from hub
developon a non-maintarget and the hub already has the script in its own checkout. - Settled - The resolvability pass reports what it covered on every run, so the hub's zero is visible rather than silent, which is why this is a separate decision rather than a defect in that pass.
- Settled - The fleet's
ptr727pins are live in the downstream repos that consume the action, andrepo_gate.py --root <repo>from a hub checkout reads them there, so the pass is not idle fleet-wide.
One pull request measuring the remaining carried surface against the carry-versus-reach test and moving whatever qualifies, now that the model is settled rather than open.
State measure. Touches AUDIT.md and spec/files.json. Cost one hub edit plus a retirement per repo on its next visit. The workflow half of this cluster, replacing copy-pasted workflow content with cross-repo reuse, is measured and answered under "Hub-Hosted Reusable Workflows" below.
- Measure carried
AUDIT.mdagainst the test. It is adapted per repo today and the question is how much of it is genuinely per-repo.- Blocked by - Nothing.
- Issue - None filed, and #305 covers the propagation half from the other direction.
- Checked -
developat743fc81on 2026-08-26, wherespec/files.jsondeclares it atintent. - Open - Whether it moves.
- Settled - The test is stated: a repository carries the content it is audited against and the configuration that describes it, and it reaches machinery whose content is identical in every repository.
- Settled -
repo-config/configure.shis the first file moved across, carrying the ledger's onlyretiredisposition and naming six repos, NxWitness, aiopurpleair, homeassistant-purpleair, ESPHome-NonRoot, VSCode-Server-DotNetCore and LanguageTags. - Settled -
spec/secrets.json's adaptedbaseline/mechanismscarry is the second, retired outright rather than moved (#993):baselineapplies to every fleet repo by definition andmechanisms/targetMechanisms/typeMechanismsresolve centrally,spec/audit.pyreading the hub's own file plusregistry/repos.json, so no downstream repo needs a replacement local copy. - Settled - An unreachable hub means the tool did not run, reported as not run rather than worked around, since a hand-rolled substitute is the duplicated effort the model exists to end.
The spec rework and its audit check shipped. What remains is the per-repo conformance the check now reports, and one section the fleet carries that the model does not name.
State decision, on where ## Build Artifacts belongs, which is the only thing here a hub pull request settles. The four conformance entries above it are not selectable as hub work at all: each lands on a repo's own next visit, in the sense "Fleet Sweeps" below gives that phrase, and they sit here rather than there because the finding counts are what the shipped check measures. Touches each repo's README.md on its next visit, plus spec/readme-structure.md and spec/readme-sections.json if Build Artifacts is adopted. Cost one edit per repo, driven by the finding rather than by a sweep.
-
Work off the conformance backlog the
readme-structuredimension now reports. Measured across all 22 cataloged repos on 2026-08-08, against the shipped checks: 73 findings, 71 on sections and 2 on shields, plus the 3 retired-badge findings the entry below carries.- Blocked by - Nothing, and no repo is edited by the hub. Each lands on its own next visit.
- Issue - None filed.
- Checked - Every repo's default branch on 2026-08-08, with the hub read at its own
develop. - Settled - The shape of the work: 17 repos owe
3rd Party Tools, 10 owe theOverviewrename, 9 owe a Table of Contents, 7 owe a License section, and 5 public repos oweQuestions or Issues. - Settled - Three order findings are genuine and each is one move: LanguageTags places Installation after Usage, aiopurpleair places Getting Started after Installation, and PlexCleaner places Questions or Issues immediately after the Table of Contents where the order now puts it ninth.
- Settled - Two placement findings are genuine: KiCadLibrary carries a
## TODOafter## License, which the "TODO.md" rule already forbids, and HomeAutomation-Config renders the license shield twice, once outside the License section. - Settled - MediaTools carries a
NuGet Pre-Releaseshield that renders the same version as itsNuGet Releaseshield, and it is dropped on that repo's next visit. The check does not report it, because a shield class is a floor and an extra shield is never a finding. - Settled - Blog is the only repo carrying a
3rd Party Toolstable today, and it needs both fixes the rule now states: drop the License column, which the audit reports since 2026-08-09, and rewrite all three roles, since "theme, vendored underthemes/" and "web server, serving the built site and the redirects" describe this repo's wiring where "static site generator" describes the tool correctly and differs only by its opening capital and its full stop.
-
Bring each repo's
3rd Party Toolsentries onto the shared catalog. Measured on 2026-08-09: 57 findings across four repos, a link, a description, or an ordering that disagrees withspec/third-party-tools.json, plus the one License columnspec/readme-structure.mdforbids.- Blocked by - Nothing.
- Issue - None filed.
- Checked - Every repo's default branch on 2026-08-09, with the hub read at its own
develop, which now conforms. - Settled - The bulk is absent descriptions rather than wrong ones: 48 of the 57 are a tool listed with no description at all, across LanguageTags, MediaTools and PlexCleaner, and PlexCleaner alone accounts for 25 of those and 27 findings overall. Of the remaining nine, three describe a tool differently from the catalog, four link it differently, one is Blog listing Hugo, PaperMod, Caddy out of alphabetical order, and one is Blog's License column.
- Settled - Twelve tools already appear in more than one repo, which is what makes the catalog worth having before the 17 repos owing the section write their own wording for each.
- Settled - Four tools are already linked by two different URLs across the fleet, and the catalog picks one each: GitHub Actions takes
github.com/actions, Dependabot takeshub.lumenfield.work/dependabot, Nerdbank.GitVersioning takes the project repo rather than its marketplace action, and uv takesdocs.astral.sh/uv/to match ruff. The hub was the outlier on the first two and is fixed. - Settled - PlexCleaner lists Bring Your Own Badge as a tool, so the retired badge service has a fourth touchpoint beyond the three rendering it, and that entry goes with the same deletion.
- Settled - The catalog is a standard set and not a complete one, so a tool only one repo uses is unaudited. Of the 36 tools the fleet lists today, 24 are used by exactly one repo and are declared only so the second adopter copies rather than invents.
-
Work off the reference-link naming and grouping backlog. Measured across all 22 repos on 2026-08-08: 55 letter findings on naming and 27 drift findings on grouping.
- Blocked by - Nothing, and each repo's block is one edit.
- Issue - None filed.
- Checked - Every repo's default branch on 2026-08-08, with the hub read at its own
develop, which now conforms. - Settled - The naming half was already the fleet's practice before it was written down: 119 of 122 shield references end
-shieldand 514 of 532 URI references end-link, andactions-link,releases-link,issues-linkanddiscussions-linkare unanimous across every repo carrying them. - Settled - The two real naming inconsistencies are the repository root, which 10 of 20 call
github-linkand the rest name for the project, and./LICENSE, which 9 repos calllicense-linkwhere a repo-local path is a bare reference. - Settled - The grouping half is drift rather than letter because it is not met: the fleet carries seventeen distinct group-header names, and two repos, NxWitness with 116 definitions and ESPHome-NonRoot with 45, carry no group headers at all.
- Settled - KiCadLibrary is the largest single block at 22 naming findings, almost all of them repo-local paths named
-link.
-
Delete the retired
byob.yarr.islast-build badge from the three repos still carrying it. The service is deprecated and the badge is not required by any shield class, so the fix is a deletion rather than a replacement.- Blocked by - Nothing, and each repo's fix is deleting one shield line and one reference definition.
- Issue - None filed.
- Checked - Each repo's default branch on 2026-08-08, with the endpoints requested the same day: MediaTools and KiCadLibrary both return HTTP 404, so they already render a broken badge, and ESPHome-NonRoot still returns 200.
- Settled - The audit reports it, so this does not rely on anyone remembering:
deprecatedShieldsinspec/readme-sections.jsoncarries the retired service and the check fires on exactly those three repos. - Settled - A dead badge is worse than an absent one, because it renders broken rather than missing and a visitor cannot tell a retired service from a failing build.
- Settled - All three repos are already non-conformant on other grounds, so this rides their next visit rather than earning a pass of its own.
-
Decide where
## Build Artifactsbelongs. LanguageTags and aiopurpleair both carry it, opening with the same**Build process and artifacts**:line and covering package, versioning, and publishing.- Blocked by - Nothing.
- Issue - None filed.
- Checked - Both repos' default branches on 2026-08-08, where the section is the only one recurring across repos that
spec/readme-sections.jsondoes not name. - Open - Whether it becomes a named optional section, folds into
Build and Distribution, or moves toWORKFLOW.md, since its content overlaps both. - Settled - It is not a finding today. An unnamed heading is dropped before the order comparison, so the two repos carrying it pass, which is why this is a decision rather than a defect.
One pull request extending the type model with the two types the fleet already needs, plus the shared style the cpp type has no canonical for.
State ready. Touches spec/project-types.json, catalog/snippets/, CODESTYLE.md. Cost one hub edit plus a carried CODESTYLE.md re-vendor.
-
Add a linter-only Python type for codegen and boilerplate Python. Code that runs during another tool's build to emit generated source ships no unit tests and no coverage and needs only the linter.
- Blocked by - Nothing.
- Issue - None filed.
- Checked -
developat1ed0cc8on 2026-08-03. - Open - Nothing.
- Settled - It stays distinct from the existing
pythontype, which is utility code that can and should carry unit tests and coverage, as in PlexCleaner. - Settled - ESPHome-Config stays
source-onlyuntil it exists and its reclassification is deferred, so its one outstanding validation finding is accepted meanwhile.
-
Add a fleet-standard clang-format config for the
cpptype. A catalog snippet plus aCODESTYLE.mdC++ section, the analogue of the shared ruff config.- Blocked by - Nothing.
- Issue - None filed.
- Checked -
developat1ed0cc8on 2026-08-03. - Open - Nothing.
- Settled - It exists so the
cppclang-format check references one canonical style rather than each repo inventing its own, and the ESPHome-Config agent's proposed file is the base.
One pull request deciding what the hugo type says about a theme, which it declares nothing about today.
State decision. Touches spec/project-types.json and spec/type-model.md. Cost one hub edit, and it becomes the type's contract that a second generator inherits.
- Decide the theme carry mechanism as a question about the type rather than about Blog. The candidates differ along the same axis the carried-content clusters are about.
- Blocked by - Nothing.
- Issue - None filed, and #456 and #558 carry the type's intake.
- Checked -
developatb82c1a3on 2026-08-05. - Open - Which of three the type requires, the vendored copy Blog ships, a submodule pinned to an upstream ref, or a separate fleet-owned repository the site consumes.
- Settled - A vendored theme is a copy that goes stale with nothing detecting it, and a submodule is a pin Dependabot can see, which is the whole difference.
- Settled - Three details the intake predicted are wrong against what Blog runs, so planning from the prediction encodes requirements the repo does not meet: the theme is vendored with no recorded upstream ref rather than a Dependabot-tracked submodule, the generator is pinned by version and hash rather than run at latest, and the deploy is a separate dispatch rather than a tag cut last after the live check.
- Settled - What held is that the deploy is a publish, the type is named for the generator with the generic checks phrased so they do not name it, and the URL parity gate asserting a floor on the golden list length before comparing is the check of record.
- Settled - Promoting the generator-agnostic
hugochecks to a shared type when a second generator arrives is a registry edit by construction, perspec/type-model.md"Generators". - Settled - The
copilot_code_reviewrule in both ruleset payloads gates no merge today, because gated Copilot review is an invite-only beta, which deserves a sentence near the merge gate so no repo reads the rule as the enforcement and relaxes the manual discipline holding the line.
One pull request giving a repo a declared way to say what it needs at runtime, the way GitHub-stored secrets are already declared.
State decision. Touches spec/secrets.json and its schema, spec/audit.py, and the hub's own .gitignore. Cost one hub edit plus adoption per repo that deploys.
- Make a gitignored secrets directory the fleet standard and declare its contents. The required set is discoverable only by reading the deploy today.
- Blocked by - Nothing.
- Issue - None filed.
- Checked -
developat1ed0cc8on 2026-08-03, wherespec/secrets.jsoncovers only the Actions and Dependabot stores and the hub carries neither the directory nor a.gitignoreentry for one. - Open - Nothing on the local half, and the GitHub half below is the same axis rather than a separate problem.
- Settled - The pattern already runs in the fleet in two shapes, HomeAutomation-Config keeping a gitignored secrets directory of env files and Docker secret files, and ESPHome-Config keeping a gitignored
secrets.yamlbeside a committed_secrets.yaml. - Settled - The committed file carries the required names with dummy values, so the shape of the requirement is in git while the values never are, which is the split the GitHub side already gets from
requiredSecrets. - Settled - Blog needs it immediately, since it deploys on the proxmox host through HomeAutomation-Config's Docker Compose stack and carries the copy destinations and the internal URI.
- Settled - Adopting it in the hub comes first, since the hub carries neither piece.
- Settled - The GitHub side has the same missing axis, surfaced by the
hugotype, since a deploy's credentials are per-environment secrets and variables whilestoresis a closed enum ofactionsanddependabot, andspec/audit.pyseeds its map with those two keys and indexes it unguarded, so adding anenvironmentsvalue raises a key error for every repo whose publish maps to that mechanism. - Settled - An optional
environmentsblock is legal inspec/secrets.schema.json, but it has no per-repo dimension and downstream copies ofspec/secrets.jsonare retired, so no repo declares its per-environment names there, and no tool would read one, which is honest and is not a gate, so a clean audit says nothing about whether an environment is configured.
One pull request stating that an agent never assumes a Docker image is present locally, however recently it pulled one.
State ready. Touches GOVERNANCE.md, and OPERATIONS.md if the mirrored one-liners move with it. Cost one hub edit, plus a carried re-vendor if the rule lands in a carried section.
- State the always-pull default and the explicit pull where the flag does not apply. A background prune can remove an image between two commands of the same session.
- Blocked by - Nothing.
- Issue - None filed.
- Checked -
developat1ed0cc8on 2026-08-03, where the four documented lint invocations already carry the always-pull flag and no rule states why. - Open - Where it lives, since "Running the Linters Locally" is scoped to the four lint tools while the rule covers any container an agent starts, and whether it is carried, since every repo runs the same images from the same instructions.
- Settled - What is missing is the rule rather than the one-liners, since an agent composing an ad-hoc
docker rundrops the flag precisely because it believes the image is cached. - Settled - The honest limit stops the flag reading as the whole answer, since
docker runagainst a registry tag re-pulls an absent image on its own, so the cases that break are a locally built tag with no registry to pull from, and any command that branches on the image being present such asdocker image inspectordocker images.
The hosted gates, release chain, Docker core, and type-specific tasks are implemented and promoted. Their completed design history lives in docs/reusable-workflows.md. The generated adoption state lives in reports/workflow-reuse.md.
State ready. Touches downstream caller stubs and repository-specific hooks. Cost one downstream visit at a time.
-
Finish the downstream adoption checklist. Regenerate the report before selecting a repository, then resume from the first incomplete rollout item in the design document.
- Blocked by - Nothing.
- Issue - None filed for the rollout. File implementation defects separately.
- Checked -
mainat74ef727on 2026-08-18, where the hosted workflow family is promoted. - Open - The incomplete downstream adoption items recorded in the rollout document.
-
Decide the three merge-bot inputs the design leaves open. The
delete-branchdefault, the Dependabot semver-major filter two repos carry, and arequiredHubUsesaudit contract.- Blocked by - Nothing, and each is a maintainer call rather than a finding.
- Issue - None filed.
- Checked -
developat7c67328on 2026-08-15, against the 16 downstream copies read for the design. - Open - All three, stated in
docs/reusable-workflows.md"Open Decisions". - Settled - Neither blocks adoption:
delete-branch: falseis the task default and adopters opt in, and the semver filter is a D8.1 conformance question for the two repos that carry it.
-
Bring the Docker repos onto one multi-stage Dockerfile shape. The build stages are inconsistent across the five Docker repos, and that is Dockerfile content rather than workflow content, so it rides beside the workflow migration rather than inside it.
- Blocked by - Nothing.
- Issue - None filed.
- Checked - Not measured. Raised by the maintainer on 2026-08-15 while reviewing the Docker family design, and the first action is a read of the five Dockerfiles.
- Open - Whether the shape is a
CODESTYLE.mdsection, adockertype check, or both.
One pull request, after a measurement, stating what change size licenses and whether a local adversarial pass earns its place, which are one question because both are about where review cost goes.
State measure. Touches GOVERNANCE.md branching or review guidance, once the numbers exist. Cost a measurement first, then one hub edit plus a carried re-vendor.
-
Measure review rounds against pull request size, and decide what the number licenses. The data needs no new instrumentation, since the review history carries it.
- Blocked by - Nothing.
- Issue - None filed.
- Checked -
developat1ed0cc8on 2026-08-03. - Open - The threshold, expressed as the size at which a change is split rather than as advice to keep changes small.
- Settled - For each recent pull request the record carries the diff size in files and lines, the number of rounds, and the findings per round, counting suppressed findings alongside threaded ones because they are the majority of what these loops produce.
- Settled - Two confounds bound any line drawn from the numbers, that a large change is usually also a novel one so size and unfamiliarity move together, and that a round finding something new is the reviewer working rather than evidence of a problem, so the metric is findings a smaller first cut would have surfaced earlier.
-
Try local defensive-review subagents as a first pass, and measure what the pass is worth. One agent per lens rather than one general reviewer.
- Blocked by - Nothing.
- Issue - None filed.
- Checked -
developatb82c1a3on 2026-08-05. - Open - Whether the overlap is large enough to shorten the remote loop rather than to add a step in front of it, which is what running both for a stretch measures.
- Settled - The remote loop is where most of a session's tokens and wall-clock go, and it delivers findings one round at a time, which is the slowest available way to learn that a change had five problems.
- Settled - The trap is that a local pass finding nothing reads exactly like a clean change, and the next inference is that the remote review can be skipped, which is the one outcome the review contract exists to prevent, so the local pass is an input to the loop and never a substitute for the round the merge gate requires.
One pull request routing the disproof record from the provider-agnostic contract, so an agent that never opens the provider runbook still knows where a proof lives after the thread closes.
State ready. Touches GOVERNANCE.md "PR Review Etiquette". Cost one hub edit plus a carried re-vendor of a byte-locked section, which is why it is not folded into the change that built the record.
- State that a disproof is recorded where it survives the pull request, not only in the thread. The record exists in
.github/copilot-instructions.md"Disproved Claims" and nothing agent-agnostic points at it.- Blocked by - Nothing.
- Issue - None filed.
- Checked -
developat756a53eon 2026-08-07, where outcome 2 of "Every Finding Ends in an Action" ends at the thread, "Responding and Resolution Expectations" requires the proof and says nothing about where it then lives, and the only pointer to the runbook is scoped to provider mechanics. - Open - Whether the destination is named in the byte-locked text at all, since a repository is free to keep its record elsewhere and a rule naming one file is a rule that has to be true in every copy.
- Settled - The write side is where the gap bites rather than the read side, because an agent following the loop is already routed to the runbook for mechanics and an agent posting a decline is routed nowhere.
- Settled - "Durable Knowledge and Self-Improvement" already requires durable knowledge to reach a committed file, so this states where one class of it goes rather than adding an obligation.
One pull request, after a survey, deciding whether anything stands between this fleet's review loop and the raw prose of a Copilot review. Today scripts/pr_review.py reads the review body as text and holds a vetted inventory of the headings, collapsed sections, metadata labels and coverage wordings it recognizes, blocking on anything it does not. That design is correct for a prose surface and it carries a cost the maintainer has accepted deliberately: a wording change at GitHub blocks every open pull request in the fleet at once, until the inventory is updated. The cost is worth paying against a reviewer silently missing a raised finding, which is the failure it replaces, but it is worth paying only for as long as prose is the only surface on offer.
State measure. Touches scripts/pr_review.py and the runbook section in .github/copilot-instructions.md, once the survey says whether there is anything to move to. Cost a survey first, then either nothing or a rewrite of the reading layer, which is the larger of the two outcomes and the reason the survey comes first.
-
Find out whether GitHub publishes a structured form of a Copilot review, and decide whether to read that instead of the prose. A schema, an API surface, a published payload, or a maintained library, anything that would make a wording change a non-event rather than a fleet-wide block.
- Blocked by - Nothing. The prose reader ships either way, so this decides what replaces it rather than whether the loop has a gate.
- Issue - None filed here, and the ask is filed upstream as GitHub community discussion 204320, which asks for a versioned machine-readable schema carrying severity, category, suggestion and resolution state, rather than the human-facing prose an integration has to infer those from. It is unanswered, so it is a place to watch rather than a dependency to wait on. The prose reader and its vetted inventory shipped under #607, which is the change this would supersede.
- Checked -
developat20916adon 2026-08-07, reading the live GraphQL schema by introspection and one review over REST, against the reader inscripts/pr_review.py. - Open - Whether
bodyHTMLis a better surface than the Markdown body, since it arrives as a rendered tree whose structure survives a change in Markdown syntax, while leaving the wording drift the inventory exists for exactly where it is. - Open - Whether any third-party library tracks this output, and whether depending on one is acceptable at all, given that
scripts/README.mdholds these scripts to the standard library with no third-party packages. - Open - Whether the review's own inline threads and their metadata carry enough to derive coverage and suppression without reading the body, which would narrow the prose surface rather than replace it.
- Settled - The public API carries no structured Copilot review as of the date above. GraphQL
PullRequestReviewexposesbody,bodyTextandbodyHTMLand no field naming a finding, a file count, or a withheld section, and REST returns the same prose body beside its ids and its state. - Settled - The only Copilot-named types in the GraphQL schema are
CopilotCodeReviewParametersand its input form, which configure review-on-push inside a branch ruleset and describe nothing about a review that has run, so the schema search that looks promising by name answers a different question. - Settled - A negative finding is the deliverable as much as a positive one, and it is recorded here rather than re-derived, since the reading layer's design rests on prose being the only surface and that premise is worth re-checking rather than assuming.
-
Find out which file a partial round skips, and why re-requesting never clears it. The coverage reading shipped in #608 blocks on a partial round, and the record says the state is durable rather than transient.
- Blocked by - Nothing, though it is research rather than a change, and the reader already reports the state correctly.
- Issue - #623, filed from a downstream repository against the
PARTIALcaveat's claim that the reviewer names no file list. The reading that surfaces the state shipped under #607. - Checked -
developat674a27aon 2026-08-08, measured over 348 Copilot review bodies on the newest 120 pull requests here and 121 on the fleet's Blog repository, each read against the pull request's own changed-file list rather than against its counts alone. - Settled - The reviewer does name a file list, and the caveat saying otherwise was wrong. It is a
| File | Description |table carried by 91 of the 348 bodies, and every table row in the corpus belongs to one of those tables. - Settled - The table names the unread file on exactly one round of the seven, which states 16 of 17 and names 16, omitting
GOVERNANCE.md. That round is also the only evidence on record that the unread file is a real file rather than an artifact of counting. - Settled - It cannot be read as coverage anywhere else. It names the whole changed set on partial and fully covered rounds alike, including all seven partials on Blog, while one round here states 61 of 62 and names 50, another states 33 of 33 and names 32, and a third names
GOVENANCE.md, the reviewer's own spelling and a path no diff carries. A reading identical under both outcomes discriminates neither. - Settled - Three of the four partials here carry their table on the round before a push, describing the diff that push replaced, so the comparison is head-scoped like the counts and reports no table rather than a stale list of unreviewed files.
- Open - Whether a partial round is worth escalating to GitHub at all. One named file on one round is a starting point rather than the pattern an escalation needs.
- Settled - It is durable rather than flaky. Four pull requests and seven rounds (#476, #479, #592, and the #609 promotion), and every later round repeated the identical ratio. A re-request has never cleared one, so the remedy the digest first stated was wrong and now says so.
- Settled - Size does not predict it. The partials changed 502, 629 and 961 lines, while fully covered pull requests here reach 33 files and 2,219 lines.
- Settled - The reviewer counts the file and does not read it, rather than losing it earlier. The stated denominator equals the API's own
changedFileson 103 of 104 pull requests, the exception being one whose branch shrank between rounds. - Settled - Splitting remains a real remedy for a feature branch and is unavailable for a promotion, whose head is
develop, so a promotion carrying a partial round is a maintainer decision by construction.
The review loop ends by replying on a thread and resolving it, and both halves failed on one pull request in ways the runbook describes nowhere. The resolve mutation was refused by the agent harness's own permission layer before any request left the machine, seconds after the reply mutation carrying the identical thread id had succeeded, so the refusal was neither GitHub's nor the id's. Handing the resolve to the maintainer then failed a second time, because the digest names a thread by its PRRT_ node id, that id appears nowhere in the GitHub interface, and the person asked to resolve it could not find what to click.
State ready. Touches scripts/pr_review.py, the runbook section in .github/copilot-instructions.md, and OPERATIONS.md. Cost one pull request, since the query change is one field and the runbook change is one paragraph.
-
Carry a thread's own web address beside its node id, so a resolve can be handed to a person.
Q_THREADSselectsid,isResolved,path,lineand the first comment'sauthorandbody, and not itsurl, so the digest can name a thread and cannot point at it. Selectingurland printing it beside the id makes the hand-off one click.- Blocked by - Nothing.
- Checked -
developat0e4a1c2on 2026-08-08, readingQ_THREADSinscripts/pr_review.pyagainst the digest line that consumes it. - Detail - The two identifiers are not interchangeable and neither is derivable from the other without a query. A
PRRT_node id is what a mutation takes, and a#discussion_rfragment is what the web page anchors on. - Detail - The evidence is #620, where a thread was handed over by node id and the reply was that it could not be found.
-
Give the runbook a shape for a write the harness refuses, which it currently has none for. Its list of dead paths is entirely GitHub's own refusals, a silent no-op, a 422, and the wrong bot login for the API in use, so a local refusal matches none of them and reads as a bad identifier, which invites the retry a blocked write must never get.
- Blocked by - Nothing.
- Checked -
developat0e4a1c2on 2026-08-08, against the known-non-working-paths list in the runbook. - Detail - The distinguishing evidence is that a reply on the same thread id, in the same session, had already succeeded and returned a comment url, so the identifier was demonstrably good.
- Detail - What cleared it was a permalink and a human click, and the durable remedy is a permission rule in host settings. That is host state rather than repo content, so it belongs in the runbook as a note rather than in a committed configuration file.
-
Confirm a resolve by re-reading the thread rather than by the mutation returning.
replyalready exits 63 where the resolve did not report the thread resolved, which is the right shape, and a loop drivinggh apiby hand gets no exit code at all and so cannot notice. The rule worth writing down is that the state is the evidence.- Blocked by - Nothing.
- Checked -
developat0e4a1c2on 2026-08-08, reading the exit-code table in thescripts/pr_review.pymodule docstring. - Detail - This is the failure the suppressed-findings count already exists for, where a step that stopped running reads exactly like a step that passed.
One pull request adding the observer the fleet has no equivalent of, reading merged pull requests across the fleet and resolving every changed path against what the hub declares it owns. The tools today read standing state, so a divergence is visible only once it is already there, and a repo-local file the manifest never names is invisible at every stage.
State ready. Touches a new spec/carry_watch.py with its self-test, reports/, AUDIT.md, and .github/workflows/validate-task.yml. Cost one hub script, hub-only, plus a first run whose output is a triage backlog rather than a change.
- Read merged fleet pull requests and classify each changed path against the manifest. The gap is a whole reading rather than a missing field, since nothing anywhere enumerates pull requests.
- Blocked by - Nothing.
- Issue - None filed. #633 is the instance that prompted it, raised by a downstream agent after the maintainer noticed it editing hub-managed CI files, and nothing mechanical had reported that.
- Checked -
developatc2ce145on 2026-08-08, readingspec/files.json,spec/divergences.jsonandregistry/repos.json, and running the enumeration query live against the owner. - Open - How a window wider than a thousand results is split, since the GitHub search API caps there and a silent truncation is the false clean this whole class of tool exists against. The split has to be visible in the output rather than inferred.
- Open - Whether a
SECTIONorCONTRACTclassification reads content in the same pass or defers to a human, since the path alone says a carried file moved and not which region of it. - Settled - The enumeration is one query rather than a per-repo loop, measured live:
search(query: "org:ptr727 is:pr is:merged base:main merged:>=<DATE>", type: ISSUE)returned 112 pull requests over a fortnight with per-pull-requestfilesandrepositoryinline. Thebase:term comes from each repo's registrygroundTruthBranchrather than a hardcodedmain. - Settled - It belongs in
spec/besidespec/fidelity_honesty.py, which is its sibling in every respect that decides placement, being owner-initiated, absent from CI, an importer ofauditas a library, and a writer of a generated report.scripts/holds gates run against one repo named by--root. - Settled - The classes are
OVERSTEPfor averbatimwhole unit,SECTIONfor a path declaring verbatim sections,CONTRACTfor aninterfaceunit,GAPfor a path the hub tracks that the manifest never names, andCANDIDATEfor a path absent from the hub changed in a pull request that also touched one of the others. - Settled -
intentandpresenceunits are deliberately not watched, being downstream-owned by design, and that exclusion is what makes suppression keyed on the ledger correct rather than over-broad. - Settled -
GAPplusCANDIDATEis the pair that means a downstream wired local tooling into a workflow the hub authored, which is exactlyptr727/Blog#69: it changed.gitattributes,.github/workflows/validate-task.ymlwhich is a ledgergapsentry dispositionedinvestigate, and achecks/check-eol-pins.pythe hub has never heard of. A sibling pull request shows the same shape over a wholechecks/tree. - Settled - Triage needs no new store.
spec/divergences.jsonalready carriesupstream-candidatein its disposition vocabulary, meaning the downstream carries an improvement the hub should adopt, and nothing in the ledger uses it today. A dispositioned pair prints with its disposition and everything else rendersUNTRIAGED, which is whatreports/divergences.mdalready does. - Settled - The rule goes in
AUDIT.mdsection 9 rather thanGOVERNANCE.md, whose sections are verbatim fleet law, so an edit there puts every downstream repo into verbatim drift until re-vendored for a sentence that is procedure rather than law. - Settled - Two floors are not optional. A run reading zero pull requests reports that rather than a clean zero, and a repo the search surfaces with no registry entry is reported separately, which feeds "Registry Membership Coverage" above.
- Settled - This is not the deferred audit automation recorded under "Standalone Chores". That entry rejected three scheduled and hook-driven shapes on three blockers, and this is owner-run and on demand like
spec/fidelity_honesty.py, so it lands on none of them.
One pull request deciding whether the review record admits a second class of entry, for a finding raised against text the whole fleet carries rather than against one repository's own file. The record's shape and its per-repository rule are both right and neither covers this case, so the deliverable is a decision about the category rather than a rewrite of what exists.
State decision. Touches .github/copilot-instructions.md, and possibly spec/section-model.md if the answer is a new carried section. Cost one hub edit, plus a fleet-wide re-vendor only if the entries themselves become carried.
- Decide where a disproof about carried text lives, given that every repository carrying the text will meet the same finding. The record's preamble says the entries are the hub's own, that a repository carrying the file keeps the shape and the rules rather than the findings, and that it records what it has proved itself. That is correct for a finding about one repository's tree and wrong for one about a canonical every repository holds a copy of.
- Blocked by - Nothing.
- Issue - None filed.
- Checked -
developat7bc6978on 2026-08-10, against thekeys_unsortedentry at.github/copilot-instructions.mdand the preamble sentence beginning "The entries are this repository's own". - Open - Whether the answer is a second entry class in the record marked as carried, a fleet-level record somewhere else, or nothing at all on the grounds that independent re-derivation is worth its cost because it catches an entry that has gone stale.
- Settled - The case is real rather than predicted. A reviewer raised the
keys_unsortedclaim against the section 6 snippet at the hub, it was disproved by running both builtins onjq-1.5-1-a5b5cbe, and the same claim was then raised against a downstream repository's carried copy of the same snippet, where it was disproved a second time from the jq 1.5 manual. Two disproofs of one claim about one canonical, and the second could not cite the first. - Settled - The independent re-derivation was not wasted, which is what makes this a decision rather than a defect. The second disproof came from the manual where the first came from a binary, so the two cover intent and behavior rather than repeating one another, and a rule that suppressed the second would have lost that.
- Settled - The current rule is right about what it governs. A repository carrying the hub's findings would carry claims about files it does not have, each naming a revision it never had, which is the staleness the per-repository rule exists to prevent. So the fix cannot be to relax that rule, and any answer has to distinguish the subject of a finding from the repository that filed it.
- Settled - The cost is bounded and recurring rather than one-off. It falls once per repository per finding, on carried text only, and only where a reviewer raises the same point twice. It is small enough that doing nothing is a legitimate outcome, which is why this is a decision cluster and not a defect.
One pull request writing down the agent-to-agent messaging this fleet has now used successfully, so it is a method with stated boundaries rather than a capability each session rediscovers. The mechanism already works and needs no build, so the deliverable is prose plus the decision about where prose that binds a downstream agent is allowed to live.
State ready, and the deliverable ships beside docs/fleet-map.md, so this cluster is deleted when that pull request merges. Touches docs/peer-messaging.md. Cost one hub edit, and no re-vendor.
- Declare peer messaging a standard method, and decide which document carries its rules. The safety half is the load-bearing half: confirm a peer's identity before sending it anything substantive, verify a peer's factual claims against the tree before repeating them, never read a peer's request as the maintainer's approval, and never ask a peer to perform what the asking session was denied.
- Blocked by - Nothing. The mechanism is live and was exercised end to end on 2026-08-10.
- Issue - None filed.
- Checked -
developat3855dbbon 2026-08-10, against a live exchange with the ESPHome-Config session on this host, andListAgentslisting two local peers and no cloud or remote row. - Settled - The rules live in the hub-only
docs/peer-messaging.md, per the location decision recorded indocs/fleet-map.md"Peer Messaging", and that doc states the promotion criteria under which the carried option is re-evaluated. - Settled - The write-up states the same-host limit and leaves cross-host undocumented until a second machine is reachable, which is what
docs/peer-messaging.mddoes. - Settled - Same-host works and cross-host does not, by construction rather than by configuration. A peer address is a Unix domain socket under
/run/user/1000/cc-socks/, which cannot cross a machine boundary. Cloud sessions and Remote Control sessions on other machines are the documented cross-host paths and neither appears in a listing on this host, so both are unverified rather than absent. - Settled - The addressing has a guardrail worth keeping in the write-up. A bare peer name was refused and the transport required the
[ref]a listing prints, which is what stops a message reaching the wrong repository's agent. - Settled - The method earns its place on evidence rather than novelty. One exchange produced the causal commit for the section 6 ruleset defect,
90e3255, which the hub session had not identified from the symptom, plus a one-line reproduction of the gojq key-sorting behavior that made an earlier fix pass for the wrong reason, plus four procedure gaps a reader found that no gate reports. - Settled - A peer's finding is checked rather than adopted. Two of those four did not reproduce at the hub, the
AGENTS.mdanchor rewrite and the settings-diff exposure, and one did and shipped as #653. So the write-up states verification as a step rather than as a courtesy. - Settled - The boundary that matters most is not politeness but permission. A peer cannot widen what the asking session may do, so work blocked in one session goes back to the maintainer rather than sideways to another agent.
Removing the python.exe and python3.exe app execution alias stubs frees the name, which is measured. What is not measured is whether Windows puts them back. The Settings page keeps reporting both aliases as On after the files are gone, so the declared state and the on-disk state diverge, and nothing found so far says which one servicing reads. This needs a human to log out and back in, and to reboot, and to report whether the enabled copies reappear in the WindowsApps directory.
State measure. Touches host-setup/windows/install-tools.ps1 and docs/host-setup.md, both of which currently say the alias can return rather than that it does. Cost one logout and one reboot on a Windows host, then a one-line hub edit either way.
- Log out and back in, then reboot, and report whether the enabled alias copies return. The check is whether
python.exeandpython3.exeexist again directly under the WindowsApps directory, which is the copy that sits onPATH. The package's own copies under the App Installer subdirectory are always present and are not the answer.- Blocked by - Nothing, beyond the maintainer having a moment to log out and reboot.
- Issue - #1161, which this cluster's own change closes. This measurement is what remains after it.
- Checked - Branch
python3-on-windowson 2026-09-01, where the aliases were toggled on from Settings, removed byinstall-tools.ps1 -Install python, confirmed gone from the WindowsApps directory, and confirmed still reportedOnby the Settings page after that page was closed and reopened. No logout or reboot has followed, so recreation is untested rather than ruled out. - Settled - Removing the file is the only mechanism available. The store the Settings toggle reads was searched for under
AppModel\SystemAppDataand theAppModelrepository and was not found, and writing a registry location nobody has identified is the guessing the write-safety rules already forbid. - Settled - The installer re-removes on every apply rather than assuming a host stays fixed, so recreation is self-healing on the next run either way, and turning the two toggles off in Settings is the durable fix a person can apply.
- Open - Whether the prose drops to "does not return" or hardens to "returns on every logon", which is the one thing the measurement decides.
Two loaders exist so a copy-paste snippet takes a stock OS install to a configured dev host, and neither has ever been run that way. Everything either has behind it is a dry run or a read against an already-configured checkout, on a machine carrying most of the target tools already. That confirms the logic is internally consistent. It confirms nothing about a winget package id still resolving, a stock Debian netinst actually lacking curl the way the docs assume, tar.exe genuinely shipping on a given Windows image, or the interactive menu reading correctly on a real console. This needs a human watching a real run on a real fresh image and reporting back what broke, including anything that merely looked fine, since neither of those closes from a description of the logic.
State ready. Touches host-setup/bootstrap.sh and host-setup/bootstrap.ps1 and, if either run turns something up, whichever script under host-setup/linux/ or host-setup/windows/ it drives. Cost VM time on the images each entry names, and an iteration round trip per finding, since a fix this file cannot verify is a fix that needs the same fresh image again.
-
Run
bootstrap.shunattended against a fresh Debian and a fresh Ubuntu image, and again to confirm the second run is idempotent.--host --yesfinishing clean, with nothing to fix, is the signal. A re-run reporting no further changes confirms idempotency rather than assuming it.- Blocked by - VM access to a current image of each, and one still-supported older release per distribution, since the contract's floors are meant to hold there too.
- Issue - None filed.
- Checked -
developat82a87d3on 2026-08-13, where this loader has existed since #674 and carries no record of a run against an image with nothing preinstalled. - Open - Which images, who runs the pass, and whether a failure blocks the loader or is filed and worked separately, since a fresh-host pass can turn up findings well past what one pull request should carry.
-
Run
bootstrap.ps1unattended against a fresh Windows 10 image with no App Installer, and a fresh Windows 11 image, then again on each to confirm idempotency. The Windows 10 case is the one that exercises the winget-missing remedy this loader prints but has never had checked against a real console. Windows 11 is the expected common case, App Installer andwingetboth present.-Host -Yesfinishing clean on each, then a clean re-run, is the same signal as the Linux entry above.- Blocked by - VM access to both images.
- Issue - None filed.
- Checked - Branch
feature/windows-bootstrap-loaderon 2026-08-13, adding this loader for the first time. It has run under-DryRunand againstPSScriptAnalyzeron a dev machine that already carriespwsh,winget, and most managed tools, which is signal on the script's internal consistency and none at all on whether it survives a host it has not touched. - Open - Same as the Linux entry: which images, who runs the pass, and how a finding routes back.
Small work with no research to preserve, selectable one bullet at a time.
- Answer the symmetric reading of
.editorconfig, a path-specific section naming files that do not exist, which is the half of #633 theeol-coveragecheck deliberately left open. The dead-pin reading it does ship is the.gitattributesside, and the same question on the other document is not the same shape: this repo's[.github/workflows/*]and[catalog/snippets/workflows/*]sections are legitimately broad, and the issue's own first attempt at it produced false positives because the matcher did not expand brace syntax, whichscripts/repo_gate.pyalready implements. Measure the exemption against the live corpus before building the gate rather than after, since a stale exemption hands out a work list that damages correct documents, and decide whetherforward-declaredcarries across or whether an editorconfig section needs its own marker. - Audit the fleet's shell surface by size and branching, and decide per script whether Python with unit tests is cheaper. The evidence is the review record rather than a language preference, since a non-trivial shell script earns findings round after round while every gate under
scripts/carries a test file undertests/and converges in one or two. The measure is lines, branch count, and the review rounds each has cost.repo-config/configure.shand the agent-safety installer are the two worth measuring, and a bootstrap script that needs the Python it exists to install is not a rewrite worth having, which protects the installer more than the config script. - Make a table of contents standard for a long document rather than for the README alone.
spec/readme-structure.mdfixes one at README position 4 and no other hub file carries one, which leaves the three longest documents without it,CODESTYLE.mdat 516 lines,GOVERNANCE.mdat 436 andWORKFLOW.mdat 301, measured ondevelopat3d1a0b1on 2026-08-06. Settle the threshold in headings or lines so the audit can check it, and settle how it sits with the reference-link exception, since the four agent-instruction files keep inline links exactly because they are read one section at a time, which is the property that makes a contents list worth having in them. The mechanical constraint is that the list is filled by the Markdown All in One extension on save, so a file nobody opens in the editor grows a stale list, which is worse than absent because it is read as current. - Adopt the OCI annotation keys for Docker image metadata across the Docker repos, replacing the ad-hoc and label-schema keys, per #363.
- Sweep the central package-version property to
Directory.Packages.propsfleet-wide, since PlexCleaner sets it inDirectory.Build.props, off theCODESTYLE.mdcanonical. - Canonicalize Python linter-config placement on
pyproject.toml, since one cataloged repo uses a standalone ruff config plus a pyright config. Track it as a drift finding and fix it downstream. - Populate reports/ for the cataloged repos that still have no audit, since a registry
statusofcatalogedasserts a result only a committed report evidences. Nine of 22 have one, measured ondevelopat3d1a0b1on 2026-08-06. This is paced by maintainer capacity rather than blocked, since repos are brought up to spec as they are worked on. - Finish onboarding hardening, from #310, making the
AUDIT.mdaudit a required onboarding step and running the per-type cold-start self-tests tracked in reports/conformance-matrix.md. Every cold-standup cell reads not-tested today. - Decide whether the human entry points the README now carries belong in
spec/readme-structure.md, so a fleet repo is measured on them rather than reinventing them. The README routes by reader (browsing, adopting, blocked by a rule, reporting, an agent) in an optional Getting Started table, and answers adoption, divergence, and issue-reporting in the Installation, Configuration, and Questions or Issues slots the spec already orders. What is undecided is how much of that is fleet-general, since a repo shipping an application has a different reader set from a rules hub, and a per-section index was considered and declined because it trades brevity for a sync obligation to whatever the docs contain. This sits beside "The README Structure Rework" and is settled with it rather than before it. - Consider renaming this repo to reflect the audit-catalog identity, which updates badge and link URLs across the fleet.
- Revisit automating the audit, explored and deliberately deferred, recorded so the reasoning is not re-derived. Three shapes were considered, a scheduled hub-driven audit publishing each report as a workflow artifact, the same thing committing the report back, and a pull-request hook in each downstream repo auditing itself against the current hub. Three things block all of them: until the fleet reaches stasis a scheduled run reports mostly noise, since a repo mid-onboarding is expected to be non-conformant, the hub has to be stable before downstreams audit against it because a hub change lands as fleet-wide findings the same day, and the downstream half is a catch-22 since a self-auditing hook is CI instrumentation the repos that most need it do not carry. Worth reopening once the fleet is onboarded and the hub goes a stretch without carried-content changes, and the artifact shape is the one to try first since it produces evidence without committing anything.
Work that lands on a downstream visit rather than as a hub pull request, so it is not selectable here. The fleet is caught up periodically rather than after every hub change, which means a carried-content edit landing in the hub does not owe an immediate sweep and this list is expected to carry several entries at once.
Blog is the pilot. A sweep is proven there before any fleet-wide rollout, because it is the smallest tree, hugo plus source-only with no build to break, cataloged and audited on 2026-08-05, and one of only two repos carrying AGENTS.md "Fleet Bootstrap" today, so a carried-section change can be observed arriving there. The other carrier is HomeAutomation-Config, which moved from operational to release on 2026-09-24 and so exercises the same pull request path as Blog, leaving no carrier on the direct-to-develop path.
Regenerate reports/divergences.md before using it as the work list, since it is a live pass over each repo's ground-truth branch and the committed copy is only as current as its last run. A stale ledger is the same hazard as a stale exemption, in that it hands out a work list measured against a tree that no longer exists. The reason this line used to give, that the committed copy still rendered repo-config/configure.sh under a re-vendor disposition, did not survive the check: that copy already carried the retire disposition, so the warning was true of the decision rather than of the file. What the 2026-08-09 regeneration actually moved was three rows, adding AGENTS.md "Fleet Bootstrap" as divergent at Blog and HomeAutomation-Config, and widening GOVERNANCE.md "Verification Discipline" and "Workflow YAML Conventions" from one repo to four.
-
Re-vendor the changed
verbatimcontent, which is one sweep covering seven files. Every repo holding a copy of a changed section is byte-mismatched against the hub until it takes the new one, which the audit reports as stale rather than modified.- Hub state - Done, verified
developat3d1a0b1on 2026-08-06 for the sections below, with the prose batch adding five moreGOVERNANCE.mdsections, verifieddevelopatd791930on 2026-08-07. - Outstanding - The whole fleet, pilot on Blog first.
- Issue - None filed, and it is the follow-through #489 and #379 wait on.
- Rides with - The
configure.shretirement and the.editorconfigline from #353, since all three are the same visit. - Detail - In
AGENTS.md, "Context and Delegation Discipline" carries the wait rule's failure clause and "Where the Rules Live" carries a row for "Hub-Hosted Tooling". - Detail - In
GOVERNANCE.md, "Verification Discipline" carries the rule that a launched process is not a result and the rule that a change's checks are located before any is run, with CI's coverage not being that list, "PR Review Etiquette" carries the five outcomes that close a finding, "Repository Boundaries and Write Safety" carries the rule that a refused write is reported rather than re-shaped, and both "Representative Data in Agent-Authored Text" and "Hub-Hosted Tooling" are entirely new carried sections no downstream repo holds, which the audit reports as sections that never arrived rather than as drift. - Detail - Three further
GOVERNANCE.mdsections differ by a single word each, "Documentation Style Conventions", "Communicating with the User" and "Repository Details", where a format name took the capitalizationCODESTYLE.md"Markdown and Spelling" states, so they are byte-mismatched for a reason a reader of the diff would otherwise call cosmetic. - Detail - Two comment lines in
.markdownlint-cli2.jsonctook the same capitalization, and that file isverbatimandwhole, so every downstream copy is byte-mismatched on a config nothing else changed about. - Detail -
CODESTYLE.mdis the fifth file, atintentrather thanverbatim, so it reaches the fleet as a rule each repo adopts in its own copy, and the same mixed spelling waits in every downstream tree. - Detail -
.github/copilot-instructions.mdis the sixth, also atintent, where "Reply and Thread Resolution Workflow" now leads with the hub's reply helper and keeps the hand-run mutations as the cross-owner and unreachable-hub path. A repo taking the old copy is not broken by it, since the mutations it documents still work, so this rides the visit rather than gating it. - Detail - The same file's "Triggering and Polling" reads the reviewer bot's node id across the repo's newest pull requests rather than from the pull request under review, because the id is the reviewer account's own and is identical on every pull request in the repo, read as one value across all eight of the newest here on 2026-08-08. This is the other part that propagates a procedure rather than refreshing a hash, so a repo left on the old copy reads its own runbook as requiring a review on the pull request before the id can be read, and hands round 1 to the maintainer to seed through the UI whenever auto-review-on-open does not fire, which is the hand-off the mutation exists to remove.
- Detail - The prose batch rewrote punctuation in five
verbatimGOVERNANCE.mdsections, "Branching Model", "Release Model", "Documentation Style Conventions", "PR Review Etiquette" and "Workflow YAML Conventions", so every downstream copy of those five is byte-mismatched and the audit reports it as stale. No rule changed meaning, so the re-vendor is a hash refresh rather than a propagation, and a repo taking the old copy is correct on the rule while wrong on the bytes. - Detail - The #578 widening is one of the two parts of this sweep that propagate a rule rather than refreshing a hash, the runbook correction above being the other, so a repo left on the old copy is wrong on the rule and not merely on the bytes, which makes the pair the half to carry first. It touches three
verbatimGOVERNANCE.mdsections, "Branching Model", "Communicating with the User" and "Operational Repositories", and the third of those matters most on HomeAssistant-Config, the oneoperationalrepo whose copy of it is still stale since HomeAutomation-Config moved toreleaseon 2026-09-24.WORKFLOW.mdtook a cross-reference in the same change and isintent, so nothing reports it. - Detail -
WORKFLOW.mdis the seventh file andrepo-config/README.mdjoinsCODESTYLE.mdand.github/copilot-instructions.mdatintent, where a punctuation-only edit produces no hash and therefore no audit finding at all. Nothing reports these, which is why they are recorded here rather than left to the run.HISTORY.mdispresenceand is each repo's own changelog, so its one fix owes nothing downstream.
- Hub state - Done, verified
-
Adopt the merge-bot caller stub, which is one file per repo replacing the copied job bodies. The audit reports the missing
merge-botcaller job on every copy until the repo adopts, which is the work list.- Hub state - Done on
develop, where.github/workflows/merge-bot-task.ymlis the task and the hub's ownmerge-bot-pull-request.ymlis the stub. The stub a repo copies is indocs/reusable-workflows.md"Adopting the Merge-Bot", and its pin is the first hub release carrying the task, so no repo can adopt before that release. - Outstanding - Every repo whose box in the stage 1 list of
docs/reusable-workflows.mdis still unticked, which the audit's missingmerge-botjob finding also lists. That list is the tracker and names the adopters, so this entry does not. HomeAutomation-Config piloted the direct-to-developpath while it was still an operational repo, where a merge-bot run merged the Dependabot pull request ptr727/HomeAutomation-Config#412 intodevelopon 2026-09-23, and none of the repos still carrying the bodies is operational, so no pilot of that path is owed. - Issue - #521, whose hub half is done and whose sweep half this is.
- Rides with - The
verbatimre-vendor above. - Detail - The unused
GITHUB_TOKENgrants #521 names are gone with the copy, since the task declares none and the stub setspermissions: {}. - Detail - Two repos filter Dependabot by ecosystem and semver tier, and per D8.1 the filter drops on adoption unless the open decision in the cluster above lands first.
- Detail - The hub cannot prove four things about the task. They are cross-repository resolution of the pin, the first Dependabot bump of it, the
rulesinput end to end, andmerge-appitself. All four are proof items in the stage 1 list ofdocs/reusable-workflows.md, the first two naming PhotoCleaner and the other two citing a homeassistant-purpleair run.
- Hub state - Done on
-
Carry the
Local Verificationheading into every repository'sOPERATIONS.md. The heading leads the file and states what verifying a change there requires, naming the part of the repo's contract CI structurally cannot exercise, and a repo whose gates are entirely in CI says that under it rather than omitting it.- Hub state - Done, verified
developat8e10a2con 2026-08-06, wherespec/section-model.mdandSTANDUP.mddeclare six headings and this repo's ownOPERATIONS.mdleads with the section. - Outstanding - Every repo carrying an
OPERATIONS.md, which is every repo, since none holds the heading yet. - Issue - #597, filed from a downstream repo whose pre-merge gate sat under a heading of its own invention and was skipped by an agent following every carried rule correctly.
- Rides with - The
verbatimre-vendor above, since the carriedGOVERNANCE.mdrule that points at the heading lands in the same visit and neither half works alone. - Detail - The audit reports nothing here today, because
OPERATIONS.mdis presence-checked only, so a repo using none of the declared headings passes. The heading check is #523's cluster, "Content in the Wrong File", and until it ships this sweep is verified by reading each file rather than by a run. - Detail - A repo that already documents a local gate has the content and not the location, so the visit is usually a re-heading rather than new prose, and the prose it does need is the sentence naming what CI cannot reach.
- Hub state - Done, verified
-
Retire the downstream
repo-config/configure.shcopies. Delete the copy as each repo is next worked on and run the hub's script against it by name.- Hub state - Done, verified
developat3d1a0b1on 2026-08-06, wherespec/files.jsonno longer declares the file andspec/divergences.jsoncarries it under theretiredisposition. - Outstanding - Six repos, NxWitness, aiopurpleair, homeassistant-purpleair, ESPHome-NonRoot, VSCode-Server-DotNetCore and LanguageTags.
- Issue - None filed, and #580 carries the decision.
- Rides with - The
verbatimre-vendor above. - Detail - Nothing asks a repo for the file and nothing reports its absence, which makes this a visit-ordered chore rather than a gate.
- Detail - The six carried a fork predating the payload-driven check mode, which is the drift this removes rather than converges.
- Hub state - Done, verified
-
Drop the
.editorconfiganalyzer relaxation across six C# repos. The hub side is done and the tree confirms it.- Hub state - Done, verified
developat3d1a0b1on 2026-08-06, where the analyzer severity property appears nowhere in.editorconfig. - Outstanding - Six C# repos, sequenced in the issue so PhotoCleaner's 362 sites do not gate the other five.
- Issue - #353, which stays open on the downstream half alone.
- Rides with - The
verbatimre-vendor above.
- Hub state - Done, verified
-
Close out the two downstream acknowledgements that hold their issues open. Neither is hub work.
- Hub state - Done, verified
developat1ed0cc8on 2026-08-03, where the manifest gap #379 raised is closed byrepo-config/settings.jsonreachingspec/files.json, and theconfigure.shhalf has since been retired outright. - Outstanding - Financial-Modeling's acknowledgement and re-vendor for #379, and the re-vendor #489 leaves.
- Issue - #379 and #489.
- Rides with - The
verbatimre-vendor above.
- Hub state - Done, verified
-
Widen the operational lint trigger to
developon three repos. Each triggers on a pull request tomainonly and therefore runs nothing at all on a pull request intodevelop.- Hub state - Done, verified
developatb82c1a3on 2026-08-05, where the change is prose and spec, so it fixes no downstream repo by itself. - Outstanding - Three repos, ESPHome-Config, HomeAssistant-Config and Vantage-Config, one line each. HomeAutomation-Config already triggers on
developand left the operational model on 2026-09-24. - Issue - #585.
- Rides with - Nothing, since an operational repo takes its changes direct to
develop. - Detail - Confirm the workflow really does trigger on
mainalone before editing, because a repo already naming both is conformant and needs no change. - Detail - Leave the ruleset alone, since the required check stays on
mainand nothing is added torepo-config/operational/develop.json. - Detail - The evidence this is not hypothetical is HomeAutomation-Config PR 34, which merged into
developwith an empty check list and a clean mergeable state.
- Hub state - Done, verified
-
Finish the host rollout and fill the tooling matrix, which are one visit each. The rollout needs the matrix to be repeatable and the matrix is only worth filling if the rollout uses it.
- Hub state - Done for the documentary half, verified
developat1ed0cc8on 2026-08-03. - Outstanding - Four machines, WSL2 Ubuntu, the MacBook Air and both ThinkPads, plus any headless or cron environment running with the token. macOS needs someone on that platform, the Proxmox question is whether that host also runs containers which decides whether Docker is required there, and the engine-inside-the-distro variant of the WSL2 Docker cell is unverified.
- Detail - The Windows half is a visit rather than a visit plus an unwritten script, since
host-setup/windows/now carries the tooling and it was written and run on a Windows host. - Issue - #365 and #483.
- Rides with - Nothing on the hub, since the write-guard newline fix has landed on
developand a machine keeps running the old hook until the installer is re-run there. - Detail - A ticked row means the host-wide rules text and not the hook, since only running the installer deploys both layers, and the proxmox host proved that distinction by carrying the documentary half alone for eight days on the machine where the incident originated.
- Detail - The prose comment batch rewrote comments in
gh-write-guard.pyand both installer wrappers, so every installed copy is now behind the hub by that much. The divergence is comment-only and changes no decision the hook takes, which the self-test confirms, so it is a re-run of the installer at the next visit rather than a correctness problem. - Detail - Honor the issue's own rule when filling a cell, that an unverified install command is worse than a blank, because a blank prompts a question while a wrong command produces a broken host and a false sense that setup succeeded.
- Detail - The superseded safety section from #364 still sits above the canonical block in this host's rules file, so the two overlap. Removing it is a judgment call on a per-machine file, which is why it is surfaced rather than applied.
- Hub state - Done for the documentary half, verified