Debt score
Every rule carries a weight. The weights of the rules that matched are
summed, structural penalties are added (cyclomatic complexity, and for
Python missing annotations or docstrings), and the total is normalised
with 100 × points ÷ (points + 60). The full itemised
breakdown is in the "Debt score breakdown" panel on the diff tab — no
point in the score is unattributable.
Residual debt and reduction
Residual debt is produced by running the same scan over the generated output. It is a measurement. If synthesis changed nothing, the residual equals the initial score and the reduction reads 0%. A finding only counts as resolved when it is absent from the re-scan.
Verification
- Python output is compiled with
compile(). Other languages get a bracket-balance check, which is labelled as a heuristic because that is what it is. - A pytest harness is generated with one assertion per original finding and executed in a subprocess. Results come from pytest's JUnit XML.
- An assertion that fails because a finding is genuinely unresolved is marked known-unresolved and does not count as a broken build.
- If the output does not parse, the comparison is flagged unreliable and no resolutions are claimed — unparseable code would otherwise make every AST rule match nothing and look like a clean sweep.
Synthesis
With IBM_WATSONX_APIKEY and
IBM_WATSONX_PROJECT_ID set, rewriting goes through IBM
watsonx Granite; the model id is resolved against the models your
account actually exposes. A rejected attempt is sent back to the model
with the verifier's complaint attached. Without credentials a
deterministic transformer runs instead — it fixes what it can prove safe
and leaves everything else reported as unresolved.
Known limits
- The scanner is rule-based. It finds the patterns it knows, not every defect.
- Java, JavaScript, TypeScript, Go and PHP complexity figures are keyword estimates. Only Python gets a real AST.
- Generated JUnit/node:test/PHPUnit files are scaffolding and are never executed here — there is no JVM or Node runtime in this process.
- The output is not executed unless you set
BOBPULSE_ALLOW_EXEC=1, which you should only do inside a container. - "Effort avoided" is a severity-weighted guess, labelled as an estimate everywhere it appears.