Use when the user asks to "fix false positives", "check FP stats", "triage autofixer false positives", "why is rule X getting dismissed", or wants to reduce dismissal rates in the program_autofixer. Queries live dismissal telemetry from Databricks, ranks rules by false-positive burden, diagnoses the root cause in the rule's visitor, and implements a fix with the user approving each step.
Scanned 9/7/2026
Install to Claude Code
npx -y skills add jiayaoqijia/cryptoskill --skill solana-fix-false-positives --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Solana Fix False Positives?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/jiayaoqijia-solana-fix-false-positives)More formats (shields.io, HTML) on the badges page.
---
name: fix-false-positives
description: Use when the user asks to "fix false positives", "check FP stats", "triage autofixer false positives", "why is rule X getting dismissed", or wants to reduce dismissal rates in the program_autofixer. Queries live dismissal telemetry from Databricks, ranks rules by false-positive burden, diagnoses the root cause in the rule's visitor, and implements a fix with the user approving each step.
user-invocable: true
---
# Fix autofixer false positives
Reduce `program_autofixer` false positives using real dismissal telemetry. The model dismisses an issue when it verifies a `false_positive_hints` condition holds, so a high dismissal rate on a rule means that rule's analysis is missing something the model can see.
This skill is a conversation, not a batch job. Never implement a fix the user has not approved, and never claim a root cause you have not confirmed by reading the rule source.
## 1. Fetch the stats
Rank rules by dismissal burden over the last 90 days:
```sql
WITH parsed AS (
SELECT from_json(response_text, 'rules array<string>, dismissed_rules array<string>') AS j
FROM ${catalog}.${schema}.mcp_tool_calls
WHERE tool_name = 'program_autofixer' AND row_type = 'response'
AND timestamp >= current_timestamp() - INTERVAL 90 DAYS
),
fires AS (
SELECT explode(j.rules) AS rule, 1 AS fired, 0 AS dismissed FROM parsed
UNION ALL
SELECT explode(j.dismissed_rules) AS rule, 0 AS fired, 1 AS dismissed FROM parsed
)
SELECT rule, sum(fired) AS fires, sum(dismissed) AS dismissals,
round(100.0 * sum(dismissed) / nullif(sum(fired), 0), 1) AS dismissal_pct
FROM fires
GROUP BY rule
HAVING sum(fired) > 0
ORDER BY dismissals DESC
LIMIT 20
```
Use `mcp__databricks__execute_sql_read_only`. Substitute `${catalog}` and `${schema}` from the deployed bundle values for the `catalog` and `schema` variables declared in `databricks.yml`; they are intentionally not committed, so ask the user if you cannot resolve them. Note that `rules` is a distinct set per tool call, so `fires` counts calls where the rule appeared, not individual issues.
Rank by absolute `dismissals`, not by percentage: a rule at 50% of 4 fires is noise, a rule at 20% of 800 fires is the real cost. Ignore rules under ~20 fires.
The 90-day window spans past fixes. Check `git log --oneline -20 -- lib/tools/programAutofixer/visitors/` for rules already fixed; for those, re-run the query with a window starting after the fix deployed to see whether it actually worked, and tell the user the before/after. That is how the loop closes: a previous run's fix is graded at the start of the next run, not at the end of its own.
Present the top rules to the user as a short table and ask which one to work on. Recommend one and say why.
## 2. Find which blind spot
For the chosen rule, break dismissals down by which `false_positive_hints` index the model matched:
```sql
WITH parsed AS (
SELECT from_json(response_text, 'dismissed_hint_counts map<string, map<string, int>>') AS j
FROM ${catalog}.${schema}.mcp_tool_calls
WHERE tool_name = 'program_autofixer' AND row_type = 'response'
AND timestamp >= current_timestamp() - INTERVAL 90 DAYS
),
hits AS (SELECT explode(j.dismissed_hint_counts) AS (rule, buckets) FROM parsed)
SELECT rule, bucket, sum(n) AS dismissals
FROM hits LATERAL VIEW explode(buckets) AS bucket, n
WHERE rule = '<RULE>'
GROUP BY rule, bucket
ORDER BY dismissals DESC
```
Bucket keys are the hint index as a string, `'other'` when no listed hint matched, or `'unspecified'` when the caller omitted `matched_hint`. Map each index back to the hint text in the rule's visitor.
These counts are per dismissed issue, while step 1 counts calls, so the buckets sum higher than the step 1 dismissal count. Compare buckets against each other, not against step 1.
A large `other` bucket means the hints themselves are incomplete: the model is finding a blind spot nobody has written down. That is a different fix from a hint index dominating.
Dismissal `reason` text and fingerprints are deliberately not logged, so the telemetry tells you *which* condition, never the specific code. Do not go looking for a raw reason column.
## 3. Diagnose
Read the rule at `lib/tools/programAutofixer/visitors/<rule>.ts` and its `false_positive_hints`. Shared analysis lives in `_helpers.ts` and `_anchor-helpers.ts`; the walker and visitor context are in `walk.ts` and `handler.ts`.
Common root causes seen so far:
- **Scope too narrow.** Rule checks only the current function, but the verification lives in a caller, a helper, or elsewhere in the file. This was the `arbitrary-cpi` case (commit `9bbf0be`).
- **Pattern too literal.** Rule matches one spelling of a safe construct and misses equivalent ones (builder chains, qualified paths, aliases).
- **Missing framework knowledge.** Anchor constraint or macro already enforces the property, rule does not know about it.
State the root cause to the user in a couple of sentences with the file:line evidence. If the telemetry and the source disagree, say so and stop rather than guessing.
## 4. Propose, then confirm
Propose the narrowest fix that kills the identified bucket. Give the user:
- what changes, in which file
- roughly how many of the observed dismissals it should eliminate
- what it deliberately does not cover
- the false-negative risk: every FP fix widens what the rule accepts as safe, so name what a real attacker could now slip past
Ask the user to approve, adjust, or reject before writing any code. If the fix would suppress a genuine vulnerability class, say that plainly and prefer rewording the hint over loosening the check.
## 5. Implement
- Edit the rule (and helpers, if the analysis is shared).
- Add fixtures to `tests/unit/programAutofixer/fixtures-anchor.ts` or `fixtures-pinocchio.ts` covering both directions: the FP pattern must stop firing, and a near-miss unsafe variant must still fire.
- Run `pnpm test`. Report the real result; if anything fails, say so and fix it before continuing.
- Update `false_positive_hints` if the fix makes a listed condition obsolete or reveals a new one.
Show the user the diff and ask before committing. On approval, stage only the files the fix touched, never `git add -A`, and commit with a `fix(autofixer):` message describing the behavior change. Do not push or open a PR unless the user asks.
Passing tests are not proof the fix worked; only telemetry is, and that arrives weeks later. Step 1 of the next run grades this one.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!