Skip to content

fix(multiscan): record run warnings in the campaign ledger - #295

Open
ppcvote wants to merge 1 commit into
openai:mainfrom
ppcvote:fix/multiscan-run-warnings
Open

fix(multiscan): record run warnings in the campaign ledger#295
ppcvote wants to merge 1 commit into
openai:mainfrom
ppcvote:fix/multiscan-run-warnings

Conversation

@ppcvote

@ppcvote ppcvote commented Aug 6, 2026

Copy link
Copy Markdown

Fixes #248.

runMultiscan called security.run() with no onWarning observer, so every warning the scan raised was discarded. A repository whose target drifted mid-run still completes, so it landed in results.jsonl as status: "completed" with nothing in the campaign output recording that the results describe a tree that moved.

The receipt now carries a warnings array when the scan raised any:

{"id":"drifted","status":"completed","attempt":1,"outputDir":"...",
 "warnings":[{"message":"Scan target changed during the run.","kind":"target_changed"}]}

kind comes from ScanWarningDetails, so a consumer can single out drift without matching on message text — the same distinction the CLI already makes at its own onWarning.

Three decisions worth review

Redaction. Warnings go through redactedErrorMessage, the redaction the failure path already applies. The ledger is a file on disk and a warning can quote a remote or a token. The CLI uses sanitizeDiagnosticValue for this, but that is private to cli.ts; redactedErrorMessage is the equivalent already imported into multiscan.ts. Happy to export the CLI one instead if you would rather have a single sanitizer.

Per attempt. The array is declared beside failure and cost inside the retry loop, so a second attempt does not inherit the first attempt's warnings.

status is untouched. The scan completed; promoting a warning to a failure would be a louder change than the issue asks for and would break resume, since completed receipts are what let a rerun skip finished repositories. The key is omitted entirely when nothing was raised, so a quiet run produces byte-identical output to today.

Verification

Windows 10, bun 1.3.11, bun test tests-ts/multiscan.test.ts:

with fix without fix
records run warnings in the receipt pass fail
redacts credential-shaped values pass fail
omits warnings when none raised pass pass (control)
totals 16 pass, 1 fail 14 pass, 3 fail

The two failing-without-the-fix tests are the ones that matter; the third is a control that passes either way and guards against warnings appearing on a quiet run.

The single unrelated failure in both columns is rejects output-directory symlinks, which needs symlink privileges this host does not grant. It fails identically on unmodified main, so it is not residue from this change.

Related

This is the multiscan half of the same shape as #251 and #195: the tool knew something and the knowledge did not reach the artifact a consumer reads. Those are about the single-scan --json and SARIF surfaces; this one is the ledger. I have not touched either of those paths — #251 already has someone working on it.

Fixes openai#248.

`runMultiscan` called `security.run()` with no `onWarning` observer, so every
warning the scan raised was dropped. A repository whose target drifted mid-run
still completes, so it landed in results.jsonl as `status: "completed"` with
nothing anywhere in the campaign output saying the results describe a tree that
moved. bulk-scan is the mode where nobody is watching an individual repository,
which is what makes the silence expensive.

The receipt now carries a `warnings` array when the scan raised any, each entry
`{ message, kind? }`. `kind` is preserved from ScanWarningDetails so a consumer
can single out drift without matching on message text, which is what the CLI
already does at the other call site.

Three details worth review:

- Warnings go through `redactedErrorMessage`, the same redaction the failure
  path already applies. The ledger is a file on disk, and a warning can quote a
  remote or a token, so it gets the same treatment as an error. The CLI runs its
  warnings through `sanitizeDiagnosticValue`, but that is private to cli.ts;
  `redactedErrorMessage` is the equivalent already imported here.
- The array is per attempt, declared beside `failure` and `cost`, so a retry
  does not inherit the previous attempt's warnings.
- `status` is untouched. The scan did complete, and turning a warning into a
  failure would be a different and much louder change than the issue asks for.
  The key is omitted entirely when nothing was raised, so existing consumers
  see no new field on a quiet run.

Three tests added. Two of them fail against the current code, which is the
point; the third is the quiet-run control that passes either way and guards
against warnings appearing spuriously.

Verified on Windows 10, bun 1.3.11: 16 pass with the fix, 14 pass and the two
new warning tests fail without it. The single unrelated failure in both runs is
"rejects output-directory symlinks", which needs symlink privileges this host
does not grant and fails identically on unmodified main.
@github-actions github-actions Bot added the bug Something isn't working label Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bulk-scan discards run warnings, so a drifted repository is recorded as completed with no signal

1 participant