Ran the humanizer rules over 79 prose files in the active specs (brief, design, requirements, research and narrative docs). Removed em dashes, significance inflation, forced rule-of-three, bold-label list items and filler. Skipped tasks.md checklists and the retired specs (harnessx-*, openrouter-sast-harness, unseen-repo-evaluation). Every number, path, command, hash and identifier was preserved: checked each file against HEAD for drift in numbers, inline code and code fences, zero drift across all 79 files. Co-Authored-By: Claude Opus 4.8 <[email protected]>
13 KiB
Requirements Document
Status 2026-10-02: complete, 32 of 32 tasks (spec.json phase corrected to
implementation-complete).
Introduction
Engineers evolving OpenUltraSAST's overlay cannot trust the pair scoreboard: coverage detections are dropped, vfc labels mostly say sink = "unknown", a quarter of the vfc corpus fails to parse because excerpts start mid-comment, two sast pairs are unlabelable as vendored, and there is no Node/TypeScript/Python web corpus and no agent-authored corpus. This spec makes the scoreboard honest per slice, per mechanism, and per provenance, adds three web and agent-authored slices, and lets the improve loop accept a change only when no provenance profile regresses. It supersedes real-world-vfc-slice for the corpus and scorer plane.
Boundary Context
- In scope: pair scorer fidelity; catalog label schema (function, mechanism, provenance, known limits, held-out split); harvest anchoring and extraction modes; sast corpus hygiene;
vibe-py,vfc-js,agent-vfcslices; dataset pointers; deterministic provenance fingerprint at scan time; per-profile and per-mechanism dashboard; per-profile acceptance in the improve loop; offline tests. - Out of scope: taint, facts, CST walker, dispositions, or tolerance of parser ERROR nodes (engine specs); BaxBench generation slices; profile-specific fact tables or thresholds; any new merge gate; vendoring files without a stated license; LLM-based classification.
- Adjacent expectations:
localstays the 100% pair gate; the stage-1 smoke gate stays inventory-only;OverlayRecordmay gain one optional additive field; the improve loop's existing accept gate (recall floor, FP ceiling, score, no matched regression) stays and grows one clause; ledger file formats do not change.
Requirements
Requirement 1: Scorer counts every adjudicated detection
Objective: As an engineer evolving adjudication, I want the pair scorer to count what the overlay actually found, so that sink aliases and coverage flows move the number instead of vanishing.
Acceptance Criteria
- When an overlay slice pair is scored, the system shall count a
coveragerecord that carries a source-to-sink flow as a detection on the vulnerable side and as a leak on the fixed side, symmetrically withpromote. - When an expected row names a function, the system shall match a promotion or coverage record only if the record lies inside that function's line range.
- When an expected row names neither a rule id nor a sink nor a function, the system shall not count a CWE-only match as a detection and shall flag the row as
weak_labelin the payload. - When any overlay slice is evaluated, the system shall report inventory and overlay metrics side by side for that slice as well as for
vfc. - When a pair is evaluated, the system shall report how many files on each side were
parse_failedand how many proposals wereunadjudicated, so parser loss is visible on the dashboard. - The system shall keep
localas the only slice whose result can fail CI. - Where a hunter model is configured, the system shall report a third scorer,
hunter, that runs the tool hunter on each isolated pair function and scores it with the same pair rules, so the LLM plane is measured on the same corpus; absence of a model shall skip the scorer with a recorded degradation.
Requirement 2: Labels carry function, mechanism, and known limits
Objective: As a maintainer, I want every pair to say what should fire, where, and by which mechanism, so that recall can be read per mechanism and unachievable pairs do not pollute the achievable number.
Acceptance Criteria
- Each expected row shall carry a
mechanismvalue from a closed, versioned vocabulary stored as data next to the catalogs. - Each pair shall carry a
provenancevalue from the closed sethuman,agent,mixed,synthetic. - When a pair is only achievable with information outside the vendored files, the catalog shall declare
known_limitwith a reason, and the system shall evaluate the pair but exclude it from the achievable metrics and report it separately. - When a catalog is loaded, the system shall reject an expected row whose
sinkisunknownand whosefunctionandrule_idare both absent, and shall reject an unknownmechanismorprovenancevalue. - Each pair shall carry a
splitvalue oftrainorholdout, declared in the catalog and never chosen at run time. - Where a recipe names a function, the generated expected row shall carry that function name.
Requirement 3: Harvest anchors at the declarator
Objective: As a maintainer growing the corpus, I want harvest to extract the function body and nothing before it, in C, JavaScript, TypeScript, and Python, so that excerpts parse and pairs are attributable.
Acceptance Criteria
- When harvest searches for a function by name, the system shall ignore occurrences inside comments and string literals and shall anchor on the declarator line.
- Where a recipe gives a line range instead of a name, the system shall extract exactly that range from the parent and fix blobs.
- Where a recipe gives a fix commit and a file, the system shall extract the function enclosing the first changed hunk on both sides.
- When the language is Python, the system shall extract by indentation, not by braces.
- When harvest is re-run for the curl recipes, the system shall produce excerpts that start at the declarator; the mid-comment excerpts shall no longer exist in the catalog.
- The system shall not fetch during pytest, and extraction shall be unit-tested on local strings.
Requirement 4: sast corpus hygiene
Objective: As an engineer, I want the textbook slice to contain only pairs that are labelable from the vendored files, so that a miss there is an engine miss.
Acceptance Criteria
- The Juliet CWE-121 strcpy pair shall vendor the buffer declarations that differ between
badandgoodG2B, so the two bodies are not byte-identical. - The OWASP Java hash pair shall be declared
known_limitwith reasoncross_artifact, or shall vendor the property file that makes it labelable; either satisfies this criterion. - When the sast slice is evaluated, the payload shall report achievable pairs and known-limit pairs as separate counts.
Requirement 5: Web and agent-authored slices
Objective: As an AppSec engineer, I want labeled Node, TypeScript, and Python web pairs, including agent-authored code, so that the harness is measured on the code operators are writing now.
Acceptance Criteria
- The system shall add a
vibe-pyslice built from Real-Vuln-Benchmark manifests: pinned commit, file, line range, function, primary and acceptable CWEs, and authorship, with false-positive trap entries used as fixed-side twins. - The system shall add a
vfc-jsslice built from SecBench.js sink locations and fix commits, harvesting from the upstream package repositories, and shall not vendor SecBench.js test files. - The system shall add an
agent-vfcslice of security-fix commits carrying an AI agent co-author trailer or generation marker, in JavaScript, TypeScript, or Python, each human reviewed before entering the catalog. - Each vendored pair shall carry repo, commit, parent, commit URL, license, provenance, mechanism, and split; a pair without a stated license shall not be vendored.
- Each new slice shall start with a reviewed set on the order of tens of pairs and shall not require network in CI.
- When the new slices are added,
datasets.tomlshall point at Real-Vuln-Benchmark, SecBench.js, AIDev, BaxBench, SecurityEval, CodeSecEval, CWEval, and CyberSecEval, and shall note that CyberSecEval labels are defined by its own rules and are not recall ground truth.
Requirement 6: Provenance fingerprint at scan time
Objective: As an operator, I want a scan to say whether the tree looks agent-authored, so that profiles can be applied and reported without a model.
Acceptance Criteria
- When a scan preprocesses a tree, the system shall compute a provenance value from deterministic signals only: agent configuration files and directories, generation markers in files, and commit trailers when a git history is readable.
- The system shall record the provenance value and the signals that produced it in the manifest.
- The system shall not call a language model to classify provenance.
- If no signal is found, then the system shall record
humanwith an empty signal list rather than guessing.
Requirement 7: Profile dashboards and per-profile acceptance
Objective: As a maintainer running the improve loop, I want metrics per provenance profile and per mechanism, and an accept gate that refuses a change that helps one profile by hurting another, so that tuning can branch without overfitting.
Acceptance Criteria
- When pairs are evaluated, the system shall report metrics per provenance profile and per mechanism in the machine-readable payload.
- When the improve loop evaluates a candidate change, the system shall compute pair metrics per profile on the
holdoutsplit and shall reject the change if any profile's pair-correct or Youden regresses beyond a configured tolerance. - The system shall keep the existing accept gate clauses unchanged and shall not treat any non-local slice's Youden as a merge condition.
- The system shall support at most four provenance profiles and shall report when a profile has fewer holdout pairs than a configured minimum.
- Where a scan carries a provenance value, reports shall name the profile so an operator can relate dashboard numbers to their tree.
Requirement 8: Offline testability and unchanged gates
Objective: As a maintainer, I want CI to prove the corpus loads and the scorer is honest without network, so that the corpus cannot rot and the gates cannot drift.
Acceptance Criteria
- When the test suite loads the default catalog, it shall observe
vibe-py,vfc-js, andagent-vfcamong the slices and every referenced file on disk. - When the pairs command runs for each new slice with JSON output, it shall exit zero and include per-profile and per-mechanism metrics.
- The stage-1 detection gate, the split-sink map gate, and the local pair gate shall be unchanged by this spec.
- The extra-free suite shall stay green; overlay-dependent assertions shall skip when the semantic extra is absent.
Requirement 9: Review tiers instead of a binary review gate
Objective: As a maintainer, I want every pair to carry how its label was established, so that the corpus can grow past what one person can review while only trusted tiers steer the improve loop.
Acceptance Criteria
- Each pair shall carry a
review_tierfrom the closed setseeded(ground truth reviewed by the upstream benchmark),advisory(a CVE or GHSA with a public fix commit),title(mechanism inferred from a commit message, unreviewed),reviewed(a named reviewer in this repository). - When pairs are evaluated, the system shall report metrics per review tier in the machine-readable payload.
- The improve loop's per-profile holdout clause shall use only
seededandreviewedpairs;advisoryandtitlepairs shall be scored and reported but shall not gate. - When a recipe carries
reviewer = "pending", its tier shall betitleand it shall load into the catalog, replacing the former candidates-only gating; thetitletier is the review queue. - The catalog loader shall reject a pair whose tier is
reviewedbut names no reviewer.
Requirement 10: Pointer slices score public code without vendoring it
Objective: As a maintainer, I want to score pairs whose upstream repository states no license, so that the corpus is not capped by redistribution rights while the repository never redistributes that code.
Acceptance Criteria
- A slice catalog may mark a pair
vendored = false; such a pair carries repo, parent, commit, path, and extraction recipe but no excerpt files in the repository. - When a non-vendored pair is evaluated and the network is allowed, the system shall harvest both sides into a local cache outside the repository tree and score them exactly like a vendored pair.
- When the network is not allowed, non-vendored pairs shall be skipped with a recorded degradation and shall not fail any test or gate.
- The default
pytestrun and every CI gate shall evaluate vendored pairs only; a separate nightly command shall evaluate pointer slices. - Harvested cache files shall never be committed; the cache directory shall be ignored by git.
- The
vibe-pyslice shall gain the LLM-generated Real-Vuln repositories as non-vendoredagentpairs, and theagent-vfcslice shall gain unlicensed agent-authored fix commits as non-vendored pairs, each withreview_tierset.