Give the fleet a bootstrap entry point that routes by repo state - #536
Conversation
A downstream agent could not discover the hub. AGENTS.md is carried at intent and the hub's only self-reference was a sentence describing the hub, which is false about a downstream repo, so an agent adapting the file correctly deleted it. The pointer was destroyed by the carry mechanism working as designed. Four repos now hold no hub reference at all, and one holds no AGENTS.md either, so no in-repo pointer could have reached it. Three layers, because no single one covers every state a repo is in. AGENTS.md gains a verbatim Fleet Bootstrap section that names the hub and routes by what the repo actually holds rather than what it should hold. Verbatim is the point: intent is what let the last pointer be adapted away. It is 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. host-setup gains a claude-md-fleet.md snippet installed as its own marker block. Host-wide coverage is what reaches a repo carrying no instruction set. It is separate from the safety block because that block's own text says nothing in it widens a permission, and this one enables. AUDIT.md states that it measures a repo whose baseline arrived, and sends an empty or partial repo to STANDUP first, since auditing a repo with nothing to audit reports all absences and reads as catastrophe rather than as a repo never stood up. Also fixes a prose_lint false positive found while writing this: a step marker opening a comment was read as a sentence terminator, so a numbered step counted as two sentences. Measured across the tree, 415 findings to 409, six false positives removed and three genuine lowercase openings surfaced and fixed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a durable fleet bootstrap entry point that survives downstream adaptation and provides consistent routing guidance based on a repo's actual state, plus supporting host-wide bootstrap content and a prose-lint false-positive fix.
Changes:
- Add a byte-locked
AGENTS.md"Fleet Bootstrap" section and wire it into the section/fidelity spec so audits can detect drift. - Extend the agent-safety host installer to manage two independent
CLAUDE.mdmarker blocks (safety + fleet bootstrap) and document the new behavior. - Fix
prose_lint.pycomment sentence detection to ignore numeric step markers at the start of comment prose.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| spec/section-model.md | Records the new AGENTS.md "Fleet Bootstrap" section classification as verbatim. |
| spec/files.json | Adds "Fleet Bootstrap" to the required verbatim sections list for AGENTS.md. |
| scripts/prose_lint.py | Strips leading numeric step markers before comment sentence/run-on checks to avoid false positives. |
| host-setup/agent-safety/README.md | Updates docs to describe the new two-block CLAUDE.md installation and adds POSIX verification for both blocks. |
| host-setup/agent-safety/install.py | Updates installer to manage and update two independent marker-delimited blocks in CLAUDE.md. |
| host-setup/agent-safety/gh-write-guard.py | Comment-only clarifications for the write-guard checks (no behavior change). |
| host-setup/agent-safety/claude-md-fleet.md | Adds a new host-wide fleet-bootstrap marker block with routing guidance to the hub/template. |
| AUDIT.md | Clarifies the audit’s assumption that an instruction set is already present; otherwise route to STANDUP first. |
| AGENTS.md | Adds the new ## Fleet Bootstrap section as the in-repo entry point for routing by repo state. |
Uh oh!
There was an error while loading. Please reload this page.
The POSIX block gained a fleet-bootstrap check and the PowerShell one did not, so a Windows host following the README would confirm half the install and read it as complete. That is the failure the kit's own notes already record from the install.ps1 case, where a cell was verified while the line it documented never was. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.
Suppressed comments (1)
scripts/prose_lint.py:587
- The new ENUM_PREFIX handling changes comment-wrap/comment-case behavior for numbered step markers, but there is no regression test covering the reported false-positive shape (e.g.
# 1. Deploy the hook.should not be flagged as a run-on). Since this repo already has extensive unit tests for prose_lint.py, please add a focused test case to lock this behavior in and prevent the false positive from returning.
# A step marker opening a comment is a label on the sentence that follows, not a sentence of its own.
# `# 1. Deploy the hook.` is one sentence, and reading the marker's dot as a terminator made it two.
# It is stripped before the sentence checks so both the run-on and the opening-case test see the prose.
ENUM_PREFIX = re.compile(r'^\d+[.)]\s+')
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.
Suppressed comments (1)
host-setup/agent-safety/claude-md-fleet.md:4
- This new Markdown file appears to be committed with LF line endings, while the repo default is CRLF for .md (see .editorconfig [] end_of_line=crlf and no *.md override). This will likely fail editorconfig-checker and also makes the file inconsistent with the other host-setup/agent-safety *.md files (which contain CR characters on each line). Convert this file to CRLF before merging.
<!-- fleet-bootstrap v1 start -->
## Fleet Governance Entry Point (Any Project, Every Session)
Repositories under the `ptr727` owner are governed by a shared template at `github.com/ptr727/ProjectTemplate`, which holds the canonical rules, the machine-readable spec, the repository registry, and the procedures for standing a repository up and bringing one back to conformance. Fetch that repository before acting on anything about conformance, carried content, repository settings, or standing a repository up. This section is host-wide rather than per repository, deliberately, because the repositories that most need it are the ones carrying no instruction set to point the way, and a pointer that lives only inside a conformant repository is a pointer that is present exactly when it is not needed. A repository carrying its own `AGENTS.md` states the same routing in a byte-locked "Fleet Bootstrap" section, and the two are kept in sync on purpose.
claude-md-fleet.md was authored as a new file and written with LF, where the repo's editorconfig sets crlf as the default and its sibling claude-md-safety.md is CRLF. editorconfig-checker caught it in CI. Verified the installer is unaffected rather than assumed: it reads each snippet in text mode, so CRLF normalizes on the way in, and a fresh install against a throwaway CLAUDE_HOME still writes an LF CLAUDE.md with both marker blocks present. Same cause as the TODO.md fix on the sibling branch. A file written whole carries whatever line ending the writer emitted, where an in-place edit would have preserved what was there. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ptr727
commented
Aug 3, 2026
Answering the suppressed finding from the review on
Correct on every point, including the prediction. Fixed in Verified rather than assumed that the installer is unaffected, since the snippet is consumed by code rather than only read: Two things worth recording rather than just fixing. The cause generalizes. The file was authored whole rather than edited in place, and a whole-file write carries whatever line ending the writer emitted, where an in-place edit preserves what was there. The same mistake hit This is the case the merge gate exists for. The review produced no threads at all, so a loop polling |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.
Suppressed comments (1)
host-setup/agent-safety/README.md:12
- README still says the installer updates "the CLAUDE.md block" (singular), but it now manages two separate marker blocks (agent-safety and fleet-bootstrap). This line should be updated to avoid implying only one block is managed.
- **Two marker-delimited blocks of host-wide content in `CLAUDE.md`**, loaded into every session on the machine (including ad-hoc work outside any project), installed and updated independently so one can change without rewriting the other.
- The `agent-safety` block carries restrictions alone, so nothing in it can widen a permission. `GitHub Write Safety` carries the same three write rules as behavioral guidance and mirrors the committed `GOVERNANCE.md` "Repository Boundaries and Write Safety". `Data in Agent-Authored Text` mirrors `GOVERNANCE.md` "Representative Data in Agent-Authored Text", so an agent illustrates with constructed data rather than data observed on the machine. `Authorization Scope and Memory Hygiene` bounds how a granted permission is recorded and read. Each mirrors a rule that otherwise reaches fleet repos only.
- The `fleet-bootstrap` block carries `Fleet Governance Entry Point`, which names the template repository and routes by the state a repository is actually in. It is separate precisely because it enables rather than restricts, and the safety block's own text says nothing in it widens a permission, so merging the two would contradict that. It is host-wide rather than per repository because the repositories that most need it are the ones carrying no instruction set to point the way, and it mirrors the byte-locked `AGENTS.md` "Fleet Bootstrap" section that a conformant repository carries.
… managed Both from the suppressed findings on this pull request, neither of which carried a review thread. The ENUM_PREFIX change had no test, in a file with an extensive suite, so the false positive it fixes could return unnoticed. Four cases now cover it: a marker is not a terminator across several marker shapes, stripping it does not hide a real run-on after it, stripping it lets comment-case see the real opening word where the digit previously masked it, and a decimal such as 3.13 is not a marker because the pattern requires trailing whitespace. All 157 tests pass. The README still described the installer as updating "the CLAUDE.md block" after this branch made it manage two, which is the same class of defect this branch is otherwise fixing: prose asserting an old behavior after the behavior changed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Suppressed comments (1)
host-setup/agent-safety/install.py:93
- The installer now writes a second (enabling)
fleet-bootstrapblock into the sameCLAUDE.md, but the existing safety snippet text still says "only restrictions live here, so nothing in this file can widen a permission" (seehost-setup/agent-safety/claude-md-safety.md). Once both blocks are installed, that statement becomes false/misleading becauseCLAUDE.mdwill contain enabling guidance. Please update the safety snippet wording to scope the claim to theagent-safetyblock (or otherwise clarify the boundary), so the two-block design doesn't contradict the safety text it relies on.
# 3. CLAUDE.md carries one marker block per snippet, replaced where present and appended where not.
# The two blocks install and update independently, so one can change without rewriting the other.
# The safety block states restrictions only.
# The fleet block enables, so it stays separate from a block whose own text says nothing in it widens a permission.
blocks = [("agent-safety", "claude-md-safety.md"), ("fleet-bootstrap", "claude-md-fleet.md")]
The safety snippet said only restrictions live here and nothing in this file can widen a permission, where "this file" is CLAUDE.md. Adding an enabling fleet-bootstrap block to that same file makes the sentence false, and a safety text that is observably false about its own file is worse than one that says less. The claim now scopes to the agent-safety block, which is where it was always true, and states what happens where the two meet: the restriction binds, since a pointer to what to read next never authorizes a write. The separation was reasoned about at the block level while the text spoke at the file level, so the design was right and its own description was not. Verified by installing into a throwaway CLAUDE_HOME: both marker blocks present and the scoped sentence in place. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ptr727
commented
Aug 3, 2026
Answering the suppressed finding on
Correct, and it lands on the reasoning behind this change rather than beside it. Fixed in The separation of the two blocks was argued at the block level, and that argument holds: the safety block carries restrictions, the fleet block enables, and merging them would have made the safety block contradict itself. But the sentence stating it was written at the file level, saying nothing in this file can widen a permission. Adding an enabling block to that same file makes the sentence observably false, which in a safety text is worse than saying less, because a reader who checks one claim and finds it wrong discounts the rest. The claim now scopes to where it was always true:
The last clause is the part worth having. The finding asked for the boundary to be clarified, and the boundary is not just "different blocks", it is which one wins. The fleet block routes an agent to a procedure and never authorizes a write, so every write it leads to is still governed by the rules above it, and saying so closes the gap rather than describing it. Verified by installing into a throwaway |
Uh oh!
There was an error while loading. Please reload this page.
…ndor groundwork (#542) 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.
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>
The report cited #536, #540 and #543 through `issue-*` link labels while their definitions pointed at `/pull/` URLs, so the label contradicted what it resolved to. #456 is a real issue and keeps its label. The External group is alphabetized, which the ordering had drifted from once `pr-545` was appended. 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.mdin ptr727/HolidayLights returns nothing, and that repo carries a 26-lineAGENTS.mdplus aREADME.mdand nothing else. ptr727/DevKitCIoT has noAGENTS.mdat all.The cause is the carry mechanism working exactly as designed.
AGENTS.mdis carried atintent, 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.mdgains averbatim"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.verbatimis the whole point:intentis what let the last pointer be adapted away. Declared inspec/files.jsonand classified inspec/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 throwawayCLAUDE_HOME(appendedthenupdated, markers 2 and 2 both runs).AUDIT.mdstates what it assumes. It measures a repo whose baseline arrived, and an empty or partial repo goes toSTANDUP.mdsections 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_lintfalse positive found while writing thisA 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 ingh-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 catalogedprose_lint.py --diff origin/develop: cleanmarkdownlint-cli2: 0 issuesgh-write-guard.py --selftest: PASSFollow-on, not in this PR
The
AGENTS.mdsection is new and no downstream repo holds it, which the audit reports as a missing section. It joins the re-vendor sweep already tracked inTODO.md. This is also the cheapest thing that makes #305 tractable, since nothing downstream currently pulls.