Context
AGENTS.md §critical_gates #9 forbids naming a client in any public-facing artifact; client specifics live only in private repos. The rule carries no conditional exceptions.
A pre-publish sweep run as a gate on #17784 — which will copy .agents/skills/** into every enrolled repo as committed bytes — found a client identifier present in this public repository across several distinct surfaces. The operator confirmed in-session (2026-08-25) that the reference is not permitted. This ticket exists because the identifier was never the interesting part: nothing in CI could have caught it, so it survived every review that touched those files, and #17784 would have replicated one instance into every enrolled repo.
Filed under the freeze's exception (3) — security/confidentiality fix. Not a scope addition.
Deliberately omitted from this body: the identifier, the file paths, and the line numbers. Enumerating them in a public ticket would index the exact thing being redacted. Specifics were distributed over A2A (team-readable, non-public) and are reproducible from the guard in AC-1.
Live latest-open sweep: latest 20 open issues checked at 2026-08-25T22:23:18Z; no equivalent found. A2A in-flight claim sweep over 30 messages, all read-states, covering the ~90-minute herd window: no overlapping [lane-claim] on this scope (#17785's Wave-2.5 tracker triage is disposition classification, a different subject).
The Problem
The occurrences are not one mistake repeated. They fall into four classes with different owners, different fixes, and different reversibility — and conflating them produces either an incomplete redaction or an overreaching one:
| class |
origin |
reversible by |
| authored prose |
a maintainer wrote the name into substrate documentation |
ordinary PR |
| graph identifiers |
node/key names from a private deployment leaked into a committed measurement artifact and a data snapshot |
ordinary PR |
| agent-authored ticket text |
an agent named the client in a sweep note on a public issue |
issue edit — done, see below |
| mirrored + historical |
the content pipeline mirrors public issue text into the tree hourly; commit subjects also carry it |
not reversible without history rewrite |
The fourth class is why a tree-only redaction is a false green: the mirror regenerates from its source, so scrubbing mirrored bytes without fixing the source restores the identifier on the next sync run.
A fifth category exists and is explicitly not a defect: a third party's own self-chosen GitHub handle, appearing in maintainer comments that reply to them. Redacting a contributor's public identity from an answer addressed to them is a different act with a different cost, and it is not this ticket's call.
The Architectural Reality
AGENTS.md §critical_gates #9 — the invariant. Discipline-only; no mechanical guard exists, which is the actual defect this ticket closes.
.agents/skills/** — the tree #17784 distributes as committed bytes to every enrolled repo. One authored instance lives here, giving it the highest propagation factor of any occurrence.
resources/content/** — the mirror of public issue and discussion text, refreshed by the chore(data): Hourly data sync pipeline update [skip ci] job. Downstream, never a fix site.
buildScripts/util/check-chore-sync.mjs via .husky/pre-commit:1 — the existing content-sync guard precedent. Note it honours --no-verify, so a hook cannot be the enforcing seat here either (same finding as #17784: authority is reusable CI, not husky).
The Fix
- A denylist guard in CI — a term list, sourced from a config the guard reads rather than hardcodes, failing the build when a listed term appears in tracked files. Runs on PRs and on the sync pipeline's own commits, since that job is a re-introduction path.
- Redact the authored instances — prose and graph identifiers, in one PR. Meaning preserved; the sentences do not depend on the name.
- Route what this ticket does not own — mirrored bytes follow their sources; history rewrite and the third-party-handle question are operator-owned and recorded here, not executed.
Already completed before this ticket was filed, because a GitHub issue body is not a tracked file and §critical_gates #7's ticket gate therefore did not apply: the agent-authored ticket text was redacted and verified clean live. The original author (a peer) was notified — ticket-create §11 reserves body edits to the author, and a §critical_gates invariant outranks that courtesy, but the notification is still owed.
Acceptance Criteria
Out of Scope
- Rewriting git history, and any force-push to a public repository with forks. Operator-owned, Tier 4.
- Redacting a third party's self-chosen public handle from replies addressed to them.
- Scrubbing mirrored bytes as an independent act — they follow their sources.
- Retroactive audit of other private-deployment identifiers. If AC-1's guard surfaces more, they become rows in the config, not new tickets.
Avoided Traps
- Scrubbing the tree and calling it done. The mirror restores from source hourly; a tree-only fix is a green that decays within the hour.
- A husky hook as the enforcing seat.
--no-verify makes it feedback, not authority — the same finding #17784 records.
- Hardcoding terms into the guard. Terms are operator-owned data; a maintainer-authored constant makes an agent the arbiter of confidentiality and cannot be updated without a code review.
- Treating all occurrences as one class. Four origins, four owners; a uniform redaction either misses the mirror path or overreaches into a contributor's public identity.
Decision Record impact
none — enforces an existing §critical_gates invariant; introduces no new authority.
Related
#17784 (the distribution ticket whose pre-publish gate found this) · #17783 (enforcement custody: reusable CI + branch protection) · #17500 (parent epic) · D#17782 (split runway; freeze exception 3)
Origin Session ID: f27af939-3cec-4f52-a67d-e4e8786fed08
Retrieval Hint: query_raw_memories("client identifier public substrate denylist guard sync pipeline reintroduction") · query_summaries("pre-publish sweep gate 17784 canonical skill distribution")
Context
AGENTS.md§critical_gates #9 forbids naming a client in any public-facing artifact; client specifics live only in private repos. The rule carries no conditional exceptions.A pre-publish sweep run as a gate on #17784 — which will copy
.agents/skills/**into every enrolled repo as committed bytes — found a client identifier present in this public repository across several distinct surfaces. The operator confirmed in-session (2026-08-25) that the reference is not permitted. This ticket exists because the identifier was never the interesting part: nothing in CI could have caught it, so it survived every review that touched those files, and #17784 would have replicated one instance into every enrolled repo.Filed under the freeze's exception (3) — security/confidentiality fix. Not a scope addition.
Deliberately omitted from this body: the identifier, the file paths, and the line numbers. Enumerating them in a public ticket would index the exact thing being redacted. Specifics were distributed over A2A (team-readable, non-public) and are reproducible from the guard in AC-1.
Live latest-open sweep: latest 20 open issues checked at 2026-08-25T22:23:18Z; no equivalent found. A2A in-flight claim sweep over 30 messages, all read-states, covering the ~90-minute herd window: no overlapping
[lane-claim]on this scope (#17785's Wave-2.5 tracker triage is disposition classification, a different subject).The Problem
The occurrences are not one mistake repeated. They fall into four classes with different owners, different fixes, and different reversibility — and conflating them produces either an incomplete redaction or an overreaching one:
The fourth class is why a tree-only redaction is a false green: the mirror regenerates from its source, so scrubbing mirrored bytes without fixing the source restores the identifier on the next sync run.
A fifth category exists and is explicitly not a defect: a third party's own self-chosen GitHub handle, appearing in maintainer comments that reply to them. Redacting a contributor's public identity from an answer addressed to them is a different act with a different cost, and it is not this ticket's call.
The Architectural Reality
AGENTS.md§critical_gates #9 — the invariant. Discipline-only; no mechanical guard exists, which is the actual defect this ticket closes..agents/skills/**— the tree #17784 distributes as committed bytes to every enrolled repo. One authored instance lives here, giving it the highest propagation factor of any occurrence.resources/content/**— the mirror of public issue and discussion text, refreshed by thechore(data): Hourly data sync pipeline update [skip ci]job. Downstream, never a fix site.buildScripts/util/check-chore-sync.mjsvia.husky/pre-commit:1— the existing content-sync guard precedent. Note it honours--no-verify, so a hook cannot be the enforcing seat here either (same finding as #17784: authority is reusable CI, not husky).The Fix
Already completed before this ticket was filed, because a GitHub issue body is not a tracked file and §critical_gates #7's ticket gate therefore did not apply: the agent-authored ticket text was redacted and verified clean live. The original author (a peer) was notified —
ticket-create§11 reserves body edits to the author, and a §critical_gates invariant outranks that courtesy, but the notification is still owed.Acceptance Criteria
Out of Scope
Avoided Traps
--no-verifymakes it feedback, not authority — the same finding #17784 records.Decision Record impact
none— enforces an existing §critical_gates invariant; introduces no new authority.Related
#17784 (the distribution ticket whose pre-publish gate found this) · #17783 (enforcement custody: reusable CI + branch protection) · #17500 (parent epic) · D#17782 (split runway; freeze exception 3)
Origin Session ID: f27af939-3cec-4f52-a67d-e4e8786fed08
Retrieval Hint:
query_raw_memories("client identifier public substrate denylist guard sync pipeline reintroduction")·query_summaries("pre-publish sweep gate 17784 canonical skill distribution")