# U2 Shared core: the quick-rule path Traced read-only 2026-10-13. `hunter.py` is the regex quick-rule pool, not the LLM hunter. No model call anywhere on this path: `rank.py:32 complete_json` is a method on a Protocol used only by `rank_targets_with_model` (rank.py:46), whose only caller is tests/test_rank.py:82. ## Happy path (`_run_scan`, cli.py:619) 1. `load_config` (cli.py:624 → config.py:225): loads `.env` into os.environ (config.py:77-92), reads TOML, rejects retired keys (config.py:236). 2. `load_policy` reads CWE_Score.tsv (policy/verycode.py:30-37); `load_ruleset` parses every rules `*.toml` by rglob, filters framework priors, applies the ledger overlay (ruleset/store.py:44-71); `assert_rules_resolve` (verycode.py:73). 3. `ingest_sarif` (mapping.py:101) → `write_static_hints` (mapping.py:240). 4. `preprocess_repository` (preprocess.py:61): `enumerate_source_files` with `git ls-files` (preprocess.py:96, 240), `build_file_target` + `detect_language` + `detect_tags` (114, 130, 146), `attach_static_hints` (mapping.py:116), `_git_commit` (285), `write_preprocess_artifact` (90). 5. `uncovered_languages` (findings.py:78); `analyze_entry_points` (mapping.py:123-130) → `attach_reachability_hints` (mapping.py:181). 6. `rank_targets` without a model → `heuristic_rank` (rank.py:36, 78); reachability from hints (rank.py:112, 227); `calibrate_rankings` (calibration.py:161); `write_rankings` (rank.py:72). 7. Standard: `run_hunter_pool` (hunter.py:103) → `schedule_hunter_tasks` (70) → ThreadPool of 8 × `_run_hunter_task` → `quick_scan_findings` (151). Other modes: `quick_scan_findings` directly with emit floors (cli.py:675-685). 8. `quick_scan_findings` (findings.py:45) compiles rules (60-62) → `_finding_from_match` (119) → `resolve_severity` (verycode.py:56), `_reachability_for_line` (154) → `StaticFinding` (16). 9. `write_findings` (findings.py:113), `write_hunter_trajectories` (hunter.py:123), then score (`build_score_artifact`, scoring/project_score.py:98). ```mermaid flowchart TD CFG[load_config
config.py:225] --> POL[load_policy
policy/verycode.py:30] --> RS[load_ruleset
ruleset/store.py:44] RS --> CHK[assert_rules_resolve
policy/verycode.py:73] --> SARIF[ingest_sarif
mapping.py:101] --> PRE[preprocess_repository
preprocess.py:61] PRE --> ENUM[enumerate_source_files
preprocess.py:96] PRE --> ASH[attach_static_hints
mapping.py:116] PRE --> EP[analyze_entry_points
mapping.py:123] --> ARH[attach_reachability_hints
mapping.py:181] ARH --> RANK[rank_targets / heuristic_rank
rank.py:36 / rank.py:78] --> CAL[calibrate_rankings
calibration.py:161] CAL -->|standard| POOL[run_hunter_pool
hunter.py:103] --> TASK[_run_hunter_task
hunter.py:144] --> QS CAL -->|other modes| QS[quick_scan_findings
findings.py:45] QS --> FM[_finding_from_match
findings.py:119] --> SEV[resolve_severity
policy/verycode.py:56] FM --> SF[StaticFinding
findings.py:16] --> SCORE[build_score_artifact
scoring/project_score.py:98] ``` ## Side effects Subprocesses: `git ls-files -z` (preprocess.py:240), git commit hash (287). Writes: preprocess.py:93, findings.py:116, rank.py:75, hunter.py:125, mapping.py:242/247, config.py:258; ruleset/store.py:102, 113 are written by the learning loop, not scan. config.py:92 mutates os.environ. findings.py:42 loads PATTERN_RULES at import time. The hunter pool computes `selected_skills` and `retrieval_context` (hunter.py:96-97, 112) but only logs their ids; they never affect findings. **Bug:** the standard-mode pool skips `min_emit_priority`/`min_emit_precision` (compare cli.py:670 with cli.py:683-684). ## Dependencies on other units hunter.py:139 → skills.route_skill_ids (skills.py:318); preprocess.py:255 → model.layout.is_vendored (lazy); findings.py:89 → model.partitions.FRONTENDS (lazy); config.py:11 → contracts.Contract. Same pipeline is also called by gate.py:81-84, map_gate.py:52-54, pairs.py:794-815, plane/tasks/alerts.py:120-122, push/quick.py:87-90, improve/evolve.py:311-314, hunter_tools.py:232/307. ## mapping.py (1,087 lines): who imports what | Function | Importers | |---|---| | ingest_sarif :101, attach_static_hints :116, write_static_hints :240, write_entry_points :245 | cli.py / preprocess.py (+ tests) | | analyze_entry_points :123 | cli, gate, map_gate, pairs, hunter_tools, plane/tasks/alerts, learn/features, improve/evolve, push/quick, push/runner; 7 tests; 6 benchmarks | | attach_reachability_hints :181 | cli, gate, map_gate, pairs, plane/tasks/alerts, push/quick, improve/evolve | | php_hook_callbacks :134 | model/scan.py, model/taint.py; 2 benchmarks | | verifier_evidence_candidates :188, semgrep_mapping_tasks :204, codeql_mapping_tasks :218, differential_mapping_record :232, sharp_edge_record :236 | **tests only** (test_mapping.py); their records MappingTaskRecord, DifferentialMappingRecord, SharpEdgeRecord (:39, :48, :56) are produced nowhere else | ## Overlap check - policy/ (CWE severity table) vs push/policy.py (revision/capability admission): different things. - scoring/ `REACH_MULT` (project_score.py:15) duplicates `_BASE_REACH_MULT` (calibration.py:255). - calibration.py: only cli.py:27 and tests import it. - contracts.py (base `Contract`), model/contracts.py (question/scope schemas), push/contracts.py (push schemas, builds on model/contracts): layered, not duplicates. ## Confidence High on the call chain; medium on importer lists (name greps with -w). Not traced: parsers inside mapping.py:250-1087, skills routing internals, families.toml consumers beyond model/taxonomy.py:32.