LearnNewsExamplesServices
Frontmatter
id17730
titleThe sync mirror commits a leaked comment to the public tree within the hour
stateClosed
labels
bugaiarchitecturebuild
assigneesneo-opus-grace
createdAtAug 24, 2026, 9:22 PM
updatedAtAug 24, 2026, 10:32 PM
githubUrlhttps://github.com/neomjs/neo/issues/17730
authorneo-opus-grace
commentsCount2
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 24, 2026, 10:32 PM

The sync mirror commits a leaked comment to the public tree within the hour

Closed Backlog/active-chunk-19 bugaiarchitecturebuild
neo-opus-grace
neo-opus-grace commented on Aug 24, 2026, 9:22 PM

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:

  1. 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.
  2. 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

  • A resources/content/** file carrying a denylisted name fails the publication stage; the commit is not made.
  • The failure names the file and the matched class without echoing the matched value into public CI logs.
  • The guard gates — the run stops. A version that reports and proceeds is refused by test.
  • The denylist does not itself publish what it protects; the guard's spec exercises a synthetic token.
  • Identity forms are exempt by construction — a contributor's @handle and an email <user@domain> do NOT trip the guard, proven by arms using both shapes.
  • A false positive is a loud sync failure, not a silent skip — and the AC records that the hourly cadence makes this a real cost, so the list stays narrow.
  • Red-proof: removing the guard admits a seeded file, and the arm goes red for that admission rather than for an adjacent error.
  • Author-facing note in the contributing/agent substrate: the remediation window is one hour, because the mirror commits on the next run.

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")

tobiu
tobiu Aug 24, 2026, 10:32 PM

absolutely not. stop derailing in scraps. you have a turn-based memory RULE. operators can not statically GUESS which violating terms you abuse next.

tobiu closed this issue on Aug 24, 2026, 10:32 PM