Conversation
A `driftNote` records a *current* deviation from the baseline, and AUDIT.md section 8 is explicit about what happens when one is resolved: > Once the deviation is resolved the note is deleted, not left describing finished work, since hand-maintained prose drifts silently otherwise. PhotoCleaner's notes carried three things that are not current deviations. This PR is purely subtractive - three claims removed, nothing added. Found while running `spec/audit.py PhotoCleaner` for that repo's round 4 conformance work. The audit does not flag these itself: its stale-note check only fires when a repo audits clean, and PhotoCleaner did not until the round landed. ## 1. The onboarding note, deleted > Baseline onboarding completed 2026-07 (...). Not conformance-complete: see `reports/photocleaner/audit.md` for the open defects. Round 4 landed and `spec/audit.py PhotoCleaner --branch develop` now reports clean at `develop@252f5c5`, so the deviation is resolved and the note goes. It is a completion record end to end, and nothing is lost that the entry does not already carry structurally in `status`, `types`, `publish`, and `requiredSecrets`. ## 2. The first-release date, deleted > ...Release is two-phase (workflow_dispatch + weekly Mon 02:00 schedule). **First release 1.0.9 published 2026-07-23.** The mechanism around it stays and is a live deviation: a static Docker Hub secret where the fleet default is OIDC, plus a two-phase release trigger. The date a past version shipped is history. ## 3. The README shape claim, deleted > ...so `README.md` carries Docker Hub shields only and gains the GitHub build and release shields when it goes public. This contradicted `spec/readme-structure.md`, which holds the README shape **constant across repository visibility** precisely so that going public is a visibility change and nothing else. PhotoCleaner's README already carries the full shield set, so the note described a reduced private variant the repo does not have and the spec does not want. There is no replacement sentence. A repo doing exactly what the spec requires earns no note, so describing the conformance would be the same category error as describing finished work. What survives in that note is the deviation itself: the repo is private, so the declared `github-release` channel is not consumer-pullable and the GitHub-sourced shields render broken until the flip. ## Result PhotoCleaner keeps two driftNotes, both describing active deviations. ## Verification - `spec/validate.py` passes (21 cataloged, 0 backlog). - Prose gate clean on the changed lines. - CRLF preserved. Authored from the downstream repo, per the rule that a downstream repo files hub findings rather than self-certifying. This changes only PhotoCleaner's own registry entry - no spec, no fleet-wide rule. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two files, one session's findings. ## `TODO.md` A flat list of twenty entries had grown past the point where the next action was visible in it. It is now grouped by the surface each entry changes, since an entry's cost is mostly set by what it touches, and every open issue is named on the entry covering the same ground so the two are read together rather than worked twice. A new opening section records **five issues verified complete against the tree**, each with the commit that fixed it, so closing them cites evidence rather than assertion. Those five are #509, #490, #489, #379 and #353, and each was re-checked line by line rather than taken at its filed state. Three new entries: - The **`prose_lint.py` scope floor**, promoted to lead its bucket, because every verdict below it is only worth reading once the gate can prove it read something. - **An agent never assumes a Docker image is still present locally**, since a background prune can remove one between two commands of the same session. The entry states the honest limit, that `docker run` re-pulls an absent registry tag by itself, so the cases that actually break are a locally built tag and any command that branches on the image being present. - The **`gh-write-guard` newline defect** found while running this sweep. `_git_subcommand_arglists` ends a `git push` argument list at `&&` but not at a newline, so a push followed by any later line resolves that line's tokens as refspecs. Measured: one branch alone, five with a following `gh pr create` whose base is `develop`. It over-blocks rather than under-blocks, so it is not a safety hole, but the denial tells the agent it is bypassing a branch rule with admin power when it is pushing an ordinary feature branch, and a safety hook that cries wolf stops being read. ## `reports/divergences.md` Regenerated. The committed copy was dated 2026-07-22 and predated the router split, so it still filed "Repository Boundaries and Write Safety", "Git and Commit Rules" and "Verification Discipline" under `AGENTS.md` when all three now live in `GOVERNANCE.md`. It was handing out a work list measured against a tree that no longer exists. The refresh corrected the propagation picture materially: **only two repos carry `GOVERNANCE.md` at all**, verified against the live repos rather than inferred. The router split never reached the fleet, so what reads as a section re-vendor is, for seventeen repos, adopting the split itself. That is #305's problem rather than a sweep, and the entry says so. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
## The problem, and why it was self-inflicted
A downstream agent opening a fleet repo has no way to discover this one.
Measured, not assumed: `grep ProjectTemplate AGENTS.md` in
ptr727/HolidayLights returns nothing, and that repo carries a 26-line
`AGENTS.md` plus a `README.md` and nothing else. ptr727/DevKitCIoT has
no `AGENTS.md` at all.
The cause is the carry mechanism working exactly as designed.
`AGENTS.md` is carried at `intent`, so downstream adapts it. The hub's
only self-reference is a sentence describing *the hub*
("**ProjectTemplate** exists for agent enablement across a fleet..."),
which is false about a downstream repo, so an agent adapting the file
**correctly deleted it**. No amount of prose discipline fixes that.
## Three layers, because no one of them covers every state
A repo is in one of four states, and an entry point has to work in all
four: no repo yet, a repo with no instruction set, a repo with a partial
or stale set, and a conformant repo.
**`AGENTS.md` gains a `verbatim` "Fleet Bootstrap" section.** It names
the hub, and routes by what the repo *actually holds* rather than what
it should hold, since those differ precisely when this matters.
`verbatim` is the whole point: `intent` is what let the last pointer be
adapted away. Declared in `spec/files.json` and classified in
`spec/section-model.md`, so the audit reports its absence as **drift**
rather than losing it silently. That is the self-verifying half.
**`host-setup/agent-safety/claude-md-fleet.md`**, installed as its own
marker block in `~/.claude/CLAUDE.md`. Host-wide is what reaches a repo
carrying no instruction set, or no repo at all, which is the case an
in-repo pointer structurally cannot cover. It is a **separate block**
from the safety one, not an addition to it, because that block's own
text states it carries restrictions alone and nothing in it can widen a
permission, and this section enables. The installer now handles both
blocks independently, verified idempotent against a throwaway
`CLAUDE_HOME` (`appended` then `updated`, markers 2 and 2 both runs).
**`AUDIT.md` states what it assumes.** It measures a repo whose baseline
arrived, and an empty or partial repo goes to `STANDUP.md` sections 1A
and 2 first. Auditing a repo with nothing to audit reports all absences,
which reads as catastrophe rather than as a repo that was never stood
up.
## Also fixed: a `prose_lint` false positive found while writing this
A step marker opening a comment was read as a sentence terminator, so `#
1. Deploy the hook.` counted as two sentences while the identical text
without the prefix passed. Verdicts diffed across the whole tree rather
than assumed: **415 to 409**, six false positives removed and three
genuine lowercase openings surfaced and fixed in `gh-write-guard.py`. A
real run-on after a marker still fires, checked explicitly.
Mangling correct comments to satisfy a broken gate is the failure this
repo has already learned about, so the gate was fixed instead.
## Verification
- `spec/validate.py`: OK, 21 cataloged
- `prose_lint.py --diff origin/develop`: clean
- `markdownlint-cli2`: 0 issues
- `gh-write-guard.py --selftest`: PASS
- installer: fresh install and re-run, both blocks correct
## Follow-on, not in this PR
The `AGENTS.md` section is new and no downstream repo holds it, which
the audit reports as a missing section. It joins the re-vendor sweep
already tracked in `TODO.md`. This is also the cheapest thing that makes
#305 tractable, since nothing downstream currently pulls.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two limits of the `gh-write-guard` hook, both found while driving
seventeen pull requests through the review loop in one session, and
neither written down anywhere. They go in the kit's own "Scope and
Limits" rather than the review runbook, since they are properties of the
hook rather than of the Copilot workflow.
## A write inside a script file is unseen
The hook reads the command the agent runs. For `bash sweep.sh` that is
one string, so a `git push` or a `gh` mutation **inside** the script
reaches the server without the hook ever parsing it.
This is the wider of the two and the one worth acting on. It is a hole
an agent opens **by accident**: collecting fifteen repetitive pushes
into a loop reads as tidiness, not as switching a guard off. I nearly
did exactly that in this sweep, and stopped only because the reason to
script it and the reason not to were the same fact.
The rule that follows is short: issue each write as its own command. The
boundary is the write and not the script, so a script that reads,
computes, or prepares local commits is unaffected, which is how the
sweep's fifteen commits were actually prepared.
## A `git push` followed by a newline over-blocks
The push argument list terminates at `&&` but not at a newline, so every
token on a later line of the same command is read as a refspec. Measured
against the installed hook:
```text
'git push -u origin revendor/x'
-> [('update', 'revendor/x')]
'git push -u origin revendor/x\ngh pr create --base develop'
-> [('update','revendor/x'), ('update','gh'), ('update','pr'), ('update','create'), ('update','develop')]
'git push -u origin revendor/x && gh pr create --base develop'
-> [('update', 'revendor/x')]
```
The push then denies as a direct push to a protected branch it never
targeted. The direction is safe, since it blocks rather than admits, but
the denial tells the agent it is bypassing a branch rule with admin
power when it is pushing an ordinary feature branch. **A guard that
cries wolf is one an agent starts working around**, which is the actual
risk. The fix is tracked in `TODO.md`; this records the limit and the
workaround meanwhile, which is the same one-write-per-command rule as
above.
## Not added, because it is already there
The same session hit `gh pr edit --body` failing on this host's gh
2.46.0 with a Projects-classic deprecation error. That is **already
documented** in the review runbook, including the exact `gh api -X PATCH
... -F body=@file` workaround, so nothing is added for it. Checked
before writing rather than after, since duplicating a rule into a rules
file is a known past failure here.
## Verification
- `prose_lint.py`: clean
- `markdownlint-cli2`: 0 issues
- Both behaviours measured against the installed hook, not inferred from
reading it
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Closes the three code findings in #538. Each was raised by a reviewer reading the canonical as it landed in five downstream repos, and each was re-verified against this copy before being touched. The unifying defect: `check` is what a maintainer runs to confirm a repo is configured correctly, and it compared a fraction of what `apply` sets. ## 1. `check_settings` could report a clean audit having compared nothing ```sh done < <(jq -r 'to_entries[] | ...' "$settings_file") # before ``` A `jq` failure inside process substitution leaves the loop body unexecuted, and `set -e` does not trip on it. **Every static setting then reported as checked and passing while nothing was compared.** The payload is now parsed into a variable first, a parse failure is a loud `FAIL`, and a payload yielding no keys is a `FAIL` rather than a clean run, since a gate that finds nothing is indistinguishable from a gate with nothing to find. ## 2. An extra live rule was invisible The rule-type comparison ran one way: payload types had to exist live, and nothing checked the reverse. A rule someone added by hand in the UI, `required_linear_history` on `main` say, passed as clean. The set is now compared **both** directions. ## 3. Most rule parameters were never read Parameters were compared for two rule types, field by field. So `copilot_code_review.parameters`, `required_review_thread_resolution`, `dismiss_stale_reviews_on_push`, `strict_required_status_checks_policy` and `do_not_enforce_on_create` were all declared in the payloads and never verified. Every parameterized rule is now compared on its **whole parameters object**, with keys sorted on both sides so API key order cannot read as drift. That keeps the check payload-driven: a parameter added to a payload is audited with no change here, which is the property naming fields one at a time had quietly lost. ## The header, which was the reason none of this was obvious It claimed "every applied ruleset, setting, and security feature must match" while the next line partially walked it back. It now states what is verified and, as importantly, what is not: anything the payloads do not declare is unaudited by construction. ## Verification, by negative test A clean pass on a conformant repo proves nothing, so each assertion was made to fail on purpose: | Perturbation | Result | | --- | --- | | Remove `required_signatures` from a `main.json` copy | `FAIL 'main' rule set = ...` | | Flip `copilot_code_review.parameters.review_on_push` | `FAIL 'main' rule 'copilot_code_review' parameters match the payload` | | Malformed `settings.json` | `FAIL ... did not parse`, exit 1 (previously a clean pass) | | Empty `settings.json` | `FAIL ... declares no keys` | A live `check` against this repo passes and now additionally verifies `copilot_code_review` parameters on both rulesets, which nothing verified before. `bash -n` clean, `prose_lint` clean, editorconfig clean, LF preserved per `.gitattributes`. ## Not in this PR #538's fifth finding, that the script names `spec/secrets.json` in three places while being carried `verbatim` into repos that have no `spec/`, is left alone deliberately. It is the same open question as the `TODO.md` entry "Decide where a carried file may name hub-only machinery", which currently cites the `scripts/pr_review.py` case, and both should be settled together rather than one being patched here. ## Downstream consequence This file is carried `verbatim` with `appliesTo: "*"`, so every repo holding a copy is now stale against the hub and takes this on its next re-vendor. That is the intended order: five repos additionally need a `ruleset-*.json` payload migration before they can take the script at all, and doing that before this landed would have re-vendored them twice. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…541) Follow-up to the suppressed finding on #535, which is now merged. That finding was correct and pointed at a defect with a measurable cost. ## The defect `spec/fidelity_honesty.py` put a verbatim section whose heading is missing downstream into the same `differs` bucket as a genuine local edit (`region is None` -> `spread["differs"]`), and the report rendered both as **"hand-modified"**. The two states want opposite responses. A hand-edit is drift to reconcile against a local decision someone made. An absence is a section that never arrived, which is carried. Calling the second one the first sends a reader looking for decisions that were never taken. ## What it actually produced Seventeen repos were reported as having hand-modified `AGENTS.md > Context and Delegation Discipline` and `Where the Rules Live`. None of them edited anything: they still hold the pre-split monolithic `AGENTS.md`, so the sections are simply not there. **That claim was quoted as evidence in this repo's own backlog work before a reviewer caught it.** The conclusion drawn from it happened to be right, because absence was separately confirmed by direct reads against the live repos, but the word it was drawn from was wrong, which is the more dangerous shape. There is a second-order effect the finding also named: an absent section contributes nothing to the `history` set, so it can never be classified `stale`. That means **every newly added verbatim section reports as hand-modified across the whole fleet on the day it lands**. The new `GOVERNANCE.md > Representative Data in Agent-Authored Text` row was showing exactly that. ## The change A new `absent` bucket, with three consequences handled beyond the label itself: - **It no longer disqualifies an `intent` unit from promotion.** A missing section is not evidence of a local decision, so it should not block a unit from being promoted to `verbatim`. - **It still counts as a live divergence for burn-down**, because the section is genuinely owed to that repo. - **The untriaged section reports the two kinds separately**, since one is fixed by reconciling and the other by carrying. ## Result ```diff -- **AGENTS.md > Where the Rules Live** - hand-modified in AudioCleaner, DevKitCIoT, ... +- **AGENTS.md > Where the Rules Live** - **not carried** by AudioCleaner, DevKitCIoT, ..., so the section never arrived rather than being edited ``` Three rows change, covering nineteen repo-section pairs. ## Verification - `spec/validate.py`: OK, 21 cataloged - `prose_lint --diff`: clean - editorconfig: clean, CRLF preserved on the regenerated report - Report regenerated from a live fleet pass so it matches its source --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR promotes a set of fleet-governance and audit reliability improvements from develop to main, strengthening bootstrap discoverability for downstream agents and tightening multiple “false clean” failure modes across the repo’s conformance tooling.
Changes:
- Add a byte-locked “Fleet Bootstrap” entry point (in-repo and host-wide) that routes by the repository’s actual state.
- Improve audit/report honesty by distinguishing “absent section” vs “hand-modified” in fidelity reporting, and regenerate the divergence ledger accordingly.
- Harden configuration verification (
configure.sh check) and prose gating (prose_lint) to avoid vacuous passes and known false positives, with added tests.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
TODO.md |
Re-buckets the backlog by surface area and records verified-complete items and new governance/tooling follow-ups. |
spec/section-model.md |
Classifies the new “Fleet Bootstrap” section as verbatim and documents its rationale. |
spec/files.json |
Declares the new AGENTS.md verbatim section in the baseline manifest. |
spec/fidelity_honesty.py |
Adds an absent bucket for missing verbatim sections and updates reporting/promotion logic accordingly. |
spec/divergences.json |
Refreshes dispositions and expands one entry with measured justification for convergence sequencing. |
reports/divergences.md |
Regenerated divergence report reflecting the new absent classification and updated dispositions. |
scripts/prose_lint.py |
Strips step markers (e.g., 1. / 2)) before sentence checks to avoid false run-on detections. |
scripts/test_prose_lint.py |
Adds unit tests covering step-marker stripping and ensuring real findings are still detected. |
repo-config/configure.sh |
Makes check mode payload-driven and failure-loud (no “checked nothing” false clean) with broader ruleset verification. |
registry/repos.json |
Removes stale driftNotes text that described completed work/history rather than current deviations. |
host-setup/agent-safety/README.md |
Documents the new separate host-wide fleet-bootstrap block and verification steps. |
host-setup/agent-safety/install.py |
Installs/updates two independent marker-delimited CLAUDE.md blocks (agent-safety and fleet-bootstrap). |
host-setup/agent-safety/gh-write-guard.py |
Comment wording/format adjustments for clearer rule descriptions (no logic changes shown in diff). |
host-setup/agent-safety/claude-md-safety.md |
Clarifies that restrictions live in the agent-safety block and still bind enabling guidance elsewhere. |
host-setup/agent-safety/claude-md-fleet.md |
New host-wide fleet governance entry-point snippet (marker-delimited). |
AUDIT.md |
Adds routing guidance: run STANDUP.md first when the instruction set is missing/partial. |
AGENTS.md |
Adds the verbatim “Fleet Bootstrap” section that points to the hub and routes by repo state. |
#543) Fixes the finding on the promotion #542. A promotion's head is `develop`, which is protected, so the fix lands here and #542 picks it up when this merges. ## The finding > The header comment says check mode verifies "exactly what the payloads declare" and that anything absent from settings.json is unaudited, but the script also asserts derived/dynamic settings (has_discussions, default_branch) and security toggles in addition to payload-driven rulesets/static settings. Correct on the facts. `check_settings` asserts `has_discussions` (computed from `private`) and `default_branch` (when `main` exists), and `check_security` asserts the two Dependabot features. None of those comes from a payload. ## Why it happened, which is the part worth recording This is an **over-correction of the defect #540 fixed**, not a new one. The header used to over-promise: "every applied ruleset, setting, and security feature must match", while the code compared a fraction. #540 rewrote it to "exactly what the payloads declare", which fixed the over-claim by introducing an under-claim. A coverage note that is wrong in *either* direction misleads the person reading it to decide what still needs checking by hand. Over-claiming tells them to skip something unverified. Under-claiming tells them to re-check something already covered, and erodes trust in the rest of the note. Landing on the true statement took two passes and a reviewer, which is the honest record. ## The change The header now names the three groups `check` actually asserts, says which one is payload-driven, and states the single genuine gap rather than implying a larger one: - rulesets and static settings, driven by the committed payloads - the derived settings `apply` computes, asserted by name - the two Dependabot security features, asserted the same way - unaudited: a static setting absent from `settings.json`, because only that group is payload-driven ## Verification `bash -n` clean, `prose_lint` clean, and a live `check` against this repo still passes with all three groups asserted. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
ptr727
added a commit
that referenced
this pull request
Aug 3, 2026
Generated output only, no hand edits. `reports/divergences.md` reads each repo's ground-truth `main`, so before #542 promoted it was measuring a hub `main` that predated this session and reporting the hub as not carrying a section it had just authored. ## What moved, and how each was established **Predicted and correct.** `AGENTS.md > Fleet Bootstrap` no longer lists ProjectTemplate. The hub's own `main` carries it now, so that row was pre-promotion state rather than fleet drift. **Predicted and wrong.** I expected `repo-config/configure.sh` to fall from 9 repos to 7. It fell to **8**. ProjectTemplate dropped off correctly, but PhotoCleaner is **genuinely stale** rather than an artifact: #540 moved the canonical, so a copy that was current yesterday is behind today. That is the intended consequence of changing a `verbatim` file, flagged in #540 itself and then forgotten when predicting here. **Not predicted, and checked rather than assumed.** PhotoCleaner dropped off five carried-section stale rows and off the `GOVERNANCE.md > Representative Data` not-carried row. The cause is external to this work: that repo merged its own promotion `c457ff3` earlier today and its `main` now carries the section. An unexplained improvement deserves the same scrutiny as an unexplained regression, so it was verified against that repo's commits rather than accepted. ## What the report now says about the fleet - **19 repos owe `AGENTS.md > Fleet Bootstrap`.** The bootstrap shipped and nothing downstream has it. That is the propagation job #536 exists to make possible, and it is the honest measure of where the fleet stands. - **Financial-Modeling is the only repo still stale on the carried governance sections**, at 18 rows. - **`.markdownlint-cli2.jsonc` still lists 16 repos**, because this session's fleet merges landed on each repo's `develop` and have not promoted to their own `main`. The ledger reads `main`, correctly, so these clear as those repos promote. - **`repo-config/configure.sh` at 8**, five of which additionally need the `ruleset-*.json` payload migration before they can take the canonical at all. ## Verification Regenerated by `python3 spec/fidelity_honesty.py --report` from a live fleet pass after the promotion merged. editorconfig clean, CRLF preserved, diff-scoped `prose_lint` clean. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Six commits, all from one session. Merged as a merge commit, never squashed, per the branching model.
1ab9b32driftNotesthat described finished workbf746aaTODO.mdby the surface each entry changes, records five issues verified complete, refreshes the divergence ledger69276d1AGENTS.md"Fleet Bootstrap" section, the host-widefleet-bootstrapblock, and theAUDIT.mdrouting that sends an un-stood-up repo toSTANDUP.mdfirst530dc0dgh-write-guardlimits found by running it: a write inside a script is unseen, and a push followed by a newline over-blocks5f4c17fconfigure.sh checkverify what the payloads declare, closing a false clean where ajqfailure left every setting reported as passing while nothing was compareda549572Why this promotion matters more than most
Two of these change what the fleet is measured against, so until they reach
mainevery downstream audit compares against ground truth that predates them.#536 is the one with reach. A downstream agent had no way to discover this repository.
AGENTS.mdis carried atintent, and the hub's only self-reference described the hub, which is false about a downstream repo, so an agent adapting the file correctly deleted it. Four repos now hold no hub reference at all and one holds noAGENTS.mdeither. The replacement isverbatimso adaptation cannot remove it, declared inspec/files.jsonand classified inspec/section-model.mdso the audit reports its absence as drift rather than losing it silently.#540 closes a false clean.
check_settingsstreamed its payload from a process substitution, and ajqfailure there leaves the loop body unexecuted without trippingset -e. Every static setting reported as checked and passing while nothing was compared. Verified by negative test rather than by a clean pass: a malformedsettings.jsonnow fails with exit 1, an extra live rule fails the rule-set assertion, and a changedcopilot_code_reviewparameter fails the parameters assertion.#541 corrects a report that was actively misleading. It accused seventeen repos of hand-editing
AGENTS.mdsections they had never been given. They still hold the pre-split monolithic file, so the sections are simply absent. A hand-edit is reconciled against a local decision, an absence is carried, and the report was naming the wrong one.Fleet state behind this
Seventeen downstream re-vendor pull requests merged alongside this work, each gate-checked at merge time and each verified after. Four repos had
configure.shwithdrawn from their re-vendor when review found they carryruleset-*.jsonpayload names the canonical does not resolve, which would have leftapplyandcheckaborting on files that do not exist. That payload migration is still owed and is tracked inTODO.md.Expected immediately after this merges
The divergence ledger currently reports the hub itself as not carrying "Fleet Bootstrap" and as owing a
configure.shre-vendor. That is the ledger reading each repo'smain, honestly, before this promotion. Regenerating after the merge is what makes the report meaningful again, and theconfigure.shre-vendor list will collapse as repos take the corrected script.Verification
spec/validate.pyOK (21 cataloged), diff-scopedprose_lintclean,markdownlint-cli2clean, editorconfig clean,gh-write-guard --selftestPASS, 157prose_lintunit tests pass.