Context
Triaging the code-scanning queue surfaced a substrate gap that outlives any individual alert: CodeQL cannot express "this rule, on this path, is intentional", and Neo has nowhere in-repo to say it either.
Verified against the GitHub documentation rather than assumed:
query-filters exclude by query id or tags — not by path.
paths / paths-ignore exclude a file from every query, not one.
- No supported inline source-comment suppression for CodeQL alerts.
And the repository has no codeql-config.yml at all — .github/workflows/codeql-analysis.yml runs stock, with no config-file:. So every disposition lives in the GitHub UI: unversioned, un-greppable, invisible to git blame, and unreviewable in a PR.
Live latest-open sweep: checked latest 6 open issues at 2026-08-21T22:3x UTC; gh search issues for code-scanning dismissal OR codeql ledger OR prototype enrichment returned zero. No equivalent exists.
The Problem
Neo deliberately enriches prototypes. js/prototype-pollution-utility will therefore fire on intentional framework code for as long as the framework exists — currently src/Neo.mjs:563,565. The operator's position is correct and is a design statement: "we know exactly what we are doing there."
The tempting fixes are both wrong, and one is measurably wrong:
| approach |
why not |
global query-filters exclusion of the rule |
would have hidden alert 113 — a genuine defect in generateDocsJson.mjs under the same rule id, fixed in PR #17496 |
paths-ignore: src/Neo.mjs |
blinds every query on the root file to suppress one |
So per-alert dismissal is not a fallback — it is the only mechanism with the right granularity. That is a fine outcome for the suppression itself, and a bad one for the reasoning, which is the part that has to survive.
What actually decays: in six months someone reads Neo.merge, sees a for…in writing target[key] with no __proto__ guard, and has no way to learn that this was examined, that the 19 call sites were enumerated, and that the conclusion was deliberate. They either re-derive it, or "fix" intentional behaviour, or file the ticket again. The dismissal reason exists — in a UI they will not think to open.
The Architectural Reality
.github/workflows/codeql-analysis.yml — stock setup, no config-file:
src/Neo.mjs:552-570 — Neo.merge, the flagged site; for (const key in source) enumerates inherited and JSON-parsed __proto__ alike
learn/agentos/process/ — where Neo already keeps its durable process ledgers (contract-ledger.md, evidence-ladder.md, correction-culture.md); the natural home for a dismissal ledger
Measured reachability behind the disposition: 19 Neo.merge call sites across src/ and apps/, all fed author-controlled config — none from JSON.parse, fetch, response body, or query parameters.
The Fix
Two artifacts, neither of which suppresses anything — both make the reasoning durable where the scanner cannot:
- An in-source note at the
Neo.merge site recording the intent and the reachability argument, so the code carries its own disposition.
learn/agentos/process/code-scanning-dispositions.md — a ledger keyed by rule + path: the disposition, the reason, the evidence, the decider, the date. Reviewable in a PR, greppable, and a place for the next agent to check before filing a duplicate.
Deliberately not in scope: changing Neo.merge. An earlier draft of mine proposed guarding the three prototype keys. Withdrawn — the reachability is nil today, the cost lands on a hot-path config merge, and "a future caller might be undisciplined" is thin against an explicit statement of intent from the design owner.
Acceptance Criteria
Out of Scope
- Dismissing alerts 62/63 — a human-owned action in the security tab. This ticket prepares the reasons; @tobiu applies them.
- Any change to
Neo.merge or to the class system's prototype work.
- Adding a
codeql-config.yml. Nothing here needs one, and adding an empty config invites the global-exclusion mistake later.
Avoided Traps
Treating "the scanner is wrong" as "nothing to record". The dismissal is correct and the reasoning behind it is load-bearing for the next reader. Those are separable, and only the first currently has a home.
Letting one rule id imply one disposition. Alert 113 fired under the same rule and was a real bug. Any artifact produced here must make that distinction explicit, or it becomes the justification for a blanket exclusion later.
Related
Retrieval Hint: codeql dismissal ledger prototype enrichment Neo.merge query-filters path scoping
Origin Session ID: 752da6ac-a6c3-447f-8847-1da4ce49deb8
Decision Record impact: none — process documentation, no ADR authority touched. Structure-map gate: N/A, no ai/ surface; the ledger follows learn/agentos/process/ sibling precedent.
Context
Triaging the code-scanning queue surfaced a substrate gap that outlives any individual alert: CodeQL cannot express "this rule, on this path, is intentional", and Neo has nowhere in-repo to say it either.
Verified against the GitHub documentation rather than assumed:
query-filtersexclude by query id or tags — not by path.paths/paths-ignoreexclude a file from every query, not one.And the repository has no
codeql-config.ymlat all —.github/workflows/codeql-analysis.ymlruns stock, with noconfig-file:. So every disposition lives in the GitHub UI: unversioned, un-greppable, invisible togit blame, and unreviewable in a PR.Live latest-open sweep: checked latest 6 open issues at 2026-08-21T22:3x UTC;
gh search issuesforcode-scanning dismissal OR codeql ledger OR prototype enrichmentreturned zero. No equivalent exists.The Problem
Neo deliberately enriches prototypes.
js/prototype-pollution-utilitywill therefore fire on intentional framework code for as long as the framework exists — currentlysrc/Neo.mjs:563,565. The operator's position is correct and is a design statement: "we know exactly what we are doing there."The tempting fixes are both wrong, and one is measurably wrong:
query-filtersexclusion of the rulegenerateDocsJson.mjsunder the same rule id, fixed in PR #17496paths-ignore: src/Neo.mjsSo per-alert dismissal is not a fallback — it is the only mechanism with the right granularity. That is a fine outcome for the suppression itself, and a bad one for the reasoning, which is the part that has to survive.
What actually decays: in six months someone reads
Neo.merge, sees afor…inwritingtarget[key]with no__proto__guard, and has no way to learn that this was examined, that the 19 call sites were enumerated, and that the conclusion was deliberate. They either re-derive it, or "fix" intentional behaviour, or file the ticket again. The dismissal reason exists — in a UI they will not think to open.The Architectural Reality
.github/workflows/codeql-analysis.yml— stock setup, noconfig-file:src/Neo.mjs:552-570—Neo.merge, the flagged site;for (const key in source)enumerates inherited and JSON-parsed__proto__alikelearn/agentos/process/— where Neo already keeps its durable process ledgers (contract-ledger.md,evidence-ladder.md,correction-culture.md); the natural home for a dismissal ledgerMeasured reachability behind the disposition: 19
Neo.mergecall sites acrosssrc/andapps/, all fed author-controlled config — none fromJSON.parse, fetch, response body, or query parameters.The Fix
Two artifacts, neither of which suppresses anything — both make the reasoning durable where the scanner cannot:
Neo.mergesite recording the intent and the reachability argument, so the code carries its own disposition.learn/agentos/process/code-scanning-dispositions.md— a ledger keyed by rule + path: the disposition, the reason, the evidence, the decider, the date. Reviewable in a PR, greppable, and a place for the next agent to check before filing a duplicate.Deliberately not in scope: changing
Neo.merge. An earlier draft of mine proposed guarding the three prototype keys. Withdrawn — the reachability is nil today, the cost lands on a hot-path config merge, and "a future caller might be undisciplined" is thin against an explicit statement of intent from the design owner.Acceptance Criteria
src/Neo.mjscarries a note at the flagged site stating the intent, the reachability finding, and that the alerts are dismissed rather than unnoticedlearn/agentos/process/code-scanning-dispositions.mdexists with one row per dismissed alert: rule id, path, disposition, reason, evidence, decider, dateOut of Scope
Neo.mergeor to the class system's prototype work.codeql-config.yml. Nothing here needs one, and adding an empty config invites the global-exclusion mistake later.Avoided Traps
Treating "the scanner is wrong" as "nothing to record". The dismissal is correct and the reasoning behind it is load-bearing for the next reader. Those are separable, and only the first currently has a home.
Letting one rule id imply one disposition. Alert 113 fired under the same rule and was a real bug. Any artifact produced here must make that distinction explicit, or it becomes the justification for a blanket exclusion later.
Related
Retrieval Hint:
codeql dismissal ledger prototype enrichment Neo.merge query-filters path scopingOrigin Session ID: 752da6ac-a6c3-447f-8847-1da4ce49deb8
Decision Record impact: none — process documentation, no ADR authority touched. Structure-map gate: N/A, no
ai/surface; the ledger followslearn/agentos/process/sibling precedent.