Files
OwnCord/.claude/skills/bughunt-run/SKILL.md
T
J3vb db0275a290 fix: batch of 29 correctness fixes across server and client (#1369)
* fix(ws): 2 defect(s) (OC-0013, OC-0140)

* fix(voice): 1 defect(s) (OC-0044)

* fix(ws): 1 defect(s) (OC-0024)

* fix(server): 1 defect(s) (OC-0027)

* fix(ws): 1 defect(s) (OC-0028)

* fix(server): 7 defect(s) (OC-0033, OC-0066, OC-0067, OC-0068, OC-0074, OC-0077, OC-0106)

* fix(ws): 1 defect(s) (OC-0051)

* fix(client): 1 defect(s) (OC-0053)

* fix(client): 1 defect(s) (OC-0055)

* fix(service): 1 defect(s) (OC-0069)

* fix(voice): 1 defect(s) (OC-0072)

* fix(service): 1 defect(s) (OC-0082)

* fix(client): 1 defect(s) (OC-0083)

* fix(plugin): 1 defect(s) (OC-0088)

* fix(plugin): 4 defect(s) (OC-0104, OC-0126, OC-0127, OC-0133)

* fix(admin): 1 defect(s) (OC-0110)

* fix(client): 1 defect(s) (OC-0114)

* fix(api): 1 defect(s) (OC-0139)

* fix(client): 1 defect(s) (OC-0149)

* test(server): adapt existing tests to updated OpenDM and IncrementMentionCounts signatures

* style(plugin): modernize loops and goroutine spawns in race test

* fix(ws): mirror the focus admission gate in the post-subscribe revalidation

* fix(service): detach DM post-commit side effects from the request ctx, fail delete closed, add empty-fan-out fallback

* fix(plugin): preserve enabled intent when upgrade reactivation hits a runtime-less build

* chore(skills): harden bughunt-fix workflow and fold review lessons into bughunt-run/db-change

* Add comprehensive documentation for task-observer skill

- Introduced environments.md to outline activation setup, compaction behavior, and handoff-doc mode.
- Created skill-authoring.md detailing taxonomy, licensing, confidentiality, and editing rules for skill creation.
- Added weekly-review.md for a structured review process of OPEN observations, including scheduled and in-session fallback modes.

* chore(go): pin toolchain go1.26.6 (stdlib CVE fixes flagged by govulncheck)
2026-08-14 10:05:40 +02:00

11 KiB

name, description
name description
bughunt-run Run a bug hunt and turn its findings into committed fixes. Use when starting a hunt, resuming one, or fixing findings already in the ledger. Covers the ledger handoff between the bughunt and bughunt-fix workflows.

Running the bughunt pipeline

Two workflows with a human gate between them. The ledger at .superpowers/findings-ledger.json is the interface. Both workflows are pure functions of their argsthe session does all file I/O, because workflow scripts have no filesystem access.

.superpowers/ is gitignored. This repo is public and unfixed defects must never reach a commit, an issue, or a PR body.

1. Hunt

Launch the hunt from a turn that carries a token-budget directive (recommended: +25M, comfortably above a full 8-round run). The workflow's cost ceiling is gated on budget.total, which is null without a directive — a directive-less run has no ceiling at all. The workflow's first log line echoes the state: budget=25M means armed; budget=NONE - cost ceiling disarmed means stop the run and relaunch with a directive.

Before launching, in order:

  1. Rebuild the graph (stale coordinates aim the explore lens at moved code): graphify update . --no-cluster — local tree-sitter, zero LLM cost, ~10.7k nodes.
  2. Rank explore targets: node .superpowers/rank-explore.mjs — writes .superpowers/explore-ranking.json, deprioritizing files recorded clean in .superpowers/explored-clean.json and dropping files that no longer exist.
  3. Read the ledger and pass every record in as known, so the hunt does not re-derive anything already found, fixed, declined, or refuted.
Workflow({
  name: "bughunt",
  args: {
    known: <every record from findings-ledger.json, as {file, line, title, status}>,
    graph: <the rows of .superpowers/explore-ranking.json>,
    lenses: [ {key, prompt}, ... ],   // optional: scope the hunt to one subsystem
    maxRounds: 8,
    dryThreshold: 2,
  },
})

If graph is omitted or empty the hunt logs explore: args.graph absent/empty - falling back to churn-based fresh eyes and still runs — degraded targeting, never a smaller lens family.

Omit lenses for a general hunt across the rotating families.

Each lens object is {key, prompt}. key must match ^[a-z0-9-]+$ — lowercase letters, digits, and hyphens only. Keys get interpolated into agent labels of the form r<N>:hunt:<key>:<model>, and a key containing uppercase, dots, or spaces breaks label parsing. A lens missing key or prompt is not validated — it reaches the finder prompt as the literal string undefined, silently degrading that lens instead of failing loudly. Check your lens objects before passing them.

When it returns, first save the raw result verbatim to .superpowers/hunts/<YYYY-MM-DD>-raw.json, then:

node .superpowers/render-run-stats.mjs .superpowers/hunts/<YYYY-MM-DD>-raw.json <hunt-name>

This validates the result shape, appends the run's telemetry to .superpowers/run-history.json, updates .superpowers/explored-clean.json, and checks every confirmed finding's coordinates against the working tree (file exists, line within length — the report agent that used to spot-check two findings is gone). Resolve any COORD warnings before appending to the ledger: stale coordinates poison bughunt-fix.

Then append each entry of result.confirmed to the ledger with status: "open", an id from nextId, and today's date. Bump nextId. The incoming record carries a prose fix field (bughunt's suggested remedy) — rename it to suggestedFix when appending, so the ledger's fix field starts as null and is free for bughunt-fix to fill in with {commit, test, revertProof} once something is actually fixed. Then:

node .superpowers/render-ledger.mjs

Each confirmed record carries finder: "opus". The dual-model finder panel was retired 2026-08-12: attribution over the only measured run priced sonnet's unique yield (1 high, 4 medium, 9 low) at roughly a third of the run's agents. The known cost: with one finder, a lazy-but-non-null finder round can read as "clean" where the panel required both models to agree it was. Watch runStats — per-lens candidate counts make an anomalously empty lens visible after the fact.

2. Gate (human)

Read .superpowers/FINDINGS.md. Mark anything you do not want fixed as declined with a rationale — declined findings are fed back into the next hunt's prompts and never re-reported.

3. Fix

Workflow({
  name: "bughunt-fix",
  args: {
    findings: <records with status "open" from findings-ledger.json>,
    branch: "fix/bughunt-YYYY-MM-DD",
    only: ["OC-0042"],        // optional
    maxSeverity: "medium",    // optional
    circuitBreaker: { threshold: 0.5, minAttempts: 3 },  // optional; false to disable
  },
})

Create and check out the branch first — the workflow commits to whatever branch is current and does not create one.

When a run trips the breaker

The run stops early if more than threshold of attempted findings fail, once at least minAttempts have been tried. declined never counts as a failure — a run where several findings are correctly declined is a good run. There are two trip points: the fix stage (before any prove agent runs) and inside the prove loop.

A tripped run means stop and investigate, do not re-run. The usual causes are being on the wrong branch, a broken test runner, or ledger coordinates gone stale after a rebase. Re-running without fixing the cause just spends the budget again.

Findings from clusters the run never reached come back blocked with a rationale naming the breaker. Set those back to open once the underlying problem is fixed — they were never attempted. Their edits are sitting uncommitted in the working tree, so the debris warning above applies to them too.

Whatever committed before the trip still goes through the gate, so result.gate tells you whether those commits are green.

When it returns, for each entry in result.results:

  • fixed → status fixed, fix: {commit, test, revertProof: "self-reported"} using the matching result.commits entry — the prove agent's own report, not yet independently checked (see step 4)
  • declined → status declined, copy the rationale
  • blocked → status blocked (not open), record the rationale; these failed their revert-proof, tripped the cross-cluster overlap guard, or their agent died, and want a human. Do NOT leave them openbughunt-fix only picks up open findings, so open would silently re-enter one of these into the next fix run, exactly the retry loop the design deliberately excludes ("one human look beats three agent attempts").

Ledger writes select records by id or fix.commit — never by date fields, which collide when two batches reconcile on the same day — and every bulk mutation asserts its expected match count before writing (a same-day sibling batch once inflated a 29-record update to 44 matches; only the count assertion caught it).

Check result.gate. A failed gate leaves the commits in place on the branch — fix it yourself, do not re-run the workflow over it.

4. Verify the fixes independently — REQUIRED

The workflow's prove agent self-reports that each test went RED with the fix reverted. Nothing inside the workflow can verify that: workflow scripts have no filesystem access. You do. Run the independent proof over every commit the workflow made:

node .superpowers/verify-fixes.mjs <sha> <sha> ...

It reverts each commit's source files to the parent, runs that stack's tests, and requires them to FAIL — then restores and requires them to PASS. This is the only check in the pipeline no agent can fabricate.

While it runs it checkouts and restores source files, so the working tree is not a stable read surface: anything reading concurrently — review agents, scanners, hooks — must read committed objects (git show HEAD:<path>) or pre-taken snapshots, and any live-tree scanner finding from that window needs re-verification against HEAD before it is believed.

Any FAIL ... VACUOUS TEST means the fix was committed behind a test that proves nothing. Revert that commit and set its findings back to open; do not talk yourself into keeping it because the code change looks right.

One class is exempt from the insta-revert: a race/deadlock-class fix whose test only fails under its detector. verify-fixes escalates to -race before declaring vacuity; if an older copy of the script reports VACUOUS on such a fix, re-prove red/green by hand under the class's detector (go test -race ./<pkg>/) before reverting anything.

For every commit verify-fixes.mjs reports PASS, upgrade that commit's findings' fix.revertProof from "self-reported" to "pass" — this independent run is the only check in the pipeline no agent can fabricate, and it is what earns the upgrade. A FAIL commit needs no further edit here — it was already reverted and its findings set back to open above.

Re-render. Before you review the branch and open the PR, inspect the working tree: a blocked or declined cluster can leave its edits and any new failing test it wrote sitting uncommitted. Discard what you don't want — a reflexive git add -A would commit tests that describe unfixed defects into a public repo. Audit what got COMMITTED, too: parallel fix agents share the tree, so a prove agent can commit a sibling cluster's content that happened to sit in a shared test file or in regenerated output. Grep the committed tests for finding ids outside the run's fixed set, and re-run the generated-code verifies (sqlc/protocol) after the debris discard — a mismatch means a commit carries foreign regen content. Anything pinning or describing an UNFIXED finding must be excised from history (amend + rebase onto the amended commit), not merely removed by a follow-up commit.

Then review the branch against the merge-base — git diff origin/main...HEAD (three-dot), never two-dot: a concurrent merge plus a background fetch can move origin/main mid-run and turn the two-dot diff into phantom deletions. If origin moved, confirm zero file overlap and a clean git merge-tree --write-tree origin/main HEAD before opening the PR by hand. The workflow never pushes and never opens a PR.

Testing the workflows themselves

node .claude/workflows/bughunt.harness.mjs
node .claude/workflows/bughunt-fix.harness.mjs
node .superpowers/render-ledger.mjs --selftest
node .superpowers/verify-fixes.mjs --selftest
node .superpowers/rank-explore.mjs --selftest
node .superpowers/render-run-stats.mjs --selftest

All six run offline with zero API calls. Run them after any edit to the relevant script.