Summary
--exploitable-only and --exploitable-all filter units by security_classification. Single-shot
enhance never writes that field — its result dict is a fixed six-key literal that does not include it,
and the single-shot prompt never asks for it. So after a single-shot enhance those flags select
zero units, always.
analyzer.py states the opposite contract in its own error message: "single-shot mode must populate
llm_context.security_classification". Nothing does.
The producer cannot emit the field
libs/openant-core/utilities/context_enhancer.py:349-356 — the whole of what single-shot writes:
unit["llm_context"] = {
"missing_dependencies": analysis.get("missing_dependencies", []),
"additional_callers": analysis.get("additional_callers", []),
"data_flow": analysis.get("data_flow", {}),
"imports": analysis.get("imports", []),
"reasoning": analysis.get("reasoning", ""),
"confidence": analysis.get("confidence", 0.5)
}
This is a whitelist, not a passthrough. Even a model that volunteers security_classification has it
dropped here. The single-shot prompt does not request it either — grepping the prompt body for
security_classification returns nothing.
Executed at b5019628
Real context_enhancer and real analyzer, with the LLM call stubbed (no API call, no spend). The
stub deliberately volunteered "security_classification": "exploitable" in its JSON response:
llm_context keys: ['additional_callers', 'confidence', 'data_flow', 'imports',
'missing_dependencies', 'reasoning']
classification: None
kept by --exploitable-all: 0
The volunteered value is discarded by the six-key literal, and the filter keeps nothing.
The consumer is fine; only the producer is missing
core/analyzer.py:59-74 already reads both shapes — it was written to fix the mirror-image bug, and
its docstring says so:
single-shot enhance writes unit['llm_context'] and historically had no classification at all
The author believed the producer had since been fixed. It has not. The reader is correct and reads a
key nobody writes.
The regression test is green because its input is hand-built
libs/openant-core/tests/test_enhance_resilience.py:283-285:
# single-shot unit: classification lives under llm_context
u = {"id": "x", "llm_context": {"security_classification": "exploitable"}}
assert _unit_security_classification(u) == "exploitable"
That unit shape is constructed in the test, not produced by enhance_unit. The test exercises the
reader against an input the producer cannot generate, so it passes while the path is broken. Any
fixture built from the real producer would fail it.
Operator-visible symptom
core/enhancer.py:154,167:
context_key = "agent_context" if mode == "agentic" else "llm_context"
cls = ctx.get("security_classification", "unknown")
Every single-shot run therefore prints [Enhance] Classifications: {'unknown': N}. The stderr warning
in analyzer.py fires only when classified == 0 — which in single-shot is always — so it is a
permanent warning rather than an exception report, and it does not stop the run from analysing zero
units.
Second consequence: --limit loses its ordering
analyzer.py:98-119 (_apply_limit) prioritises by the same field. With every unit unclassified the
priority key is constant, so --limit N degrades to a head-slice in whatever order units arrive —
the behaviour the function exists to avoid.
Not documented as a reduced-capability mode
Single-shot is offered as a first-class fast/cheap option (cli.py:1413-1415,
apps/openant-cli/cmd/enhance.go:43), not as degraded. The one line that points the other way,
parsers/go/test_pipeline.py:977 ("Requires agentic mode to have classified units"), describes a
different filter inside a per-parser harness that reads agent_context only — and it is contradicted
by analyzer.py:546-548, which asserts the single-shot contract for the CLI path.
Suggested fix
- Add
security_classification to the single-shot result dict at context_enhancer.py:349-356, and
ask for it in the single-shot prompt, so the field the reader at analyzer.py:59-74 already
handles is actually produced.
- Alternatively, if single-shot is meant to be classification-free, make that explicit: have
--exploitable-only/--exploitable-all fail fast with a message naming the mode, rather than
silently selecting zero units, and drop the contract sentence at analyzer.py:546-548.
- Rebuild the
test_enhance_resilience.py:283-285 fixture from enhance_unit's real output rather
than a hand-written dict, so the test can detect this.
Direction: today the filter is a total false negative on single-shot datasets — it analyses zero
units where it should analyse some. Fixing it increases the number of units examined, which is the
safe direction for a scanner.
What I am not claiming
- I am not claiming this affects agentic mode. Agentic writes
agent_context with the field
present, and the filter works there.
- I am not claiming a specific vulnerability was missed in a real engagement. The evidence is the
executed producer/filter pair above, not an observed incident.
- I have not measured how many users run single-shot with
--exploitable-only.
Grounding curve (added 2026-08-22)
| commit |
producer (6 keys) |
prompt omits it |
reader |
_apply_limit |
contract sentence |
test |
verdict |
0d729f6 (initial) |
true |
true |
no filter |
absent |
absent |
absent |
FAILS |
d710b90 (PR #23) |
true |
true |
reads agent_context only |
absent |
absent |
absent |
PARTIAL |
ad17f3f (PR #133) |
true |
true |
true |
true |
true |
true |
FULL |
b5019628 (HEAD) |
true |
true |
true |
true |
true |
true |
FULL |
Attribution, and this is the part worth a maintainer's attention. The producer legs are
original code — enhance_unit's six-key dict is ^0d729f6, unchanged for the life of the repo.
Everything else was introduced by merged PR #133 (ad17f3f): it fixed the reader to be
mode-agnostic (_unit_security_classification, analyzer.py:59-74), added _apply_limit's
classification-based prioritisation, wrote the "single-shot mode must populate
llm_context.security_classification" contract sentence, and added the regression test that passes
on a hand-built unit — without ever checking that enhance_unit could produce that shape.
So between PR #23 and PR #133 the filter also matched zero single-shot units, but for the
mirror-image reason this issue sets aside (the reader consulted agent_context alone). PR #133 is
what turned a reader bug into a producer bug, and is where the fix and the test rebuild belong.
Summary
--exploitable-onlyand--exploitable-allfilter units bysecurity_classification. Single-shotenhance never writes that field — its result dict is a fixed six-key literal that does not include it,
and the single-shot prompt never asks for it. So after a single-shot enhance those flags select
zero units, always.
analyzer.pystates the opposite contract in its own error message: "single-shot mode must populatellm_context.security_classification". Nothing does.The producer cannot emit the field
libs/openant-core/utilities/context_enhancer.py:349-356— the whole of what single-shot writes:This is a whitelist, not a passthrough. Even a model that volunteers
security_classificationhas itdropped here. The single-shot prompt does not request it either — grepping the prompt body for
security_classificationreturns nothing.Executed at
b5019628Real
context_enhancerand realanalyzer, with the LLM call stubbed (no API call, no spend). Thestub deliberately volunteered
"security_classification": "exploitable"in its JSON response:The volunteered value is discarded by the six-key literal, and the filter keeps nothing.
The consumer is fine; only the producer is missing
core/analyzer.py:59-74already reads both shapes — it was written to fix the mirror-image bug, andits docstring says so:
The author believed the producer had since been fixed. It has not. The reader is correct and reads a
key nobody writes.
The regression test is green because its input is hand-built
libs/openant-core/tests/test_enhance_resilience.py:283-285:That unit shape is constructed in the test, not produced by
enhance_unit. The test exercises thereader against an input the producer cannot generate, so it passes while the path is broken. Any
fixture built from the real producer would fail it.
Operator-visible symptom
core/enhancer.py:154,167:Every single-shot run therefore prints
[Enhance] Classifications: {'unknown': N}. The stderr warningin
analyzer.pyfires only whenclassified == 0— which in single-shot is always — so it is apermanent warning rather than an exception report, and it does not stop the run from analysing zero
units.
Second consequence:
--limitloses its orderinganalyzer.py:98-119(_apply_limit) prioritises by the same field. With every unit unclassified thepriority key is constant, so
--limit Ndegrades to a head-slice in whatever order units arrive —the behaviour the function exists to avoid.
Not documented as a reduced-capability mode
Single-shot is offered as a first-class fast/cheap option (
cli.py:1413-1415,apps/openant-cli/cmd/enhance.go:43), not as degraded. The one line that points the other way,parsers/go/test_pipeline.py:977("Requires agentic mode to have classified units"), describes adifferent filter inside a per-parser harness that reads
agent_contextonly — and it is contradictedby
analyzer.py:546-548, which asserts the single-shot contract for the CLI path.Suggested fix
security_classificationto the single-shot result dict atcontext_enhancer.py:349-356, andask for it in the single-shot prompt, so the field the reader at
analyzer.py:59-74alreadyhandles is actually produced.
--exploitable-only/--exploitable-allfail fast with a message naming the mode, rather thansilently selecting zero units, and drop the contract sentence at
analyzer.py:546-548.test_enhance_resilience.py:283-285fixture fromenhance_unit's real output ratherthan a hand-written dict, so the test can detect this.
Direction: today the filter is a total false negative on single-shot datasets — it analyses zero
units where it should analyse some. Fixing it increases the number of units examined, which is the
safe direction for a scanner.
What I am not claiming
agent_contextwith the fieldpresent, and the filter works there.
executed producer/filter pair above, not an observed incident.
--exploitable-only.Grounding curve (added 2026-08-22)
_apply_limit0d729f6(initial)d710b90(PR #23)agent_contextonlyad17f3f(PR #133)b5019628(HEAD)Attribution, and this is the part worth a maintainer's attention. The producer legs are
original code —
enhance_unit's six-key dict is^0d729f6, unchanged for the life of the repo.Everything else was introduced by merged PR #133 (
ad17f3f): it fixed the reader to bemode-agnostic (
_unit_security_classification,analyzer.py:59-74), added_apply_limit'sclassification-based prioritisation, wrote the "single-shot mode must populate
llm_context.security_classification" contract sentence, and added the regression test that passeson a hand-built unit — without ever checking that
enhance_unitcould produce that shape.So between PR #23 and PR #133 the filter also matched zero single-shot units, but for the
mirror-image reason this issue sets aside (the reader consulted
agent_contextalone). PR #133 iswhat turned a reader bug into a producer bug, and is where the fix and the test rebuild belong.