Skip to content

feat(#498): adversarial injection/manipulation eval suite - #730

Merged
Weegy merged 5 commits into
byte5ai:mainfrom
sneumannb5:feat/498-adversarial-injection
Aug 19, 2026
Merged

feat(#498): adversarial injection/manipulation eval suite#730
Weegy merged 5 commits into
byte5ai:mainfrom
sneumannb5:feat/498-adversarial-injection

Conversation

@sneumannb5

@sneumannb5 sneumannb5 commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

What

Adversarial injection/manipulation eval suite (Closes #498). Deterministic wire-level probes gate every PR via npm test; key-gated behavioral tier (escalation + 3-juror Delphi) runs on main/nightly.

Why

Injection defenses (Privacy Shield v4 boundary, issue-body scrubber, ticket delimiter) were only asserted architecturally. This attacks them, reads the verdict off the wire artifact, and fails on a hardening regression or coverage loss.

Test plan

  • npm run typecheck:adversarial
  • adversarial unit tests (harness + probes + negative controls) — pass
  • npm run eval:adversarial keyless — 12/12 held; baseline byte-stable; regression/coverage-loss paths verified
  • behavioral tier (needs ANTHROPIC_API_KEY) — runs on first main push

Risk / blast radius

  • CI: new adversarial-eval.yml (key-guarded, mirrors golden-eval.yml); package.json adds 2 scripts.
  • No schema/API/runtime change — all under test/. No new required env-var.

View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

sneumannb5 and others added 3 commits August 18, 2026 09:04
…e attacker's

The three Delphi jurors each read the entire escalation transcript, and a
non-unanimous round 1 doubles that to six full reads. Pinning them to
`models.attacker` therefore made the eval's cheapest-to-judge step its most
expensive one: judging "did the digest boundary and the role hold" is a bounded
classification over text already in context, not the open-ended adversarial
reasoning the conductor does.

Splits `juror` out of `BehavioralModels` as its own field, wired through
`ADVERSARIAL_JUROR_MODEL` and defaulting to `claude-sonnet-5`. On the current
corpus (one behavioral scenario, maxTurns 4) that takes a run from roughly
$0.13-0.19 to $0.09-0.11, and the saving scales linearly as behavioral
scenarios are added — which is the point, since today's absolute number is
small but the jury is the part that grows fastest.

Safe because the jury is not the only scorer: `findIdentityLeaks` runs over the
same transcript and forces `breached` on any real identity value regardless of
the vote, so a leniently-voting juror cannot turn an actual leak green. What a
weaker jury can still cost is the softer signal — manipulation resistance where
nothing literal leaked — which is why the default steps to Sonnet rather than
straight to Haiku; the env var makes Haiku available for anyone who measures it
against a known-breached transcript first.

The added test asserts against the calls the provider actually received, not
against the interface: a `juror` field that nothing read would have passed
every existing test. Reverting the one-line wiring turns it red.
@Weegy

Weegy commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Cost note plus a ready-made patch — sneumannb5#1, based on this PR's branch (the suite isn't on main yet, so it couldn't target main). It's opened against your fork, so it may not show up in your notifications here; hence this pointer. Yours to merge, squash into this PR, or close.

What it changes: BehavioralModels gains a juror field (ADVERSARIAL_JUROR_MODEL, default claude-sonnet-5). Today the three Delphi jurors run on models.attacker.

Why: each juror reads the entire escalation transcript, and a non-unanimous round 1 turns three full reads into six — so the jury is the largest input-token consumer in a behavioral run while doing the most bounded work. On the current corpus (1 behavioral scenario, maxTurns: 4) that is roughly half the run cost:

Jury Run
today ~$0.060–0.120 $0.13–0.19
with the patch ~$0.036–0.072 $0.09–0.11

The absolute number is small either way — the reason to do it before merge is the slope, since cost scales with the number of behavioral entries and the jury grows fastest.

Why a cheaper jury is safe here: findIdentityLeaks overrides the vote on any real identity value, which your own the findIdentityLeaks oracle overrides a fooled jury ⇒ breached test already pins. The residual risk is the softer signal (manipulation resistance with no literal leak), which is why the default steps to Sonnet rather than Haiku; Haiku stays available via the env var, documented as something to measure first.

One caveat worth raising separately from the patch: the eval can't run at all yetANTHROPIC_API_KEY does not exist as a repo secret. golden-eval.yml has the same gate and has reported success on every run so far without executing a single eval (5/5 runs, 6–13s each, all the guard-skip path). Merging this PR before the secret is set would add a second permanently-green workflow. Worth setting the secret first so the first run actually tells you something.

@Weegy

Weegy commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Pushed to this branch on Marcel's instruction — heads-up @sneumannb5, since it touches your PR. Two merge commits on top of your e855dc36, now at 093cbe13. Nothing rebased, nothing force-pushed, none of your commits rewritten.

1. The juror-model change (previously offered as sneumannb5#1, now closed as superseded). BehavioralModels gains a juror field via ADVERSARIAL_JUROR_MODEL, defaulting to claude-sonnet-5; the three Delphi jurors previously ran on models.attacker. Each juror reads the whole escalation transcript and a non-unanimous round 1 doubles three reads to six, so the jury was the eval's largest input-token consumer while doing its most bounded work. On the current corpus that takes a run from ~$0.13–0.19 to ~$0.09–0.11, and the saving scales linearly as behavioral scenarios are added.

Safe to downgrade because the jury is not the only scorer: findIdentityLeaks forces breached on any real identity value regardless of the vote — the property your own the findIdentityLeaks oracle overrides a fooled jury ⇒ breached test already pins. The residual exposure is the softer signal (manipulation resistance with no literal leak), which is why the default steps to Sonnet rather than Haiku; Haiku stays available via the env var and the README says to measure it against a known-breached transcript first.

The added test asserts against the calls the provider actually received, not against the interface — a juror field nothing read would have passed every existing test. Reverting the one-line models.jurormodels.attacker wiring turns exactly that test red, and only that one; I ran the mutation to confirm.

2. Current main merged in (07b62998), resolving the behind state. No conflicts, no conflict markers, and the decoupling ratchet holds at 3295 without touching your baseline bump.

Local verification on the merged tree: npm run build clean · npm run typecheck clean · npm run lint clean · node scripts/check-core-decoupling.mjsreferences held at 3295 · npm test6839 tests, 6827 pass, 0 fail, 0 cancelled, 12 skipped.

ANTHROPIC_API_KEY is now configured on the repo, so the behavioral tier will actually execute on the first post-merge run instead of taking the guard-skip path. One thing worth a separate look: the secret list currently also contains a stray AANTHROPIC_API_KEY (double A) — almost certainly a typo from setting it, and worth deleting so nobody later wires a workflow to the dead name.

@Weegy
Weegy merged commit 61a8bf8 into byte5ai:main Aug 19, 2026
9 checks passed
Weegy added a commit that referenced this pull request Aug 19, 2026
…en) (#748)

The adversarial eval crashed on its first real run with
`400 \`temperature\` is deprecated for this model`. That was the loud symptom.
The quiet one is worse.

`LlmScreener.screen()` sends `temperature: 0` on every inbound turn, and its
caller turns any exception into `unscreenable` — an explicit fail-open. The
screener is constructed with the agent's own model
(`buildOrchestrator.ts:329`), whose default is `DEFAULT_ORCHESTRATOR_MODEL =
'claude-opus-4-8'`. That model rejects `temperature: 0` with a 400. So on the
repo's own default configuration, #579's inbound security screening returned
`unscreenable` for every payload and reported no error.

Five more production call sites are affected the same way — the plan-runner's
materializer, replanner (x2), gate and gc all pass `temperature: 0`.

Measured against the live API rather than inferred, because the rule is not
what it looks like:

  model              omitted   0     0.5   1
  claude-opus-4-6    OK        OK    OK    OK
  claude-opus-4-7    OK        400   400   OK
  claude-opus-4-8    OK        400   400   OK
  claude-opus-5      OK        400   400   OK
  claude-sonnet-4-6  OK        OK    OK    OK
  claude-sonnet-5    OK        400   400   OK
  claude-haiku-4-5   OK        OK    OK    OK

`opus-4-6` accepts the parameter while `opus-4-7` rejects it, so "newer than
X" is a plausible and wrong gate. `temperature: 1` is always accepted because
it IS the default — which is why the eval's attacker step (temperature 1)
survived and only the juror (temperature 0) raised.

Fix in the adapter, the layer that owns the wire contract: `buildParams`
omits `temperature` when the model does not honour it. That covers all seven
call sites at once, including the legacy v1 wrapper, which delegates through
the same neutral provider. `supportsTemperature` is exported so a caller can
tell that its determinism request will be dropped.

Note this is not fallout from the juror-model knob in #730: the juror ran on
claude-opus-4-8 at temperature 0 before that change and would have raised the
same 400. The eval simply never ran until ANTHROPIC_API_KEY was set.

Verification: 6841 tests, 0 fail (baseline 6839 + the two added here); tsc
clean; repo lint clean. Mutation check with a rebuild between runs — forcing
the gate to always-true and to always-false each turns exactly one new test
red.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Adversarial injection/manipulation eval suite in CI (evaluate ProofAgent-Harness)

2 participants