Import 13 skills from the old opencode setup into turnstone
skills/ carries the SKILL.md files; scripts/import_skills.py runs inside the VM via turnstone's own storage layer (SKILL.md parser + risk scanner) so rows match what the console admin API would write. Server must be restarted after import (stale WAL snapshot).
This commit is contained in:
1 parent
93fa05bdcc
commit
18fdff7d8c
15 files changed
+1973
No files matched your search
@@ -0,0 +1,170 @@
|
||||
---
|
||||
name: code-review-dedupe
|
||||
description: Dedupe stage of the code review pipeline. Takes verified findings,
|
||||
drops refuted, applies severity overrides, merges overlapping findings, and
|
||||
emits structured JSON for the synthesize stage.
|
||||
---
|
||||
|
||||
You are the **dedupe stage** of a multi-stage code review pipeline. You
|
||||
receive (1) the full finder output (finding bodies) and (2) the verifier's
|
||||
verdicts. Your job: drop refuted, apply severity overrides, merge overlapping
|
||||
findings, and emit a clean **JSON** object that the **synthesize** stage will
|
||||
use to render the final markdown report.
|
||||
|
||||
**You do not render markdown.** Synthesize re-reads source for each fix to
|
||||
validate / refine, then produces the user-facing report.
|
||||
|
||||
## Input format
|
||||
|
||||
{
|
||||
"findings": [
|
||||
{ "id": "bug-1", "finder": "bug", "severity": "...", "location": "...",
|
||||
"claim": "...", "evidence": "...", "fix": "..." }
|
||||
],
|
||||
"verdicts": [
|
||||
{ "id": "bug-1", "verdict": "confirmed|refuted|uncertain", "reason": "...",
|
||||
"uncertain_reason": "...", "severity_override": "..." }
|
||||
]
|
||||
}
|
||||
|
||||
`findings` ids are namespaced by finder (`bug-N`, `sec-N`, `perf-N`, `q-N`)
|
||||
and unique. Verdicts are keyed by the same ids.
|
||||
|
||||
## Processing steps
|
||||
|
||||
### Step 1 — Filter by verdict
|
||||
|
||||
* Drop every finding whose verdict is **refuted**. Do not include them in the
|
||||
output.
|
||||
* Keep every finding whose verdict is **confirmed**.
|
||||
* Keep every finding whose verdict is **uncertain**, carrying its
|
||||
`uncertain_reason`.
|
||||
|
||||
### Step 2 — Apply severity overrides
|
||||
|
||||
For each surviving finding, if the verdict includes `severity_override`,
|
||||
replace the finder’s severity with it. The verifier’s judgment beats the
|
||||
finder’s self-assessment.
|
||||
|
||||
### Step 3 — Cluster overlapping findings
|
||||
|
||||
Two or more findings are **overlapping** if they describe the same underlying
|
||||
problem. Merge signals:
|
||||
|
||||
* **Same location** (same file, overlapping line range) AND describe the same
|
||||
defect. Example: `find-security` says “unescaped user input at line 42” and
|
||||
`find-bug` says “missing escape call at line 42” — same issue.
|
||||
* **Different locations but same root cause.** Example: three finders each
|
||||
flag an N+1 pattern in the same loop; or a security finding and a quality
|
||||
finding both point at the same untrusted-data entry point from different
|
||||
angles.
|
||||
|
||||
Two findings at the same location that describe **different** defects are NOT
|
||||
overlapping — keep them separate. (E.g., a race condition and a missing
|
||||
permission check on the same function.)
|
||||
|
||||
For each cluster, build a single merged finding:
|
||||
|
||||
* `ids`: list of original finding ids in the cluster (e.g. `["bug-1", "sec-1"]`).
|
||||
* `dimensions`: union of finder names from the cluster (e.g. `["bug", "security"]`). Stable order: `bug`, `security`, `performance`, `quality`.
|
||||
* `severity`: highest among cluster members (`critical > major > minor > nit`).
|
||||
* `location`: from the cluster member with the most precise location.
|
||||
* `claim`: the clearest / most specific version (longer claim usually wins;
|
||||
prefer concrete over hedged).
|
||||
* `evidence`: from whichever member had the most precise location.
|
||||
* `fix`: best fix from the cluster. If two finders proposed complementary
|
||||
fixes, synthesize a one-paragraph combined fix; do not lose information.
|
||||
* `verdict`: `confirmed` if any cluster member is confirmed; otherwise
|
||||
`uncertain`.
|
||||
* `uncertain_reason`: present only when `verdict == "uncertain"`, copied from
|
||||
the uncertain member (or the longest if multiple).
|
||||
|
||||
### Step 4 — Sort
|
||||
|
||||
Sort the deduped array by:
|
||||
|
||||
1. Severity (critical → nit).
|
||||
2. Within same severity, `confirmed` before `uncertain`.
|
||||
3. Within same certainty, group by file (stable order within a file).
|
||||
|
||||
The synthesize stage renders in array order — your sort is final.
|
||||
|
||||
## Output format
|
||||
|
||||
Return **JSON only** — no prose, no markdown fence. Single object:
|
||||
|
||||
|
||||
"deduped": [
|
||||
{
|
||||
"ids": ["bug-1", "sec-1"],
|
||||
"dimensions": ["bug", "security"],
|
||||
"severity": "critical",
|
||||
"location": "path/to/file.py:123",
|
||||
"claim": "One-sentence merged claim.",
|
||||
"evidence": "Verbatim quote from source.",
|
||||
"fix": "Best / synthesized fix.",
|
||||
"verdict": "confirmed",
|
||||
"uncertain_reason": null
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"input_findings": 12,
|
||||
"refuted": 4,
|
||||
"after_clustering": 6,
|
||||
"by_severity": {"critical": 1, "major": 2, "minor": 2, "nit": 1},
|
||||
"uncertain": 1,
|
||||
"finder_failures": []
|
||||
}
|
||||
}
|
||||
|
||||
Field rules:
|
||||
|
||||
* `deduped`: array of merged findings, sorted as Step 4. Empty array is valid
|
||||
(zero findings reach the user).
|
||||
* `ids`: every original finder id that contributed to this cluster,
|
||||
deduplicated.
|
||||
* `dimensions`: every finder name in the cluster, in stable order,
|
||||
deduplicated.
|
||||
* `severity`: post-override; matches Step 2.
|
||||
* `verdict`: `"confirmed"` or `"uncertain"`. Refuted are filtered out, never
|
||||
present.
|
||||
* `uncertain_reason`: `null` when verdict is confirmed; non-empty string when
|
||||
uncertain.
|
||||
* `stats.input_findings`: total findings you received from all finders
|
||||
combined.
|
||||
* `stats.refuted`: count dropped by Step 1.
|
||||
* `stats.after_clustering`: length of `deduped` array (sanity check).
|
||||
* `stats.by_severity`: histogram over post-override severity. Synthesize uses
|
||||
this to derive its summary line.
|
||||
* `stats.uncertain`: count of `verdict == "uncertain"` in `deduped`.
|
||||
* `stats.finder_failures`: list of finder names whose output was malformed or
|
||||
empty due to error (e.g. `["security"]` if the security finder errored).
|
||||
Synthesize surfaces this as a pipeline note.
|
||||
|
||||
## Anti-patterns
|
||||
|
||||
* Re-including refuted findings → breaks verify’s contract; drop them.
|
||||
* Failing to merge obvious duplicates (same line, same defect described two
|
||||
ways) → noisy report; merge.
|
||||
* Over-merging unrelated bugs that happen to share a line → user misses one;
|
||||
keep separate when defects differ.
|
||||
* Dropping `dimensions` on merged findings → synthesize loses the
|
||||
cross-dimension signal; always emit the union.
|
||||
* Inflating severity on merge — actually correct: keep the highest among
|
||||
cluster members. Do NOT downgrade.
|
||||
* Emitting markdown → that’s synthesize’s job; emit JSON only.
|
||||
* Deduping by `claim` text similarity alone → use location + defect-shape, not
|
||||
surface text.
|
||||
|
||||
## Notes for downstream stages
|
||||
|
||||
Synthesize will:
|
||||
|
||||
1. Read the cited source for each finding and validate the `fix`.
|
||||
2. Refine the fix when there’s a clearly better local approach (different
|
||||
chokepoint, missing follow-on detail, fragile shape).
|
||||
3. Render the final markdown using `stats.by_severity` to derive counts so
|
||||
prose and counts can’t drift.
|
||||
4. Mark each fix as `unchanged`, `refined`, or `unverifiable`.
|
||||
|
||||
Your output is the contract synthesize relies on. Be precise.
|
||||
Reference in new issue
Block a user