Maintainer: hard-coded application specifications are problematic; WordPress is not the only PHP application, same for Python and JS. Language-level knowledge stays; framework roles are inferred per repository; existing framework entries become tagged optional priors kept only by A/B evidence; leave-one-framework-out evaluation. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01EmM1j46qgKMh3zqD1QMwEo
9.3 KiB
Requirements Document
Introduction
Detection decisions stop being hand-coded. Every instrument becomes a producer of per-candidate signals, a trained
decision engine turns signals into a calibrated probability per candidate and family, pipeline changes are chosen
by A/B experiments run on the plane, and expected detection on unseen code is extrapolated with stated uncertainty.
Maintainer direction and context: brief.md.
Boundary Context
- In scope: a feature record per candidate built from every instrument and the memory store; a labelled
dataset with provenance; training, calibration and versioning of the decision engine; A/B experiments as plane
Runs; learning curves and leave-one-population-out estimates; the engine's use in
ousast scan,ousast pre-pushand plane Runs; a final one-time check on frozen population v3. - Out of scope: new instruments (they plug in as signal producers later); deep or large models; any change to population v3 or its protocol; publishing results (licences still gate that).
- Adjacent expectations: instruments keep working standalone; the memory store (harnessx-removal Req 6) is the system of record; the plane attributes every experiment's token spend per task.
Requirements
Requirement 1: Instruments emit signals, not verdicts
User Story: As the maintainer, I want every detector's output to become evidence for one decision, so that no single hand-written rule or prompt decides whether code is vulnerable.
Acceptance Criteria
- For each candidate (repository, pin, file, function, family) the system assembles one feature record from every instrument that ran: quick-rule hits (rule ids, counts), engine findings and questions completed, model sink classification, triage, verify pass verdicts and turns, tie-break, repository facts (callers, entry-point distance), coverage, language and family.
- A missing instrument is recorded as missing, never as a negative signal (coverage
nonestays distinct from "ran and found nothing"). - Feature records are stored in the memory store with the instrument versions and image digests that produced them, so any record can be rebuilt.
Requirement 2: Labels come from ground truth with provenance
User Story: As the maintainer, I want the engine to learn only from outcomes we can defend.
Acceptance Criteria
- Positive labels: candidates matching a declared site or fix range on a vulnerable pin (populations, pair corpus). Negative labels: the same functions on fixed pins, benign-control pins, and findings adjudicated false by the maintainer or a judge task with recorded evidence.
- A plane verdict alone (agreed, rejected, disputed) is a feature, never a label.
- Every label records its source (population, pair, adjudication), split, and the date it was created; labels from a reserved or frozen population are unusable for training until that population's evaluation is recorded.
Requirement 3: A trained, calibrated, versioned decision engine
User Story: As the maintainer, I want one engine that decides from the signals, retrained as data grows, so that detection improves without hand edits.
Acceptance Criteria
- The engine is a simple, inspectable model (regularised logistic regression or shallow gradient-boosted trees), trained per family where data allows and pooled otherwise; its output is a calibrated probability.
- Training is reproducible from the memory store and a recorded split: the same inputs give the same model, and every model version records its data snapshot, feature set, hyperparameters and metrics.
- Evaluation uses population-level folds (leave-one-population-out, and pairs grouped by repository): no repository appears in both training and evaluation of a reported number.
- Operating points (block, report, drop) are thresholds chosen on training folds for stated precision targets and reported with the recall they give; the M4 gates (>= 95% precision, >= 90% recall) are the targets, not assumed.
- The engine explains each decision by its top contributing features, stored with the finding.
Requirement 4: Pipeline changes are chosen by A/B experiments
User Story: As the maintainer, I want changes to prompts, models, pass counts, rule sets and features compared by controlled experiments, so that nothing is adopted on one run's number.
Acceptance Criteria
- An experiment is declared before it runs: arms (variants as plane Run configurations), unit of randomisation (candidate, file or repository), metric (detection, precision, cost per candidate), minimum detectable effect, sample size or sequential stopping rule, and budget.
- Arms run on the same units where the design allows (paired), with assignment recorded; results and spend go to the memory store per arm.
- A variant is adopted only when the pre-registered test shows a difference with its confidence interval excluding zero, or declared equivalent within a margin for cost-only changes; otherwise the result is recorded as inconclusive.
- The first experiment is the known open question: the known-callers prompt line (17 vs 19 sites flagged in any pass), on the development corpus.
Requirement 5: Detection is extrapolated, with uncertainty
User Story: As the maintainer, I want to know how well the system will detect on code nobody has seen, before and without tuning on it.
Acceptance Criteria
- Learning curves: engine metrics as a function of training data size and number of populations, per family, with confidence bands.
- Expected recall and precision on unseen code are reported per family and language as intervals from the population-level folds; a family with too few labels is reported as "insufficient data", never as a number.
- The extrapolation is checked exactly once against frozen population v3 under protocol v3, after the engine and its operating points are fixed; the comparison of predicted and measured intervals is recorded.
Requirement 6: The engine is what users get
User Story: As a user scanning a fresh codebase, I want the decision engine's judgement, not a raw list of rule hits.
Acceptance Criteria
ousast scan,ousast pre-pushand plane Runs report findings with the engine's probability, the operating point they passed and the top contributing features.- Without a trained model available the tools fall back to today's behaviour and say so in the report.
- Hand-written rules remain as signal producers; changing their enabled/shadow status no longer changes what is reported unless the engine's decision changes.
Requirement 7: Built for unseen repositories -- a repo safety net
User Story: As the maintainer, I want the engine judged by how it behaves on repositories it never saw, because "the tool will never run against the test cases it's being trained with. it's purpose is a repo safety net." (maintainer, 2026-09-30).
Acceptance Criteria
- Every reported metric comes from folds grouped by repository across all corpora: a repository in an evaluation fold contributes no row to training.
- Features are repository-agnostic by an allow-listed schema (no repository, path, file, function or commit identities), enforced by a test.
- Two operating points serve the safety net: BLOCK, chosen for >= 95% precision on unseen repositories, and
ADVISORY, trading precision for recall; both reported with out-of-repository intervals. The decision unit is a
candidate in changed code (
pre-pushdelta), with whole-repository scans as a secondary mode. - Calibration is checked on held-out repositories; where it does not hold, only ADVISORY is offered for that family.
- Any adaptation to a user's own repository (facts reuse, their dismissals) is opt-in, separate from the generalisation model, and never contributes to its reported numbers.
Requirement 8: No framework or application knowledge as a prerequisite
User Story: As the maintainer, I want the safety net to work on any application in a language, because "if there is hardcoded application specification needed to build trees this is problematic. wordpress isn't the only php application. same for python, js etc" (maintainer, 2026-09-30).
Acceptance Criteria
- Hand-written knowledge is limited to the language level (built-ins, superglobals, process execution, raw database drivers, deserialisers). Roles specific to a framework or application (its request objects, query builders, hooks, sanitisers, guards) are inferred per repository at scan time from its own source and its dependencies, not required from a hand-written table.
- Existing framework-specific entries (WordPress, Django, Flask, Express, Spring and others in
ruleset/semantic/andruleset/<lang>/) are tagged with their framework and enter the engine only as optional prior features; a prior is kept only if an A/B experiment on held-out repositories shows it helps, and the engine must still work with every prior switched off. - Evaluation is stratified by framework and includes leave-one-framework-out folds (for example: train without any WordPress repository, evaluate on WordPress), so a number never depends on having seen the target's framework; results are reported per framework and for "framework unseen in training".
- The share of each reported result that depends on framework priors is stated (with priors vs without).