Conversation
## Problem
[AUDIT.md](../blob/develop/AUDIT.md) section 6's ruleset comparison
projected `bypass_actors` on both sides and sorted it with
`sort_by(.actor_id)`. No committed payload declares that key, so the
committed side did not mis-compare, it failed outright:
```
jq: error (at repo-config/main.json:58): Cannot iterate over null (null)
```
Process substitution then hands `diff` an empty left side, so the whole
live payload reads as an addition and the `|| echo DRIFT` branch fires.
**Every ruleset on every repo reported drift**, from the snippet rather
than from the repo. Reported by the agent resyncing ESPHome-Config.
Pre-fix, against Blog, which is in sync:
```
0a1,68
> {
> "bypass_actors": [ { "actor_id": 5, "actor_type": "RepositoryRole", "bypass_mode": "always" } ],
...
develop: DRIFT
main: DRIFT
```
## Why this field
`spec/audit.py`'s `RULESET_SUBSET` already excludes it, and [the comment
above it](../blob/develop/spec/audit.py#L42) records that including it
once made every repo report a ruleset `DEFECT`.
`repo-config/configure.sh` is the authority both must agree with:
`apply` reads the live list and writes it back unchanged, `check`
reports it and asserts nothing, because who may bypass a ruleset is a
human decision taken in the UI. The mechanized path was corrected and
the prose snippet beside it was not.
## Change
Three surfaces stated the old policy, so the class is swept rather than
the instance:
- `AUDIT.md` section 6 compares `name`, `target`, `enforcement`,
`conditions`, `rules`, matching `RULESET_SUBSET`, and states why the
field is outside it.
- `docs/repo-config-carry.md` payload regeneration no longer captures
`bypass_actors`. Capturing it baked one repo's bypass list into the
canonical every other repo diffs against.
- `repo-config/README.md` no longer lists the field as part of the
compared subset.
## Verification
The snippet is extracted from the shipped `AUDIT.md` bytes rather than
retyped, placeholders substituted, and run against three repos including
two operational ones (exercising the `operational/develop.json`
selection):
```
=== Blog === develop: in sync main: in sync
=== ESPHome-Config === develop: in sync main: in sync
=== HomeAutomation-Config === develop: in sync main: in sync
```
Gates: `prose_lint.py --diff HEAD` clean, `repo_gate.py --check eol` and
`--check eol-coverage` clean, `markdownlint-cli2` 0 issues on all three
files.
Note the committed downstream copies of these payloads are separately
stale against the hub canonical (`reports/divergences.md`), which is a
re-vendor drift. This PR does not touch that: the live rulesets match
the hub's payloads, so enforcement was never affected.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>## Problem Nothing detected a file a repo carries and should not. Every file check in `spec/audit.py` reads a path the manifest **declares**, so the manifest's complement was invisible. The only record of a deletion was a hand-written `retire` disposition in `spec/divergences.json`, surfaced through a manifest-gap pass that read a single reference adopter (`Financial-Modeling`). That pass understated the work. It named **six** repos carrying `repo-config/configure.sh`. Fifteen do. ## Approach The detector is derived, not listed: **the hub's git-tracked paths minus the `spec/files.json` baseline** is what the hub hosts and no repo carries. A file dropped from the manifest starts being reported on the next run, with no retirement list to remember to edit. - `spec/audit.py` gains `hub_only_paths()` and reports each hub-hosted path the audited repo's ground-truth branch also holds, as a new `carried-scope` cross-cutting dimension (`carried.hubonly.absent`). - `spec/fidelity_honesty.py`'s gap pass runs over the **whole fleet** instead of one adopter, and each gap names its carriers, so a disposition reads as a work list from live state rather than a hint. - `git ls-files` and the tree read move into `audit.py` as shared helpers, since both tools needed the same subtraction. ## A hit is a candidate, not a verdict The match is on path alone, and the first fleet run proved that matters twice: | Path | Reality | |---|---| | `scripts/README.md` (KiCadLibrary) | Documents its **own** KiCad tooling (`common.py`, `verify_library.py`) beside the scripts it describes | | `.husky/pre-commit` (7 repos) | Each runs that repo's own formatters (CSharpier, `dotnet format`); the hub's runs the doc gates | Both were **read** before being dispositioned `accepted`. So only `retire` asserts a deletion, and `AUDIT.md` section 10 states that where the fix gets applied, because deleting an untriaged hit destroys work the hub never owned. `TODO.md` is accepted on the same basis: `STANDUP.md` section 2 names it as a destination for a repo's own backlog, so the manifest omits it deliberately. ## Fleet result ``` repo-config/configure.sh (15) retire <- the real deletion .github/workflows/publish-release.yml (17) investigate .github/workflows/validate-task.yml (13) investigate LICENSE (20) accepted .husky/pre-commit (7) accepted <- path collision pyproject.toml (3) investigate TODO.md (2) accepted scripts/README.md (1) accepted <- path collision ``` Every gap is triaged, so the report's untriaged section holds only the pre-existing verbatim-section rows. `reports/divergences.md` is regenerated (13 lines changed). ## Verification `spec/audit.py --selftest` gains three cases, each **checked against a broken implementation** rather than only against the working one, so they can fail: | Case | Mutant it catches | |---|---| | Declared paths never leak into the deletion set | Dropping the subtraction leaks 25 declared paths | | A malformed ledger row is dropped, not fatal | Dropping the shape guard raises `KeyError` | | A truncated tree returns `None` | Ignoring `truncated` returns a partial set that reads as "carries nothing" | Live: both target repos now report exactly one deletion each (`configure.sh`), confirmed present via the contents API rather than inferred. Gates: `--selftest` PASS, `validate.py` OK, `prose_lint.py --diff HEAD` clean, `repo_gate.py --check eol` clean, `markdownlint-cli2` 0 issues, `ruff` (repo rule set) clean, `mypy` clean. One incidental observation, not addressed here: a transient TLS error mid-audit replaces **all** of a repo's findings with a single `ERROR`, so a flaky call costs that repo's whole result. It reports honestly and exits non-zero, so it is not a false clean, but it is worth a follow-up. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
> **Merge after #650.** Section 4 documents the `hub-only:` finding that PR lands. Merged first, this file describes a detector that does not exist yet. ## Problem "Sync this repository with the hub" had no procedure to resolve to. `STANDUP.md` takes a repository from nothing to operational, `AUDIT.md` measures one and deliberately changes nothing, so an already-stood-up repository that has fallen behind routed to the audit and got a findings list. A findings list states no order, and three things were undocumented rather than merely unwritten: 1. **The order.** The rules govern every file judged against them, so a verbatim rule section lands before those files, the same closing-window shape as `STANDUP.md` section 1A. And a deletion lands before the re-vendors, or you refresh a file that is about to go. 2. **Deletion.** The one remedy that removes content, so a wrong finding loses work instead of adding noise. 3. **What the measurement cannot see.** A carried file at `intent` fidelity is presence-checked only, so a clean run does not mean a current file. ## Change `RESYNC.md`, hub-only like `STANDUP.md` for the same reason (a repository needing it cannot be relied on to hold a current copy), wired into every surface that already advertises the other two: | Surface | What it gains | |---|---| | `AGENTS.md` "Fleet Bootstrap" | The third route points here instead of at `AUDIT.md` | | `host-setup/agent-safety/claude-md-fleet.md` | The same routing, kept in sync deliberately | | `README.md` | A Getting Started row, a doc bullet, a **"Resync a repository"** vocabulary entry, and the carry-the-rules paragraph | | `AUDIT.md` | States that it measures and does not sequence | | `GOVERNANCE.md` "Repository Onboarding and Conformance" | Names the third procedure | | `spec/section-model.md` | Lists it as hub machinery, not fleet law | The vocabulary entry is the point of the whole PR: per that section of the README, a request using one of those terms is "a complete instruction rather than a starting point for interpretation." Saying "resync with the hub" in a downstream repo now resolves to an ordered procedure. ## Host verification joins a procedure for the first time `scripts/host_gate.py` and `spec/host-tools.json` both existed and **neither** `STANDUP.md` nor `AUDIT.md` ever invoked them. `STANDUP.md` section 0 checks identity and signing only. The documented invocation passes `--repo`, and that is not incidental: ```shell python3 scripts/host_gate.py --repo <path-to-target-checkout> ``` A repository may declare a root `host-tools.json` layering over the hub's (tighten-only). The gate reads that file relative to `--repo`, which defaults to the working directory, so a bare run from a hub checkout layers the **hub's own** declaration and silently passes on the floors the target adds. Verified both ways: the bare run emits no layering note, and a target carrying no local file is silent by design, so a missing note is not evidence the flag was unnecessary. My first draft of that line said to run it bare, which would have shipped a command that skips what it claims to check. ## Blast radius of the byte-locked edit The `AGENTS.md` "Fleet Bootstrap" section is `verbatim` fleet-wide, so editing it makes downstream copies stale. Measured before writing it: **two** repositories carry the section (Blog, HomeAutomation-Config) and **both are already on the re-vendor list**, so this adds no repository to it. The other 18 do not carry it at all, which is a standup gap rather than drift. `claude-md-fleet.md` is the source of the installed `~/.claude/CLAUDE.md` fleet block, so each machine picks the new routing up when `host-setup/agent-safety/install.py` is next run. **I have not run the installer**, since the source is unmerged and installing from a branch would put unreleased content on the host. ## Verification Every command in the file was run as written, not transcribed: `host_gate.py` (passes, confirming the Python 3.13 floor), `audit.py`, `fidelity_honesty.py --report`, and `configure.sh`'s signature checked against its own usage. Gates: `prose_lint.py --diff HEAD` clean, `repo_gate.py --check eol` clean, `markdownlint-cli2` 0 issues over all 7 files, `cspell` 0 issues on `RESYNC.md`, CRLF confirmed byte-wise on every file (the new file was written LF and converted). ## Deliberately not in this PR Declaring `host-tools.json` in the `spec/files.json` baseline. That makes every repository owe a file it does not have, which is a fleet-wide new finding and a spec decision rather than a doc fix. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Your call, per the decision to accept the fleet-wide finding. Floor set to **1.7** per your follow-up. ## The jq gap `jq` is used by the ruleset normalizer in `repo-config/configure.sh`, the ruleset diff in `AUDIT.md` section 6, and payload regeneration in `docs/repo-config-carry.md`. It was declared in **neither** the contract table nor `spec/host-tools.json`. A host without it cannot apply, check, or audit a repo's branch configuration, and nothing said so. ## The floor is 1.7, and it is a target I did not pick a number and justify it after. Measuring first said **no floor was warranted at all**: nothing in the tree uses a post-1.5 jq feature (checked `walk`, `@base64d`, `--args`, `$__loc__`, `pick`, `toarray`, `abs`, `getpath`, `ltrimstr`, `splits`, `limit`, `$ENV` — the only hits were shell variables and Actions expressions, not jq filters). So the floor is a **target**, the same kind as `python3`, and the entry says so rather than implying a defect nobody found. 1.7 is anchored to what the platform provides: **current Debian stable packages 1.7.1**, so a distribution install satisfies the floor and no host needs a manual build to meet it, while an older release sits below and is the case the floor exists for. That anchor is what makes it actionable. Two rejected alternatives, for the record: - **1.8** (the newest, installed here) would fail a current apt host that runs every documented filter correctly. A floor set to whatever happens to be installed is a host failure nobody can act on, which is the hazard this file's own note names. - **1.6** would encode the `walk/1` boundary, which is a fact about the filters rather than something a host operator can act on. Facts kept in the entry because a reader needs them: on jq 1.5 `walk/1` does not degrade, it **fails to compile**, so a diff built on it reports drift on every ruleset it never compared. And the claim that `keys_unsorted` also needs 1.6 stays **disproved** per the `.github/copilot-instructions.md` record, measured on `jq-1.5-1-a5b5cbe`. ## Consequences swept, not left The floor now sits **above** 1.6, so `walk/1` is available — which made false the comments in `AUDIT.md` and `repo-config/configure.sh` that justified hand-defining a recursion by walk's absence. Both now give the reason that survives: the recursion costs nothing and compiles below the floor as well. This is the same class of defect as #649 (a rule changed, its prose left asserting the old one), so it was swept by term rather than by instance. ## host-tools.json becomes a carried file A repo now states the tools its **own** procedures need beyond the fleet declaration, so the tighten-only layering lives somewhere a reader finds rather than has to know to look for. A repo with nothing to add carries the stub with an empty `tools` list — the footing `OPERATIONS.md` already set — and this repo's new root file is that worked example, with a `note` distinguishing it from `spec/host-tools.json`. **Measured before landing: none of the 22 cataloged repos carries one**, so this adds exactly one `LETTER` per repo. This PR satisfies the hub's own. ## The gate joins the procedures it was written for `scripts/host_gate.py` existed and **no procedure ran it**. `STANDUP.md` section 0 and `AUDIT.md` now do, alongside `RESYNC.md` in #651. Each passes `--repo`, because the gate reads the target's declaration relative to that flag and a bare run layers the hub's instead while printing the same healthy digest either way. ## Verification `scripts/test_host_gate.py` **failed first**, which is the tests working: `test_the_declared_floors_are_the_ones_with_a_stated_reason` asserts the floor set exactly, so a new floor cannot land unnoticed. I updated it and generalized `test_a_target_floor_says_so_rather_than_implying_a_defect` from the single `python3` entry to the **set** of target floors, so a third one added without the two-kinds wording fails rather than reading as measured. The boundary is checked through the gate's own `compare`/`parse_version` rather than a reimplementation of them: ``` jq 1.5 -> FAILS jq 1.7 -> meets jq 1.6 -> FAILS jq 1.7.1 -> meets jq 1.6.1 -> FAILS jq 1.8.2 -> meets ``` - 552 script tests OK, `--selftest` PASS, `validate.py` OK - `host_gate.py`: 7 tools, `jq 1.8.2 meets the 1.7 floor`, and it reads the hub's own stub (`host-tools.json layered 0 local entry(s)`) - `prose_lint`, `repo_gate --check eol`, `markdownlint-cli2`, `cspell` on the gated files: all clean - **`editorconfig-checker` caught a real defect every other gate passed**: a `sed -i` I used wrote one bare LF into `AUDIT.md`. Fixed and re-verified byte-wise; the diff stayed at 9 lines rather than a whole-file rewrite. ## One bookkeeping item left for you The `keys_unsorted` entry in `.github/copilot-instructions.md` carries **Delete when** - "nothing this check runs on carries a jq older than 1.6", and an enforced 1.7 floor arguably satisfies that. I left it in place rather than deleting it unilaterally, because it exists to answer a repeat reviewer finding and its measured proof is cited from the new `jq` entry. Your call whether it retires. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two changes from one source: a stale enumeration a peer agent found, and a backlog entry for the method that found it. TODO disposition: **New entry**, per `TODO.md` rule 8. ## 1. The stale enumeration Two enumerations of `spec/` list its contents and stop before the host tool contract, which landed there in #652 along with a stub at the repository root: - `GOVERNANCE.md` "Repository Layout" - `README.md`'s `spec/` bullet Both were accurate when written. Neither is now, and a reader looking for where the tool floors live would search both and find nothing. ### Provenance, and two claims that did not reproduce Raised by the **ESPHome-Config agent** over peer messaging, not by a gate. It hit the same class in its own `Repository Layout` while planning its `AGENTS.md` -> `AGENTS.md` + `GOVERNANCE.md` split, and named it as the trap the `Documentation Style Conventions` maintenance rule exists for: prose no linter can see, going stale under a change that never mentioned it. I checked the hub for the same shape and for two adjacent claims from the same report. Recorded because a future reader will wonder: | Claim | Hub result | |---|---| | Carried docs still link `AGENTS.md#<section>` for sections that moved to `GOVERNANCE.md` | **Not present.** Zero `AGENTS.md#` anchors tree-wide, so the hub's anchors were rewritten with the split. Downstream-only, for repos still carrying pre-split docs. | | Other `gh api ... --jq '{...}'` projections feeding a diff share the gojq key-sorting exposure fixed in #649 | **Not exposed.** The only other one is the settings diff, where both sides pass through `jq -S .` over a flat boolean object, so key order cannot reach the comparison. Verified by running it both ways, `gh api --jq` and raw `gh api` piped to real `jq`: byte-identical. | `repo-config/` keeps its "apply script" wording, still true of the hub, which hosts the script it no longer carries downstream. ## 2. The backlog entry A `decision` cluster, because the mechanism needs no build and the only open question is **which document may carry rules that bind a downstream agent**. `GOVERNANCE.md` reaches those agents and costs a fleet-wide re-vendor plus the two manifest edits `spec/section-model.md` requires of any new section. A hub-only `docs/` file costs nothing and leaves the rules unreachable from the repositories that would apply them, which is the failure `AGENTS.md` "Fleet Bootstrap" exists to prevent. Settled and recorded so it is not re-derived: - **Cross-host does not work, by construction rather than configuration.** A peer address is a Unix domain socket under `/run/user/1000/cc-socks/`, which cannot cross a machine boundary. Cloud sessions and Remote Control sessions are the documented cross-host paths and neither appears in a listing on this host, so both are unverified rather than absent. - **The addressing has a guardrail worth keeping.** A bare peer name was refused and the transport demanded the `[ref]` a listing prints, which is what stops a message reaching the wrong repository's agent. - **The method earns its place on evidence.** One exchange produced the causal commit for the section 6 defect (`90e3255`), which this session had not identified from the symptom; a one-line reproduction of the gojq behavior that made an earlier fix pass for the wrong reason; and four procedure gaps no gate reports. - **A peer's finding is checked rather than adopted.** Two of those four did not reproduce here, one did and is part 1 of this PR. - **The boundary that matters is permission, not politeness.** A peer cannot widen what the asking session may do, so blocked work goes back to the maintainer rather than sideways to another agent. ## Verification `prose_lint --diff HEAD`, `repo_gate --check eol`, `editorconfig-checker`, `markdownlint-cli2` on `TODO.md`, and `cspell` on the gated files: all clean. Both `[files]` and `[section-model]` reference names already resolve in `TODO.md`. `GOVERNANCE.md` "Repository Layout" is `intent` fidelity per `spec/section-model.md`, so part 1 forces no fleet-wide re-vendor. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
## Problem #650 introduced the `hub-only:` finding, whose remedy removes a file rather than updating one. #651 and #652 stated that remedy in `AUDIT.md` section 10 and `RESYNC.md` section 4 as: delete the copy, reach the hub's, and *"where a carried doc named the local path, point it at the hub's."* That covers one of three shapes and assumes a hub equivalent exists. The two it missed are the ones that cost something. **A runnable command citing the deleted path.** This is the expensive shape, because a documented command *fails* where a dead link merely disappoints. Measured rather than argued: ``` NxWitness repo-config/README.md 13: - [`configure.sh`](./configure.sh) - idempotent `gh api` script with two modes: 14: - `./repo-config/configure.sh check` - validate only, no writes... 17: - `./repo-config/configure.sh apply` - create-or-update the rulesets... 61: REPO=ptr727/NxWitness ./repo-config/configure.sh apply 62: REPO=ptr727/NxWitness ./repo-config/configure.sh check ``` Retiring that one file makes **five lines of a carried document wrong, two of them commands a reader would run**. `configure.sh` is carried by **15 repositories**, so this is queued work rather than a hypothetical. **A mention with no hub equivalent.** It is removed rather than re-pointed, and removing a reference-style link removes its definition with it, per `GOVERNANCE.md` "Documentation Style Conventions", *because an orphaned definition fails the no-unused-defs rule.* So the old remedy could leave a **gate failure** behind while reading as complete. ## Provenance Raised by the **ESPHome-Config agent** over peer messaging, from a case of its own: deleting a nested `easystart/AGENTS.md` at Pieter's instruction, it found three inbound references across two files, one of them a reference definition that would have been left orphaned. It put the scope question to Pieter rather than deciding it, and that is what surfaced the generalization. I measured the fleet-wide shape rather than adopting the specific case: checked five repositories' `repo-config/README.md` for links (not merely mentions) to `configure.sh`, which is how the NxWitness figures above were obtained. Four mention it without linking; one links it and documents four invocations. ## Change Both statements of the remedy now name the sweep as part of the deletion, with the three shapes and their different fixes. `RESYNC.md` additionally carries the measured evidence, since it is the procedure an agent follows step by step. ## Verification `prose_lint --diff HEAD`, `repo_gate --check eol`, `markdownlint-cli2` on both files, `editorconfig-checker`: all clean. Both files gained a `governance-documentation-style` reference definition, and both reference blocks re-verified as sorted by reference name. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Promotes a coordinated set of hub changes that (1) add RESYNC.md as the third fleet entrypoint, (2) formalize host-tool verification (including a jq floor and a carried host-tools.json overlay contract), and (3) improve conformance auditing by deriving “hub-only” deletions from the manifest complement.
Changes:
- Add
RESYNC.mdand route existing entrypoints/docs to it for the “already stood up, now stale” case. - Extend the host-tool contract: declare
jq(>= 1.7 target), ship and baseline a repo-localhost-tools.jsonoverlay + schema, and wire/clarifyscripts/host_gate.pyoutput formatting. - Implement and document the hub-only deletion detector (hub git-tracked paths minus
spec/files.json), and widen the manifest-gap reporting to fleet-wide carriers.
Reviewed changes
Copilot reviewed 25 out of 25 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| TODO.md | Adds a decision/backlog entry documenting peer messaging as a method and its governance placement question. |
| STANDUP.md | Adds host tool verification guidance (including when/why to omit vs pass --repo), and notes host-tools.json in the baseline carry list. |
| spec/section-model.md | Updates hub-only (“not carried”) enumeration to include RESYNC.md alongside other hub machinery. |
| spec/project-types.json | Introduces the carried-scope dimension/check describing hub-only file detection/remediation semantics. |
| spec/host-tools.json | Adds jq as a required tool with a 1.7 target floor and supporting rationale/source guidance. |
| spec/host-tools-local.schema.json | Adds a dedicated schema for repo-local host-tools.json overlays. |
| spec/files.json | Adds host-tools.json to the carried baseline manifest. |
| spec/fidelity_honesty.py | Makes manifest-gap detection fleet-wide, names carriers per gap, and reports unreadable trees instead of silently undercounting. |
| spec/divergences.json | Expands gaps ledger with accepted collisions and improves configure.sh retirement reasoning (no hand-kept carrier list). |
| spec/audit.py | Adds hub-only path derivation + repo tree reading helpers and audits hub-only carried files with ledger-based dispositions. |
| scripts/test_host_gate.py | Adds tests enforcing overlay/schema correctness and extends floor/target-floor assertions to include jq. |
| scripts/host_gate.py | Improves failure message formatting (multi-line WHY/INSTALL) and prints all continuation lines with proper indentation. |
| RESYNC.md | New ordered resync procedure covering routing, host verification, measurement, ordered remedies, and deletion/inbound-reference sweep guidance. |
| reports/divergences.md | Updates generated divergence report format to include carriers per manifest-gap entry. |
| repo-config/README.md | Removes bypass_actors from the asserted subset and documents why it is writable-but-unmanaged. |
| repo-config/configure.sh | Updates comments to reflect declared jq floor and rationale for retaining portable recursion. |
| README.md | Adds resync entrypoint (RESYNC.md) and updates “spec/” description to mention the host tool contract/floors. |
| host-tools.json | New repo-local overlay stub (empty tools) pointing at the new overlay schema and documenting layering semantics. |
| host-setup/agent-safety/claude-md-fleet.md | Updates routing guidance to point to RESYNC.md for already-stood-up repos. |
| GOVERNANCE.md | Notes RESYNC.md as the procedure for already-stood-up repos that fell behind; updates spec/ description to include host tool contract/floors. |
| docs/repo-config-carry.md | Removes bypass_actors from the regeneration projection and documents why it is excluded. |
| docs/host-setup.md | Adds jq to the host tool table and adds a missing [audit] reference for the new link. |
| cspell.json | Adds resync / resynced to the project dictionary. |
| AUDIT.md | Clarifies that AUDIT.md measures (does not sequence), adds host verification guidance, documents carried-scope/hub-only semantics, and updates the ruleset diff snippet to match the managed subset. |
| AGENTS.md | Updates Fleet Bootstrap routing to use RESYNC.md for the “instruction set present, current or stale” case. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Follow-up to [#654](#654), fixing a scoping defect in it. ## Problem [#654](#654) put the inbound-reference sweep **inside** the bullet for "A `hub-only:` finding" in `AUDIT.md` section 10, and after three disposition-specific bullets in `RESYNC.md` section 4 whose intro scopes to the hub-only detector. The case that produced the rule was not a hub-only finding. It was a repository deleting **its own** file: no `spec/divergences.json` entry, no `retire` disposition, no hub equivalent. So the rule its case generalized was documented in the one place its case could not reach, and an agent removing a repo-owned file finds nothing. Nothing about the sweep depends on who owned the file. Only the `retire`-confirmation step is hub-only-specific. ## Change - `AUDIT.md` section 10: the sweep becomes its own bullet, stated as governing **any** deletion. The `retire` confirmation stays with the `hub-only:` bullet, where it belongs. - `RESYNC.md` section 4: the sweep bullet says the three bullets above it turn on a disposition and belong to the detector, while the sweep turns on nothing. - The repo-owned case is promoted from footnote to evidence, since it is what demonstrates the rule is not hub-only. ## The second case is now measured, not predicted [#654](#654) said an orphaned definition "fails the no-unused-defs rule". That has since been observed rather than argued: removing the inline link to a nested `AGENTS.md` **tripped `MD053`** on the definition it orphaned, before the author reached that edit. So the delete-the-file-only path fails CI rather than merely breaking prose, which is a stronger claim than the one shipped. ## Provenance Raised by the **ESPHome-Config agent** over peer messaging. It verified [#654](#654 merged text against `ca3df85` rather than taking it on trust, and found the scoping while executing the very deletion the rule came from. Its words: *"the rule that my case generalized is documented in the one place my case would not have reached."* That is the second defect this loop has caught in my own merged work today, both times by a reader doing the thing the doc describes rather than reading it. ## Verification `prose_lint --diff HEAD`, `markdownlint-cli2` on both files, `editorconfig-checker`, `repo_gate --check eol`: all clean. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Uh oh!
There was an error while loading. Please reload this page.
Promotion of six squashes. Merge with a merge commit, never a squash, and never with
--delete-branch— this PR's head isdevelop.AUDIT.mdsection 6's ruleset diff projectedbypass_actors, which no payload declares, so jq exited 5 and every ruleset on every repo reported DRIFT. Two further surfaces carried the same dead policy.spec/files.jsonbaseline, so a retired file is derived rather than remembered. Correctedconfigure.shfrom 6 carriers to 15.RESYNC.md, the third entry point, routed from the byte-lockedAGENTS.md"Fleet Bootstrap".jq1.7 target floor,host-tools.jsonas a carried baseline file, andscripts/host_gate.pywired into all three procedures for the first time.spec/enumerations that went stale when the host contract landed there.Why this promotion matters more than most
Downstream repos read hub
mainas ground truth. Whilemainsits at0a86bca:AUDIT.mdsection 6 snippet gets false ruleset drift on both branches. The ESPHome-Config agent reproduced this live and is holding its own fix stashed rather than diverging from the hub.main's revision and then again after promotion. Verified against Blog, whose carriedAGENTS.md"Fleet Bootstrap" differs from both refs, so it is two re-vendors instead of one.RESYNC.mddoes not exist onmain, so a downstream agent asking "how do I sync" still routes toAUDIT.md, which measures and deliberately states no order.Fleet cost this carries, measured and accepted
host-tools.jsonbecomes a carried baseline file, so it is oneLETTERon 22 of 22 cataloged repos, the hub included. That was measured before landing and accepted by the maintainer.RESYNC.mdsection 0 was amended so a letter wave of this shape reads as a file to carry inside a resync rather than as evidence a repo was never stood up.Verification on the merged head
spec/validate.pyOK,spec/audit.py --selftestPASS, 557 script tests OK,scripts/host_gate.pyclean over 7 declared tools,repo_gate.py --check eolclean,editorconfig-checkerclean, working tree clean. Post-merge fleet audit: 22 repos, zero errors.Two hub-side artifacts clear on this merge, both promotion-pending rather than defects: the hub's own
AGENTS.md"Fleet Bootstrap" reads as stale against itsdevelopcanonical, and the hub reports its ownhost-tools.jsonabsent because the file exists ondeveloponly.🤖 Generated with Claude Code