Skip to content

The verification loop

A bugb run is a loop over one repository: annotate, export, synthesise goals, probe, write back, report — then rotate the perspective and do it again. This page explains what each step contributes and, more importantly, how the loop decides to stop.

This is not an argument that you need a loop. One pass of guardlink and one cxg pentest is a perfectly reasonable thing to run, and it is cheaper. The argument here is about what a single pass cannot tell you.

A single pass answers “which of the threats I could see today are exploitable”. The loop exists because that set moves:

  • Annotation is not exhaustive. An agent reading a repository from one perspective finds one set of threats. From another perspective it finds others — see Lenses.
  • A confirmed finding changes the map. Proving one control absent makes reachable a second thing that was previously guarded.
  • Probing fails for reasons that are not answers. A dead session or an unreachable target produces no evidence, and no evidence is not a result. Only a later round can turn that into one.

The journal of a real two-round run, with the goal attempts collapsed:

p00 preflight
p10 env
p20 auth
r0/p40 export
r1/p30 annotate
r1/p40 export
r1/p50 synthesize
r1/p60 pentest → goal g-1-01 … g-1-08, attempts 1–2 each
r1/p70 writeback
r1/p80 report
r2/p30 annotate
r2/p40 export

The three p00p20 steps run once. Everything from p30 runs per round.

Step Contributes
p00 preflight versions and capabilities of the tools it will drive
p10 env the target environment, from its recipe
p20 auth sessions for each identity
r0/p40 export a baseline export, before any round runs
p30 annotate an agent annotates the repository under this round’s lens
p40 export guardlink’s SARIF for what the model now says
p50 synthesize exposures become goals — scope, actor, preamble, mechanism, baseline, success
p60 pentest cxg runs each goal, concurrently, retrying with a mutated approach
p70 writeback confirmed findings become @confirmed annotations in the source
p80 report ledger, summary, advisories

Round 0’s export is the step most often overlooked, and it matters most in a repository that has been annotated before. Without it, round 1 would count an existing backlog of 100 annotations as 100 things it had discovered. With it, round 1 measures only what it added.

Every step is journalled, which is what makes bugb resume exact: a run interrupted in r2/p60 resumes there, and r1 does not re-execute.

A goal is assembled from six slots, and five of them are deterministic: the scope comes from the exposure and its resolved route, the actor from the lens and the seeded identities, the preamble from the environment recipe, the mechanism from the SARIF message verbatim, and the success condition from the vulnerability class. Only concrete exploit bodies and unresolved routes need an agent, batched into one call per round.

The preamble is the slot worth understanding. If a target needs a CSRF handshake and the probe does not perform it, every mutating request is rejected, the vulnerable code is never reached, and the run reports the mitigation as holding. That is a false negative shaped exactly like a clean result. Putting the handshake in the recipe — written once per target — is what keeps it out of the agent’s hands.

Three ways, each recorded with its reason.

Converged. Consecutive healthy dry rounds, up to the depth the plan set. A round is dry when it found nothing new. It is healthy when nothing degraded it.

Every lens retired. The run has nothing left to look from. A run tracks this per lens, in lenses.json, alongside the counters it decides on:

{
"lens_id": "unauth",
"attempts": 1,
"uses": 0,
"new_exposures": 0,
"confirmed": 0,
"consecutive_dry": 0,
"last_round": 1,
"retired": false,
"retired_reason": ""
}

A budget ceiling. Rounds, goals, or agent calls.

The distinction that carries the weight is healthy. A round degraded by timeouts or dead sessions is neither dry nor productive — it is neutral, and it neither advances nor resets the streak, because its emptiness proves nothing.

A run that degrades repeatedly halts and says so:

- **Status** — halted (2 consecutive degraded rounds; results are not trustworthy until the underlying failure is fixed)
  • Time. The real run above spent roughly 87 minutes over two rounds, mostly in p60: eight goals per round, twelve attempts in round 1 and sixteen in round 2, because a goal that fails is retried with a mutated approach.
  • Agent calls. Annotation, environment-recipe authoring, goal enrichment and write-back are all agent-driven, and the annotate phase runs every round.
  • Traffic against the target. Real exploit traffic, repeatedly, under several identities.
  • Repeated annotation of your source. Every round has an agent editing the repository on the run branch.

--rounds, --max-goals and --fast are the levers. --fast cuts to one template per goal and one retry, which is the right trade for a repeat run and the wrong one for discovery.