feat(corpus-seeded-mechanisms): variant findings in MAP, sandbox-eligible, counted in the manifest

Task 2.3 (Req 3.4, 3.5, 3.6, 6.2).

- [variants] enabled / max_mechanisms config; MAP runs variant search
  after the overlay in standard and deep scans only, appends the variant
  findings, rewrites overlay.json with merged ids, and the manifest
  records mechanisms_searched, files_searched, findings and
  merged_into_overlay.
- REGRESS: variant findings join the candidate list after the promotions
  as zero-score hotspots, under the existing safety check and cap.
- Spec notes for group 2 and the design synced (variant_search.py,
  seed.py, derive_shape signature).

Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
This commit is contained in:
Marius Ciepluch
2026-09-05 14:40:15 +02:00
co-authored by Claude Fable 5.1
parent c1621777f9
commit a767ba80b4
6 changed files with 257 additions and 7 deletions
@@ -93,6 +93,8 @@ src/openultrasast/
├── semantic/
│ ├── mechanisms.py # Mechanism += origin, review_tier, pairs, shape, guard; append_from_pair; dedupe_by_shape
│ ├── variants.py # Shape, GuardKind, derive_shape(vuln_ir, fixed_ir, label, facts) -> Shape|None; match_shapes(file_ir, shapes, facts) -> list[VariantHit]
│ ├── variant_search.py # search_tree, hits_to_findings, SearchResult (needs store/overlay/findings; variants.py stays free of them)
│ ├── seed.py # export_mechanisms(cases, store) -> ExportReport (maintainer command `mechanisms export`)
│ └── loo.py # evaluate_loo(cases, *, store_factory) -> LooResult; payload
├── findings.py # origin "variant" via tags/finding_id prefix "variant:" (no schema change)
├── cli.py # MAP: variant search after overlay; manifest counters; `pairs --loo`; `mechanisms export --slice`
@@ -173,8 +175,11 @@ class Shape:
def key(self) -> str: ... # stable, text-free
def derive_shape(vuln: FileIR, fixed: FileIR, *, function: str, sink: str | None, line: int | None, mechanism: str, facts: SemanticFacts) -> Shape | None: ...
def match_shapes(ir: FileIR, shapes: Sequence[Shape], facts: SemanticFacts) -> list[VariantHit]: ...
def derive_shape(vuln: FileIR, fixed: FileIR, *, function: str, sink: str | None, line: int | None, mechanism: str, facts: SemanticFacts, vuln_text: str = "", fixed_text: str = "", cwe: str | None = None) -> Shape | None: ...
# texts: guard statements are neither calls nor binds in FileIR, so the guard kind is classified from the labeled
# function's lines present only on the fixed side (closed set, none of the text enters the shape); cwe: when a label
# names no sink and no line, the fact-sink call in the function whose CWE matches is the labeled call.
def match_shapes(ir: FileIR, shapes: Sequence[Shape], facts: SemanticFacts, *, mechanism_ids: Mapping[str, str]) -> list[VariantHit]: ...
```
Invariants: shapes carry no path, line, or literal beyond identifiers; `derive_shape` returns None when the labeled sink call site is not found.