| Where it runs | a stage inside `Stage.MAP` | it needs entry points, which MAP has; it mirrors the obligations checker exactly |
| CPG lifetime | **one build per scan**, reused across regions | a build is 5–30s on an excerpt and the dominant cost at repo scale; per-region builds would make this unusable |
| Region source | entry points, file fallback | Req 2; a contributor has no labelled function |
| Family routing | by region shape and language, from the specs that exist | Req 2.3; asking every family of every region is the unbounded version |
| Budget | one total per scan, spent highest-rank-first | Req 3.2; the enumerator's per-region cap of 8 is meaningless across thousands of regions |
| Ordering | rung first, then rank | Req 3.1; what the model established outranks what the LLM proposed |
| Merge | additive | Req 1.2; removing an existing finding is a product change nobody asked for |
## File Structure Plan
### New
```
src/openultrasast/model/
├── regions.py # EntryPointRecord|FileTarget -> Region + the families that region admits
└── scan.py # repo driver: rank regions, hold the budget, drive pipeline, collect findings
benchmarks/repos/
├── README.md # what a pinned checkout is for and how a baseline is regenerated
└── <repo>.toml # pinned url + commit + the known CVE and where it lives
```
### Changed
-`cli.py` — a `model` stage inside MAP, mirroring `_check_obligations`; its payload into the manifest and a
report section.
-`model/report.py` — a per-rung summary block for a scan (it currently reports per-slice for corpus runs).
-`config.py` — a `ModelConfig`-style block: enabled, budget, max regions. Zero-dep, defaults conservative.
### Unchanged and load-bearing
`model/pipeline`, `taint`, `dominance`, `config_value`, `specs`, `candidates`, `ladder`; `mapping`; the three
gates; `redaction`.
## Components and Interfaces
### model/regions.py
```python
@dataclass(frozen=True)
classScanRegion:
"""Where to look, and what to look for there."""
path:str
function:str|None# None for a file-level fallback region
families:tuple[str,...]# only the families this region's shape and language admit
rank:float# from the entry point's access level and trust boundary
| No LLM endpoint | entailment still reported; suspicion band unasked | `learning_endpoint_unavailable` |
## Requirements Traceability
| Criteria | Delivered by |
|---|---|
| 1.1, 1.2 | the `model` stage in MAP; findings merged additively |
| 1.3 | capability probe; stage skipped with `cpg_unavailable` |
| 1.4 | the committed gate baseline, re-run before and after |
| 1.5 | `ModelScanResult.by_rung` into the manifest and report |
| 2.1, 2.2 | `regions_for` over `entry_points`, file fallback recorded in `ScanRegion.source` |
| 2.3 | `ScanRegion.families` from region shape and language |
| 2.4 | no `function` label on the scan path; `ScanRegion.function` may be None |
| 3.1 | ordering by rung, then rank |
| 3.2, 3.4 | `ScanBudget` total, regions consumed in rank order |
| 3.3 | `regions_unjudged` + `budget_exhausted` |
| 3.5 | `cost_usd`, elapsed in the manifest |
| 4.1–4.5 | `benchmarks/repos/` pinned checkouts; a measurement recording build time, wall-clock, peak memory, rungs, cost, and whether the known CVE was found |
| 5.1, 5.2 | rung + witness + path:line on every finding; the report states what `suspicion` means |
| 5.3 | findings to the writable output, never the analysed tree |
| 5.4 | `model/report.py` scan summary |
| 6.1–6.5 | the image built and verified; wrappers; read-only mount; network off; pinned + checksummed engine |
| 7.1, 7.2, 7.3 | the two decisions recorded in this spec's notes with their reasons |