A4: roadmap-2026-09-04.md moves to .kiro/archive/steering/ so it no longer loads as steering (roadmap.md and safety-net.md replaced it); roadmap.md's pointer follows it. overview.md's spec table is recounted from tasks.md and spec.json (adds ai-service-plane, harnessx-removal, learned-decision-engine, pre-push-safety-net; drops the stale "active one"), and the module line now cites the regenerated audit: 150 modules, 145 load-bearing, 5 standalone, 0 orphaned; 1,964 tests collected. A5: a dated status note at the top of the requirements (or brief) of 16 specs: retired, superseded, dormant or complete, and for model-grounded-detection the removal of judge/calibrate/execution (closing the Req 10 execution-tier option). spec.json phase corrected to implementation-complete where every task is checked (model-grounded- detection, propose-adjudicate-prove, tree-sitter-overlay-extra, corpus-seeded-mechanisms, pair-corpus-honesty); openrouter-sast-harness gets a spec.json with status retired. Nothing deleted, no spec moved. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
12 KiB
Requirements Document
Status 2026-10-02: superseded by
model-grounded-detection(spec.jsonsuperseded_by). 4 of 20 tasks were done and the tasks were never approved; none will be. Kept as decision history; nothing in src, tests or CI points here.
Introduction
The detector's instability is structural, not statistical. Measured on 2026-09-06 over twelve injection pairs at
temperature 0 with thinking disabled, 120 runs produced 120 distinct tool-call trajectories — not one repeat —
and outcome disagreement tracks trajectory divergence (30.9% at low similarity against 4.2% at mid). The model
spends its budget searching: 555 grep_repo calls against 71 entry_points, guessing where sinks are while the
semantic IR already knows. So learning-harness's K runs are an ensemble of distinct search procedures rather than
repeated measurements, and the noise floor it derives a regression budget from measures how often an unconstrained
search happens to reach the sink.
This feature removes the freedom instead of averaging over it. A deterministic pass enumerates the candidate sites inside the labeled region from the IR; the model is asked one bounded, typed question per candidate with no tools and no choice of what to look at; and the predicates the scorer already applies after the fact are enforced at generation time with a bounded retry. The candidate generator was measured before anything was built: the pattern ruleset caps recall at 12.1%, below the 64.9% the unconstrained loop already reaches, while the semantic IR caps it at 96.6% with a median of ten sites per labeled function.
Baseline to beat, deepseek-v4-flash, K = 5, 57 scorable web pairs: detection 64.9%, leak 12.3%, pair-correct
52.6% — and 83.3% detection on hand-written vibe-py against 44.4% on agent-written agent-vfc, which is the gap
this feature must close rather than widen. Context and measurements: brief.md,
benchmarks/measurements/2026-09-06-trajectory-divergence.json, 2026-09-06-candidate-ceiling.json.
Boundary Context
- In scope: candidate enumeration from the semantic IR, per family shape; the typed, toolless judgment step; constraint predicates enforced at generation time with a bounded retry; the evidence that decides whether the K-run and noise-floor machinery still earns its place; per-slice reporting that keeps real-world code in front.
- Out of scope: the family taxonomy, the classifier, the pair scorer's outcome rules, the split, the verifier boundary and write roots, redaction, the three gates, and the publication machinery — all of which stand and are what made this feature's measurements possible. Also out: prompt optimisation, reflective evolution and any optimiser (deferred to a later spec, on this feature's evidence); finding bugs in JavaScript interpreters, WebGL renderers or browser engines; memory-safety fuzzing for C and C++; fine-tuning model weights; adopting DSPy as a dependency.
- Adjacent expectations:
learning-harnesskeeps owning the taxonomy, the scorer, the split and publication; its Req 3.5 (K runs), 8.2 (noise floor) and 9.5 (acceptance) are amended by this feature only on evidence it produces, never on argument. The pair corpus keeps owning labels, tiers and splits. The unconstrained tool hunter stays as the generalist and as the baseline this feature is measured against.
Requirements
Requirement 1: Candidates come from the semantic IR, and the enumerator's ceiling is published
Objective: As a maintainer, I want the sites a detector judges to be enumerated deterministically, so that the model stops searching and the limit of what it can possibly find is a number rather than a hope.
Acceptance Criteria
- The Constrained Detector shall enumerate candidate sites for a region from the semantic IR — call sites and bindings — and shall not use the pattern ruleset as its candidate generator.
- The Constrained Detector shall shape candidates by family: a call site for injection, path, deserialization, untrusted destination and prototype; an operation on a protected resource with the guards in scope for access control; a configuration assignment or literal for permissive configuration and secrets.
- The Constrained Detector shall publish, per family and per slice, the fraction of labeled functions for which at least one candidate was enumerated, and shall name that fraction as the ceiling on its recall.
- If the semantic IR cannot parse a side, then the Constrained Detector shall record
unsupported_languageand shall score nothing for that pair rather than falling back to a search. - If a labeled function yields no candidate site, then the Constrained Detector shall record
no_candidateand report that pair as unscorable with that reason rather than as a miss. - While enumerating, the Constrained Detector shall bound the candidates handed to one judgment call and shall record the bound and the number discarded when a region exceeds it.
Requirement 2: One bounded, typed question per candidate, with no search
Objective: As a maintainer, I want the model to answer only what a model is needed for, so that two runs of the same detector on the same input take the same path.
Acceptance Criteria
- The Constrained Detector shall call the model with the candidate site, its enclosing function, the binding chain the IR resolved for its arguments, and the guards in scope, and shall offer it no tools.
- The Constrained Detector shall require a typed answer per candidate: a verdict, the expression it believes carries untrusted input, the guard it found or the statement that there is none, and the family.
- The Constrained Detector shall derive every finding from a typed answer and shall never parse a finding out of free prose.
- When the detector is run twice on the same input with the same configuration, the sequence of model calls shall be identical, and a test shall assert this.
- The Constrained Detector shall record, for every claim, the candidate that produced it, so that a claim is traceable to the site it was asked about.
Requirement 3: Real-world code decides, and the gap to teaching code is published
Objective: As a maintainer, I want changes judged on code from real projects rather than on hand-written teaching examples, so that the tool is not tuned for cases nobody has.
Acceptance Criteria
- The Constrained Detector shall report every number per slice and shall never publish an aggregate across slices without the per-slice numbers beside it.
- The Constrained Detector shall designate the slices harvested from real projects — agent-vfc, vfc-js, vfc and github — as the deciding slices, and the hand-written vibe-py slice as a development slice that cannot on its own justify accepting a change.
- If a change improves the development slice and regresses any deciding slice, then the Constrained Detector shall reject it, and the rejection shall name the slice that regressed.
- The Constrained Detector shall publish the overfitting gap — development-slice detection minus deciding-slice detection — beside every headline number, and shall state that the baseline gap is 83.3% against 44.4%.
- The Constrained Detector shall measure and publish the candidate ceiling separately for the real-world slices, because the functions there are larger than the teaching corpus's and the enumerator's behaviour on them is not implied by its behaviour on vibe-py.
- While the deciding slices carry no pair a learning path may teach from, the Constrained Detector shall state that with every number it publishes rather than presenting the development slice as if it were the corpus.
- The Constrained Detector shall keep memory safety measured and non-gating, and shall not let the 176 vfc rows dominate an aggregate that a reader would take as the tool's web-taxonomy performance.
Requirement 4: The predicates that score a finding also constrain it
Objective: As a maintainer, I want the checks that decide whether a finding counts applied while it is being made, so that a violation is corrected rather than silently discarded.
Acceptance Criteria
- The Constrained Detector shall express as predicates the checks the scorer already applies: the reported line falls inside a parsed function, the named sink occurs at that line, the claimed family is in the taxonomy, and the claimed source expression appears in the enclosing function.
- When a predicate fails, the Constrained Detector shall retry the judgment once with the failed answer and the predicate that rejected it, and shall not retry more than once per candidate.
- If a retry also fails a predicate, then the Constrained Detector shall discard the claim and record the predicate that rejected it, and shall count that discard in a published tally.
- The Constrained Detector shall apply the same predicates at generation time and at scoring time from one definition, so the two cannot drift apart.
Requirement 5: The noise machinery is re-measured, and kept only if it earns its place
Objective: As a maintainer, I want the K runs, the noise floor and the regression budget re-measured against a constrained detector, so that machinery built for an unconstrained one is deleted rather than maintained.
Acceptance Criteria
- The Constrained Detector shall re-run the trajectory measurement against itself and shall publish the resulting distinct-trajectory count per pair.
- The Constrained Detector shall re-measure the per-family noise floor at the same K as the baseline and shall publish it beside the baseline's floor.
- If the re-measured floor is zero for a family, then the Constrained Detector shall report that family's K-run
requirement and its regression budget as unnecessary and shall name the
learning-harnessrequirements that would be amended. - The Constrained Detector shall state, wherever a K-run number is published, whether the runs were repeated measurements or an ensemble of distinct procedures.
Requirement 6: The constrained detector is measured against the unconstrained one, on the same corpus
Objective: As a maintainer, I want one comparison that says whether this was worth building.
Acceptance Criteria
- The Constrained Detector shall be scored by the existing class-aware pair scorer, with the same denominators, the same unscorable vocabulary and the same split, and shall introduce no scoring rule of its own.
- The Constrained Detector shall publish detection, leak and pair-correct rates against the recorded baseline of 64.9%, 12.3% and 52.6%, per slice and in total.
- The Constrained Detector shall publish its cost per correct pair against the baseline's $0.030 on vibe-py and $0.136 on agent-vfc.
- If the constrained detector's detection rate is below the unconstrained loop's on any deciding slice, then that shall be published as the headline of the comparison rather than as a footnote.
- The Constrained Detector shall keep the unconstrained tool hunter available and shall not remove it while it scores higher on any deciding slice.
Requirement 7: Degradation, budget and boundaries
Objective: As a maintainer, I want the same operational contract the rest of the tool has.
Acceptance Criteria
- The Constrained Detector shall run behind the existing chat endpoint resolution and shall record
learning_endpoint_unavailableand score nothing rather than fall back to another provider. - The Constrained Detector shall meter its own spend through the client's meter and shall stop when a configured cap is crossed, recording what it had spent.
- The Constrained Detector shall leave the detection, map and local pair gate outputs byte-identical.
- The Constrained Detector shall keep the core install at zero dependencies, and every optional capability it uses shall degrade to a recorded reason without its extra.
- The Constrained Detector shall pass every prompt and every persisted trajectory through redaction.