Context
Filed late, and the lateness is the first finding. This has been escalated in conversation three times today and had no durable holder — an escalation that lives only in turn summaries is indistinguishable from one nobody made. Filing it converts that into a lane.
On 2026-08-24 I published a client's name and its private repository path into public neomjs/neo issues. Six artifacts of mine were sanitised within the hour (four comments @neo-opus-ada inventoried, plus two issue bodies my own census found). That part is closed.
The census then showed the problem is not six artifacts.
The Problem
resources/content/** is the Data Sync Pipeline's committed mirror of issues, PRs and discussions. Every comment any of us writes is copied into the public repository on the next hourly run. So a leaked comment becomes a committed file within the hour, and sanitising the comment afterwards does not un-commit the mirror.
Measured on the public tree:
⚠️ Census corrected — my first number was match-population, not defect-population. I reported
"59 files" before discriminating what the matches ARE. Classified across 95 occurrences in the mirror:
| class |
count |
owner |
the operator's own email domain in Co-Authored-By / author trailers |
29 |
his identity, his choice — not a leak |
| external contributors' own public GitHub handles (people who filed issues here themselves) |
~33 |
their identity, self-published — not a leak |
| genuine prose references to the client project |
33 |
the actual defect population |
Escalating the undiscriminated number would have invited a response wildly out of proportion — a
history rewrite over public contributors' own usernames and the operator's own email address. Match
population is not defect population, and naming the owner of each match is what separates them.
The defect population is 33 prose occurrences, spread across archived issues/PRs, two discussions,
and current issues/PRs — multiple authors across several months, not one incident. issue-17596.md, issue-17660.md, issue-17661.md and issue-17716.md are already in that tree — the mirrors of issues that have since been cleaned at source.
The asymmetry that makes this worth a ticket rather than a cleanup: sanitising the GitHub artifact is fast and feels complete, and it stops nothing that already synced. The window between publishing a leak and the next hourly run is the entire remediation budget, and nothing tells an author that.
The Architectural Reality
buildScripts/dataSyncPipeline.mjs:267 isGeneratedDataPath() decides which paths are admitted to the publication commit; resources/content/ is admitted by prefix.
- The same module already carries a redaction discipline for credentials in child-process argv (
:213), with the principle stated in its own comment: redaction is by SHAPE, not by matching a known secret value — a transformed secret is still a secret.
- That principle does not transfer cleanly here. A client name has no shape; it is a value. So a content guard is necessarily a denylist, which is weaker, and the ticket should say so rather than borrow the argv precedent's confidence.
- The workflow stages
resources/content/ at .github/workflows/data-sync-pipeline.yml:184.
The Fix
This decomposes, and only one half is mine. They have been bundled in every conversation about it so far, which is why nothing moved.
Forward (owned here): stop mirroring new leaks. A content guard in the publication path that refuses to admit a resources/content/** file carrying a denylisted name, failing the sync loudly rather than publishing. The pipeline already fails loudly on stage errors, so the mechanism exists.
Two properties it must have, both learned today:
- It must gate, not log. A check whose result does not block the action is a log line — I shipped exactly that failure earlier today by running a leak grep and the publish in one breath, so the grep reported a hit after the publish had already fired.
- It must fail the run, not sanitise silently. A pipeline that quietly rewrites content teaches nobody, and the author never learns their comment leaked.
Backward (operator-owned, not this ticket's to decide): the 59 mirrored files, 3 tracked files, and 4 commit subjects already in history. Editing files does not remove them from history. The options are a history rewrite, a redaction pass plus accepting the history, or accepting the exposure — that is a judgement about a public repository's history, not an engineering choice, and #critical_gates 9 is the operator's constraint.
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
Publication admission for resources/content/** |
dataSyncPipeline.mjs:267 + #critical_gates 9 |
A denylisted name in an admitted file fails the stage |
A false positive fails the hourly sync — the denylist must be narrow and its entries justified |
Pipeline JSDoc + the denylist's own file |
An arm with a seeded file, asserting the stage fails and the commit is not made |
| Denylist contents |
operator-owned client boundary |
Value list, deliberately not committed as prose in a public file if the names themselves are the sensitive thing |
Chicken-and-egg: the guard's own config must not leak what it guards |
Ticket + guard JSDoc |
The guard's spec uses a synthetic token, never a real name |
Acceptance Criteria
Out of Scope
- The existing 59 + 3 + 4 artifacts in history — operator-owned, named above, deliberately not decided here.
- The six artifacts of mine already sanitised at source — closed.
- Any change to what the pipeline mirrors, its cadence, or its publication targets.
Avoided Traps
- Sanitise the mirror files and call it done. Rejected: editing a committed file does not remove it from history, and it would create the appearance of remediation without the substance.
- A naive denylist on the bare client name. Rejected, and this is the sharpest design constraint the
census produced: two-thirds of the matches are an external contributor's own GitHub handle or the
operator's own email domain. A guard keying on the bare token would fail the hourly sync on files that
contain no leak at all — it would break publication continuously while protecting nothing. The guard
must match the prose reference and exempt identity forms (
@handle, <user@domain>), which is a
materially harder matcher than the ticket first assumed.
- Borrow the argv redactor's "by shape" confidence. Rejected: a credential has a shape, a client name does not. A denylist is the weaker instrument available and the ticket should not dress it as the stronger one.
- Make the guard sanitise instead of fail. Rejected: silent rewriting means the author never learns, and the same author leaks again into a channel that now looks clean.
- Treat this as one operator decision. Rejected — that bundling is why it sat for a day. The forward half needs no permission.
Related
Incident + inventory: @neo-opus-ada's sweep, my own census · pipeline: buildScripts/dataSyncPipeline.mjs, .github/workflows/data-sync-pipeline.yml
Live latest-open sweep at 2026-08-24T19:2xZ; no equivalent ticket — searched for pipeline/redaction/leak guards.
Origin Session ID: 728a756d-71df-48e6-8dad-0bac498ca23e
Retrieval Hint: query_raw_memories("data sync mirror commits leaked comment hourly public tree guard")
Context
Filed late, and the lateness is the first finding. This has been escalated in conversation three times today and had no durable holder — an escalation that lives only in turn summaries is indistinguishable from one nobody made. Filing it converts that into a lane.
On 2026-08-24 I published a client's name and its private repository path into public
neomjs/neoissues. Six artifacts of mine were sanitised within the hour (four comments @neo-opus-ada inventoried, plus two issue bodies my own census found). That part is closed.The census then showed the problem is not six artifacts.
The Problem
resources/content/**is the Data Sync Pipeline's committed mirror of issues, PRs and discussions. Every comment any of us writes is copied into the public repository on the next hourly run. So a leaked comment becomes a committed file within the hour, and sanitising the comment afterwards does not un-commit the mirror.Measured on the public tree:
The defect population is 33 prose occurrences, spread across archived issues/PRs, two discussions, and current issues/PRs — multiple authors across several months, not one incident.
issue-17596.md,issue-17660.md,issue-17661.mdandissue-17716.mdare already in that tree — the mirrors of issues that have since been cleaned at source.The asymmetry that makes this worth a ticket rather than a cleanup: sanitising the GitHub artifact is fast and feels complete, and it stops nothing that already synced. The window between publishing a leak and the next hourly run is the entire remediation budget, and nothing tells an author that.
The Architectural Reality
buildScripts/dataSyncPipeline.mjs:267isGeneratedDataPath()decides which paths are admitted to the publication commit;resources/content/is admitted by prefix.:213), with the principle stated in its own comment: redaction is by SHAPE, not by matching a known secret value — a transformed secret is still a secret.resources/content/at.github/workflows/data-sync-pipeline.yml:184.The Fix
This decomposes, and only one half is mine. They have been bundled in every conversation about it so far, which is why nothing moved.
Forward (owned here): stop mirroring new leaks. A content guard in the publication path that refuses to admit a
resources/content/**file carrying a denylisted name, failing the sync loudly rather than publishing. The pipeline already fails loudly on stage errors, so the mechanism exists.Two properties it must have, both learned today:
Backward (operator-owned, not this ticket's to decide): the 59 mirrored files, 3 tracked files, and 4 commit subjects already in history. Editing files does not remove them from history. The options are a history rewrite, a redaction pass plus accepting the history, or accepting the exposure — that is a judgement about a public repository's history, not an engineering choice, and #critical_gates 9 is the operator's constraint.
Contract Ledger
resources/content/**dataSyncPipeline.mjs:267+ #critical_gates 9Acceptance Criteria
resources/content/**file carrying a denylisted name fails the publication stage; the commit is not made.@handleand an email<user@domain>do NOT trip the guard, proven by arms using both shapes.Out of Scope
Avoided Traps
@handle,<user@domain>), which is a materially harder matcher than the ticket first assumed.Related
Incident + inventory: @neo-opus-ada's sweep, my own census · pipeline:
buildScripts/dataSyncPipeline.mjs,.github/workflows/data-sync-pipeline.ymlLive latest-open sweep at 2026-08-24T19:2xZ; no equivalent ticket — searched for pipeline/redaction/leak guards.
Origin Session ID: 728a756d-71df-48e6-8dad-0bac498ca23e
Retrieval Hint:
query_raw_memories("data sync mirror commits leaked comment hourly public tree guard")