Carried files must not reference the template repo; mechanize the check - #359
Merged
Conversation
The private-template reference has spread to 13 of 20 downstream repos, 8 of them public. The dominant instance is a stale copilot-instructions paragraph telling agents to report drift upstream, which hard-links the private template twice - and links an AGENTS.md section the hub no longer has, so it 404s even with access. The #344 rule barred sibling-repo references but arguably let the hub through as "the subject". It now says explicitly that a carried file names the template in neither prose nor link: the template is private, so the link 404s for that repo's readers, and the flow is machinery a consumer should never see. Where a carried file must express a template-level behavior ("report a rule discrepancy upstream"), it states the behavior and not the destination; the maintainer supplies the destination out of band. Mechanized in spec/audit.py rather than left to review, since this spread silently across the fleet: the two agent-instruction files are checked for the template name. The name is derived from the git remote, not the checkout directory - a differently-named clone would otherwise stop detecting silently, the fail-open case the check exists to prevent. Verified: flags PlexCleaner (copilot-instructions) and ESPHome-Config (AGENTS.md); Vantage-Config clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds an explicit governance rule that carried files must not reference the private template repo, and mechanizes enforcement by extending the fleet audit to detect template-name mentions in agent-instruction files.
Changes:
- Update the “no cross-repo references” rule to explicitly include the template repo itself.
- Extend
spec/audit.pyto flag template-repo references inAGENTS.mdand.github/copilot-instructions.mdacross audited repos. - Align the machine-readable audit spec (
spec/project-types.json) with the new rule and enforcement mechanism.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
spec/project-types.json |
Updates the recurring “no cross-repo references” rule text to explicitly include the template repo and mention the mechanized audit check. |
spec/audit.py |
Adds template-repo name derivation and a new audit finding when agent-instruction files reference the template repo name/link. |
AGENTS.md |
Clarifies documentation-style conventions to explicitly forbid references to the template repo in carried files. |
…pilot #359) Two ways the check could miss silently: - The match was case-sensitive, so a lowercased URL (github.com/ptr727/projecttemplate) would pass. Compares casefolded now; GitHub treats repo names case-insensitively. - hub_name()'s directory-name fallback was itself silent, so a fork, worktree, or remoteless checkout would degrade the check with no signal. It now reports whether the name came from the remote, and the run prints a stderr warning when it did not. Both are the fail-open case this check exists to prevent, so neither should be quiet about it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
spec/audit.py:53
- hub_name() only reads remote.origin.url. If someone runs this audit from a fork (origin points to the fork), the template-reference check will scan for the fork repo name and can miss references to the upstream template repo with no warning (the docstring explicitly calls out forks as a fail-open scenario). Prefer upstream when available, then fall back to origin, and only then to ROOT.name (with the existing warning).
def hub_name():
"""This repo's name on the remote, and whether it came from the remote.
Read from origin rather than the checkout directory, which a differently-named clone, a fork, or a
worktree without an origin would silently break. The caller announces the directory-name fallback:
a silently degraded match is the fail-open case this check exists to prevent.
"""
r = subprocess.run(["git", "config", "--get", "remote.origin.url"], capture_output=True, text=True, cwd=ROOT)
if r.returncode == 0 and r.stdout.strip():
return r.stdout.strip().rstrip("/").removesuffix(".git").split("/")[-1], True
return ROOT.name, False
ptr727
added a commit
that referenced
this pull request
Jul 19, 2026
Addresses **item 1** of #357. Item 2 (the two prose/comment style proposals) is deliberately not in this PR - it is a house-style decision I am putting to the owner separately. ## The gap this closes #359 fixed the **hard** leak: a carried file naming or linking the private template, which 404s for that repo's readers. It also mechanized a check for it. A downstream agent caught the **soft** half, which that check cannot see because it greps for the repo name: carried prose that attributes a rule to *"the template"*. > "A repo owner may widen their own CI file list, but **the template ships** README + HISTORY as the default" > "Guarantees say 'default branch' portably; **the template implements it** as the literal `main`" No repo is named, so nothing 404s - but it still exposes a coordinator the reader cannot reach, and it frames the repo's own rule as someone else's. Genuinely good catch; the durable fix belongs here, as the filer said, or it returns on every reconvergence. ## What changed Eleven attributions across `AGENTS.md`, `CODESTYLE.md`, and `WORKFLOW.md` now state the rule directly: | before | after | |---|---| | "the template ships README + HISTORY as the default" | "README + HISTORY are the default" | | "the template's default Python profile" | "the default Python profile" | | "the template uses `client-id: ...`" | "use `client-id: ...`" | | "the template implements it as the literal `main`" | "it is implemented as the literal `main`" | | "the template constrains only that the output be input-deterministic" | "the constraint is only that the output be input-deterministic" | | "not when it matches the template byte for byte" | "not when it matches a reference implementation byte for byte" | | "the template ships the tracker but not this consumer wiring" | "the tracker ships without this consumer wiring" | | "by exporting the template's two rulesets" | "by importing the committed `repo-config/*.json` ruleset payloads" | | "Because the template has no such leaf" | "Because no such leaf ships" | Behavior is unchanged throughout; only the framing moves from *reported* to *owned*. **One occurrence remains by necessity:** the rule that bans the practice (`AGENTS.md` "This includes the template repo itself"). It cannot be stated without referring to the concept, and it names no repo and gives no location. Two later passes caught what the first regex missed - "this template" (AGENTS.md, CODESTYLE.md) and "the `release` template" (WORKFLOW.md). Remaining matches for the word are different meanings, not attributions: Serilog structured message templates, and the `reports/_template.md` filename. ## Note on the mechanical check `spec/audit.py` greps for the repo **name**, so it catches the hard leak but not this class - phrasing is a judgment call and a keyword sweep for "the template" would false-positive on legitimate uses (this PR's own rule statement, for one). Flagging that limitation rather than pretending the check covers it. `spec/validate.py`, markdownlint, editorconfig-checker: clean. CRLF preserved. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 19, 2026
Closed
ptr727
added a commit
that referenced
this pull request
Jul 19, 2026
Reported from a downstream session: Copilot insisted on removing a `README.md` link pointing to a related device-builder repo, citing the no-cross-repo-link rule. That link was legitimate content. The rule I wrote in #344/#359 made that misreading reasonable, so this fixes the rule rather than the reviewer. ## Two defects in the rule as written **1. It led with the mechanism and buried the carve-out.** The sentence opened with "adds no link (inline URL or reference definition) to another fleet repo" and only qualified it fifteen lines later with "unless that repo is the subject". Read top-down, the ban looks absolute. **2. It claimed `README.md` is carried verbatim. It is not.** `spec/files.json` gives `README.md` neither `whole: true` nor `sections`, so it is a per-repo file governed only by `readme-structure.md`. A repo's own README is its own content. The rule never should have reached it, which is exactly where the over-application landed. ## Restated by intent Two things banned in carried files: - **Any reference to the template repo**, prose or link. Private, so it 404s, and the coordination flow is machinery a consumer should not see. - **A sibling fleet repo named as an illustrative example** ("repo X does it this way", "see repo Y's adoption"). That couples repos and rots as they diverge. And explicitly permitted: > **A contextually relevant link to a related project is not a coordination reference, and is expected.** Where another repo is part of this repo's subject matter - the image that consumes this config, the builder that generates this hardware, a library this depends on - link it normally. The test is whether the link serves a reader of *this* repo's content, not whether the target happens to be in the fleet. Plus the scope correction: the rule governs carried template content, and a repo's own `README.md` and topical docs are outside it. `spec/project-types.json`'s `recurring.norepoxref` is reworded to match. ## The mechanical check needed no change `spec/audit.py` greps only for the **template name** in the two agent-instruction files. It never looked for sibling links, so it was not the source of the over-application and its behavior is unchanged - re-verified against PlexCleaner (flags) and Vantage-Config (clean). `spec/validate.py`, markdownlint, editorconfig-checker: clean. *Downstream note: ESPHome-NonRoot is amending its local AGENTS.md for the same reason. Once this lands, its next carry converges on this wording.* 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The template is private, so any reference to it from a carried file 404s for that repo's readers and exposes template machinery a consumer should never have to see. Raised from downstream work where the references were being stripped by hand, repo by repo.
Scope of the leak
Swept every cataloged repo. 13 of 20 reference the template; 8 are public:
The dominant instance is a stale
copilot-instructions.mdparagraph telling agents to report drift upstream, which hard-links the private template twice - and one link targets anAGENTS.md#staying-in-sync-and-reporting-drift-upstreamsection the hub no longer has, so it 404s even for someone with access. The hub itself carries neither the paragraph nor that section: this is stale carried content that outlived its source (#305 territory, and exactly the "prose asserting the old behavior" rule from #356).The rule
#344 barred sibling-repo references but arguably let the template through as "the subject".
AGENTS.mdnow says explicitly:That preserves the agent-to-agent reporting flow (which is real and valuable) while keeping it out of a public file.
Mechanized, not left to review
This spread silently across 13 repos, so
spec/audit.pynow checks the two agent-instruction files for the template name and raises a drift finding.The template name is derived from the git remote, not the checkout directory name - a clone into a differently-named folder would otherwise stop detecting with no signal, which is the fail-open case the check exists to prevent (AGENTS.md "Verification Discipline": gates fail loud, never narrow quietly).
Downstream
Not fixed here - the 13 carried copies need the reference removed in their own repos. Raising the delivery shape (per-repo issues vs. folding into each repo's existing conformance issue) rather than filing 13 issues unilaterally.
spec/validate.py, markdownlint, editorconfig-checker: clean.🤖 Generated with Claude Code