Skip to content

Promote develop to main: fleet bootstrap, gate honesty, and the re-vendor groundwork - #542

Merged
ptr727 merged 7 commits into
mainfrom
develop
Aug 3, 2026
Merged

Promote develop to main: fleet bootstrap, gate honesty, and the re-vendor groundwork#542
ptr727 merged 7 commits into
mainfrom
develop

Conversation

@ptr727

@ptr727 ptr727 commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Six commits, all from one session. Merged as a merge commit, never squashed, per the branching model.

Commit PR What it does
1ab9b32 #537 Deletes three stale PhotoCleaner driftNotes that described finished work
bf746aa #535 Buckets TODO.md by the surface each entry changes, records five issues verified complete, refreshes the divergence ledger
69276d1 #536 Adds the byte-locked AGENTS.md "Fleet Bootstrap" section, the host-wide fleet-bootstrap block, and the AUDIT.md routing that sends an un-stood-up repo to STANDUP.md first
530dc0d #539 Records two gh-write-guard limits found by running it: a write inside a script is unseen, and a push followed by a newline over-blocks
5f4c17f #540 Makes configure.sh check verify what the payloads declare, closing a false clean where a jq failure left every setting reported as passing while nothing was compared
a549572 #541 Reports a verbatim section that never arrived as absent rather than as hand-modified

Why this promotion matters more than most

Two of these change what the fleet is measured against, so until they reach main every 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.md is carried at intent, 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 no AGENTS.md either. The replacement is verbatim so adaptation cannot remove it, 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.

#540 closes a false clean. check_settings streamed its payload from a process substitution, and a jq failure there leaves the loop body unexecuted without tripping set -e. Every static setting reported as checked and passing while nothing was compared. Verified by negative test rather than by a clean pass: a malformed settings.json now fails with exit 1, an extra live rule fails the rule-set assertion, and a changed copilot_code_review parameter fails the parameters assertion.

#541 corrects a report that was actively misleading. It accused seventeen repos of hand-editing AGENTS.md sections 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.sh withdrawn from their re-vendor when review found they carry ruleset-*.json payload names the canonical does not resolve, which would have left apply and check aborting on files that do not exist. That payload migration is still owed and is tracked in TODO.md.

Expected immediately after this merges

The divergence ledger currently reports the hub itself as not carrying "Fleet Bootstrap" and as owing a configure.sh re-vendor. That is the ledger reading each repo's main, honestly, before this promotion. Regenerating after the merge is what makes the report meaningful again, and the configure.sh re-vendor list will collapse as repos take the corrected script.

Verification

spec/validate.py OK (21 cataloged), diff-scoped prose_lint clean, markdownlint-cli2 clean, editorconfig clean, gh-write-guard --selftest PASS, 157 prose_lint unit tests pass.

ptr727 and others added 6 commits August 3, 2026 11:00
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>
Copilot AI review requested due to automatic review settings August 3, 2026 21:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread repo-config/configure.sh Outdated
#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>
Copilot AI review requested due to automatic review settings August 3, 2026 21:25

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit 3a7cc64 into main Aug 3, 2026
7 checks passed
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants