Fills the gap the ledger has been carrying since 2026-07-05:
docs/ROADMAP.md row "Red-team (OWASP LLM01-LLM10)" declared
"planned — no Promptfoo config exists." promptfooconfig.yaml is
that config. It is the automated, repeatable counterpart to the manual, dated exercise in
docs/audits/red-team-2026-06-22.md — that
report stays as the qualitative, multi-turn record; this config is what a machine can re-run
on demand.
- Targets the running chat server's
POST /api/chat(same request path the shipped UI andsprout askuse), once in English and once in Spanish, so a probe has to defeat the real pipeline (retrieval -> generation -> citation guard -> safety filter), not a mocked shortcut. plugins: [owasp:llm, ...]expands to Promptfoo's mapping of concrete attack plugins to each of LLM01-LLM10:2025 (LLM03 Supply Chain has no runtime-probeable surface — it's covered bymake securityinstead; see the config's comments anddocs/audits/red-team-2026-06-22.md§7).- Adds
hallucination,overreliance, andharmful:specialized-adviceon top of the OWASP preset because the manual report flagged "just tell me it's safe" pressure and confabulation-under-coercion (§4, §5) as the highest-stakes classes for this domain. - Adds the
multilingualstrategy because the manual report's stated watch item is that the EN/ES guards are maintained by hand (§6) — this gives it an automated adversarial check instead of relying only on the parity eval suite's fixed cases.
uv run sprout ingest
uv run sprout serve & # starts the API on :8000 (Ctrl+C / kill %1 when done)
cd eval/redteam
export ANTHROPIC_API_KEY=... # generation + grading model; see promptfooconfig.yaml
npx --yes promptfoo@latest redteam run
npx --yes promptfoo@latest redteam report # opens the HTML report in a browserredteam run generates adversarial probes tailored to the purpose description and then
evaluates them against both targets. A finding is: a certifying "safe"/"non-toxic" sentence
rendered in either language, an ungrounded (non-cited) factual claim, or a toxicity-adjacent
answer missing the vet/poison-control routing notice — see purpose in the config for the
full definition Promptfoo's grader is instructed to apply.
Generation and grading call an external Anthropic model and need ANTHROPIC_API_KEY — a
live network dependency and a real (small) per-run cost. That is deliberately opt-in here,
consistent with config/sprout.yaml's generation.provider: deterministic default (no
network, no key) and the reasoning already recorded for Garak in docs/ROADMAP.md ("the
offline deterministic default has no LLM to scan"). The default build has no LLM in the
generation seat, but the HTTP target still exercises the full answer pipeline regardless of
which generator backs it, so this red team is meaningful today, not only once the
Bedrock/Anthropic seam is turned on.
The advisory redteam job in .github/workflows/ci.yml runs this on workflow_dispatch
and is skipped (not failed) when secrets.ANTHROPIC_API_KEY isn't configured on the repo —
mirroring the pa11y job's continue-on-error pattern. It is intentionally excluded from
ci-gate's needs: list: wiring it in as a required, blocking check is a follow-up once
the key is provisioned as a repo secret and a maintainer has watched it run clean at least
once (that provisioning step is a one-time action only a repo admin can take, tracked in
docs/ROADMAP.md rather than done silently here).
- New attack classes: add plugin ids to
redteam.plugins(seenpx promptfoo@latest redteam pluginsfor the full catalog). - New languages: Sprout's own
languages.supportedinconfig/sprout.yamlis the source of truth; add a matchingtargetsentry (language: <code>) and extendredteam.language. - Findings that turn out to be real gaps: add the newly-observed attack phrasing as a
regression case in
src/sprout/eval/suites/safety.pyorrefusal.py(the deterministic, merge-blocking suites) — Promptfoo red-teaming finds the gap, the eval suite is what holds the line on every subsequent PR, same division of labor the manual report already documents (§4 "Action" note).