Skip to content

Give the fleet a bootstrap entry point that routes by repo state - #536

Merged
ptr727 merged 5 commits into
developfrom
bootstrap-fleet-entry-point
Aug 3, 2026
Merged

Give the fleet a bootstrap entry point that routes by repo state#536
ptr727 merged 5 commits into
developfrom
bootstrap-fleet-entry-point

Conversation

@ptr727

Copy link
Copy Markdown
Owner

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.

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>
CopilotAI review requested due to automatic review settings August 3, 2026 17:20

CopilotAI 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 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.md marker blocks (safety + fleet bootstrap) and document the new behavior.
  • Fix prose_lint.py comment 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
FileDescription
spec/section-model.mdRecords the new AGENTS.md "Fleet Bootstrap" section classification as verbatim.
spec/files.jsonAdds "Fleet Bootstrap" to the required verbatim sections list for AGENTS.md.
scripts/prose_lint.pyStrips leading numeric step markers before comment sentence/run-on checks to avoid false positives.
host-setup/agent-safety/README.mdUpdates docs to describe the new two-block CLAUDE.md installation and adds POSIX verification for both blocks.
host-setup/agent-safety/install.pyUpdates installer to manage and update two independent marker-delimited blocks in CLAUDE.md.
host-setup/agent-safety/gh-write-guard.pyComment-only clarifications for the write-guard checks (no behavior change).
host-setup/agent-safety/claude-md-fleet.mdAdds a new host-wide fleet-bootstrap marker block with routing guidance to the hub/template.
AUDIT.mdClarifies the audit’s assumption that an instruction set is already present; otherwise route to STANDUP first.
AGENTS.mdAdds the new ## Fleet Bootstrap section as the in-repo entry point for routing by repo state.

Comment threadhost-setup/agent-safety/README.md
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>
CopilotAI review requested due to automatic review settings August 3, 2026 17:37

CopilotAI 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 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+')

CopilotAI 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 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>
CopilotAI review requested due to automatic review settings August 3, 2026 18:10
@ptr727

Copy link
Copy Markdown
OwnerAuthor

Answering the suppressed finding from the review on 3c1ef2b, which generated no threads. Quoting it, since a suppressed comment has no thread to reply in:

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.

Correct on every point, including the prediction. Fixed in 7eb9ec6: claude-md-fleet.md is now CRLF, matching its sibling claude-md-safety.md and the [*] default.

Verified rather than assumed that the installer is unaffected, since the snippet is consumed by code rather than only read: install.py 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 carrying both marker blocks. editorconfig-checker is clean on the branch.

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 TODO.md on #535 in the same session, from the same cause, and was fixed there in 700bc69. The risk scales with how much of a file is replaced rather than with how much of it changed.

This is the case the merge gate exists for. The review produced no threads at all, so a loop polling isResolved == false would have reported this pull request clean while a correct finding stood in the collapsed block. GOVERNANCE.md "PR Review Etiquette" already requires reading the suppressed findings for exactly this reason, and this is a clean instance of it: the only finding in the round was the suppressed one, and it was right.

CopilotAI 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 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>
CopilotAI review requested due to automatic review settings August 3, 2026 18:44

CopilotAI 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 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-bootstrap block into the same CLAUDE.md, but the existing safety snippet text still says "only restrictions live here, so nothing in this file can widen a permission" (see host-setup/agent-safety/claude-md-safety.md). Once both blocks are installed, that statement becomes false/misleading because CLAUDE.md will contain enabling guidance. Please update the safety snippet wording to scope the claim to the agent-safety block (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>
CopilotAI review requested due to automatic review settings August 3, 2026 18:49
@ptr727

Copy link
Copy Markdown
OwnerAuthor

Answering the suppressed finding on install.py:93, which carried no review thread. Quoting it:

the existing safety snippet text still says "only restrictions live here, so nothing in this file can widen a permission" ... Once both blocks are installed, that statement becomes false/misleading because CLAUDE.md will contain enabling guidance.

Correct, and it lands on the reasoning behind this change rather than beside it. Fixed in 37649fe.

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:

only restrictions live in the agent-safety block, so nothing in it can widen a permission. Another marker block in the same file may state enabling guidance, and where the two meet the restriction binds, since a pointer to what to read next never authorizes a write.

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 CLAUDE_HOME: both marker blocks present, the scoped sentence in place.

CopilotAI 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 11 out of 11 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit 69276d1 into developAug 3, 2026
7 checks passed
@ptr727
ptr727 deleted the bootstrap-fleet-entry-point branch August 3, 2026 19:05
ptr727 added a commit that referenced this pull request Aug 3, 2026
…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.
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>
ptr727 added a commit that referenced this pull request Aug 3, 2026
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>
Sign up for freeto 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

@ptr727