Refine
thespider-agent refine picks actionable findings, has a local model write a fix, records the commits as a proposed fix, then runs a retest. It repeats until that backlog is empty or --max-iterations is hit.
Refine itself never writes fixed or still_vulnerable. Only a finished retest does.
Refine asks the agent to list actionable findings from TheSpider Cloud, run an agentic fix in a local worktree, record the commits as needs_retest, then retest. Only a finished retest marks a finding fixed or still vulnerable. The loop repeats until the backlog is clear.
Repeat until the selected backlog is empty, or `--max-iterations` is hit.
One iteration
- Select.
status=actionable, filtered by--min-severity(a floor:highmeanscriticalandhigh),--branch, and--profile. Sorted by severity, capped at 100. A truncated list is disclosed, never called "clear". Empty list exits0. - Fix. Re-fetch each finding (skip it if a human already triaged it). Render the finding packet into a fix prompt. Run the fix binding in agentic mode in the worktree. The prompt asks for a small targeted fix, cheap tests, a commit, and no push.
- Validate. Counts only if the model exited
0, the tree is clean, and at least one new commit has the pre-fix HEAD as an ancestor. Otherwise that finding is a no-op. - Record.
needs_retestplus the commit list. The server stores aproposedfix row and setsfixed_at_committo the last SHA. The finding stays actionable. - Retest. Create a retest campaign at the post-fix HEAD, run its slices with the retest binding, wait until it finishes. Failed slices mark the run degraded; the finding stays
needs_retestand comes back next iteration. - Re-list.
fixeddrops out of actionable.still_vulnerablestays in.
An iteration that made zero new commits and worked zero campaigns stops early.
Retest slices go through the same runner as run and daemon, so each one leaves a per-lease run log you can follow with thespider-agent log. The fix runs in step 2 use a different runner and are not logged that way.
Flags
thespider-agent refine [--max-iterations N] [--min-severity SEV] [--branch NAME] [--list]
[--model CODE] [--retest-model CODE] [--fix-timeout SECS] [--retest-timeout SECS]
[--in-place] [--profile security|bugs] [--redact] [--json]
| Flag | Default | Meaning |
|---|---|---|
--max-iterations | 3 | Stop after N loops. 1 is fix once and verify. |
--min-severity | none | A floor, not a set. high expands to critical,high. |
--branch | none | Filter and guardrail: the checkout must already be on this branch. |
--list | off | Dry run. Print the selection, write nothing. Exit 0 if empty, 1 if work remains. |
--model | first project binding | The models.dev code that fixes. |
--retest-model | same as --model | The models.dev code that retests. Required when the fixer is a freeform command. |
--fix-timeout | binding timeout | Seconds for one agentic fix. |
--retest-timeout | 1800 | Seconds to wait for each retest campaign. |
--in-place | off | Fix in the parent checkout. Needs a clean tree. No merge copy at the end. |
--profile | repo config | security or bugs. all is rejected. |
--redact | off | Same client-side stdout scrub as run --redact. |
--json | off | Versioned envelope. See Agent CLI contracts. |
thespider-agent refine --min-severity high --max-iterations 2
Exit codes
| Code | Name | Meaning |
|---|---|---|
0 | OK | Selected backlog is empty. |
1 | FINDINGS | Work remains after --max-iterations. |
3 | TIMEOUT | A retest wait exceeded --retest-timeout. |
4 | DEGRADED | A retest finished with failed slices, or with no conclusion. |
5 | USAGE | Bad flags, --profile all, unbound models, or a 409 commit_mismatch. |
6 | OPERATIONAL | Transport, auth, or missing worktree tooling. |
7 | INTERRUPTED | SIGINT/SIGTERM. The worktree is left in place with resume hints. |
When several apply: 7 > 3 > 4 > 1 > 0.
Worktree
By default refine works in a throwaway git worktree, so your checkout is not the fix agent's working directory:
git worktree add <tmp> -b thespider/refine/<project-slug>-<utc-ts> <parent HEAD>
- The worktree is removed on a normal exit. The branch is kept.
- Merge instructions print at the end (worktree mode only).
- A failing
pre-pushhook is installed in the worktree, so the agent cannotgit push. Commits are authored asthespider-agent <refine@thespider.local>. - Campaigns record your original checkout branch, not the worktree branch name.
--in-place skips the worktree. The tree must be clean before each fix.
Before you point it at a repo
Finding text is model-derived. The packet renderer strips heading forgeries and control characters, but the fix agent is a writer: your credentials, your network, your host. A worktree keeps its commits off your branch. It is not a sandbox.
A freeform command runs exactly what you wrote. Built-in agentic modes (acceptEdits, workspace-write) auto-approve edits and can still hang on other prompts. --fix-timeout bounds that.
The push blocker is a git hook, not a network control. It stops git push. It does not stop anything that talks to the remote another way.
Metering
Every retest slice is a billable slice_claim at claim time (first claim per slice; retries do not double-count). Retest ingest may also meter ai_dedupe_call. Refine prints the campaign id, slice count, and that note as each campaign is created. Plan limits: Pricing.
Proposed vs fixed
needs_retest+ commits writes aproposedfix row. The finding stays actionable.- A retest that does not see it again concludes
fixedand marks that rowverified. - A retest that sees it again concludes
still_vulnerable. The proposed row stays proposed for the next fix.
If refine crashes mid-run, the next run re-selects the same finding. Expect history to cycle needs_retest → still_vulnerable → needs_retest until a fix verifies. That trail is the point.
Interrupt
On interrupt, refine leaves the worktree and prints its path, branch, in-flight campaign ids, and:
thespider-agent run --campaign <id> --once
A later refine run warns about non-terminal retest campaigns and prints the same hint.