v2.6.0 — Phase 2B: Analyst Copilot + DAG Parallel + Calibration
Phase 2B Release
SCOUT v2.6.0 delivers three axes of change that position it as a single-firmware analyst copilot: performance (DAG parallelization PoC), analyst UX (reasoning trail + MCP override loop), and honest confidence calibration (detection vs priority separation).
Merged via PR #6 (rebase) as 6 atomic commits — any one could ship independently.
1. DAG Parallelization PoC (PR #10)
- New
stage_dag.pywith manualSTAGE_DEPS(42 entries) + Kahntopo_levels()(15 levels, max-width 7) run_stages_parallel()— ThreadPoolExecutor level-wise submit, skip-on-failed-dep,fail_fast=True/Falsemodes. Sequentialrun_stages()unchanged- New CLI flag:
--experimental-parallel [N](default 4 workers) on bothanalyzeandstagessubcommands ProgressTracker(out_of_order=True)for completion-order rendering in parallel mode
# Opt-in parallel execution
./scout analyze firmware.bin --experimental-parallel 42. Reasoning Trail Persistence (PR #11 + PR #13)
New reasoning_trail.py module captures structured ReasoningEntry records for LLM-driven finding adjustments. The adversarial_triage debate loop now records advocate / critic / decision entries with llm_model and 200-char raw_response_excerpt. The fp_verification pattern matcher records sanitizer / non-propagating / sysfile hits with per-pattern delta.
All three analyst surfaces expose the trail:
- Web viewer — collapsible
<details>section with CSS styling - Analyst markdown report — numbered "Reasoning Trail (N steps)" subsection per finding
- TUI finding detail —
render_finding_detail_with_trail()(AIEDGE_TUI_ASCII-compatible)
SARIF properties bag gains scout_reasoning_trail.
3. MCP Analyst Tools (PR #12)
4 new MCP tools for analyst-driven feedback:
| Tool | Purpose |
|---|---|
scout_get_finding_reasoning |
Fetch full reasoning trail for a finding |
scout_inject_hint |
Push an analyst hint into the feedback registry |
scout_override_verdict |
Force-set a finding verdict (confirmed / false_positive / wont_fix / needs_info) |
scout_filter_by_category |
Filter findings by vulnerability / misconfiguration / pipeline_artifact |
terminator_feedback.py extended with add_analyst_hint / get_analyst_hints / set_verdict_override (fcntl.flock-safe, assert_under_dir enforced). The adversarial_triage advocate prompt now reads analyst hints from AIEDGE_FEEDBACK_DIR and prefixes them priority-sorted — opt-in; byte-identical behavior when env var unset.
4. Detection vs Priority Calibration (PR #15)
Closes external reviewer critique that EPSS-additive confidence made SCOUT's confidence field look like a ranking heuristic instead of a true-positive probability.
New scoring.py with:
@dataclass(frozen=True)
class PriorityInputs:
detection_confidence: float
epss_score: float | None
epss_percentile: float | None
reachability: str | None
backport_present: bool
cvss_base: float | None
def compute_priority_score(inputs: PriorityInputs) -> float:
# Weights: detection 50% / EPSS 25% / reach 15% / CVSS 10%
# Backport: -0.20 penaltycve_scan.py:1140-1170 refactored: confidence now strictly capped at STATIC_CODE_VERIFIED_CAP=0.55 (static evidence only). EPSS / reachability / backport / CVSS now feed priority_score instead.
New doc: docs/scoring_calibration.md with a before/after worked example.
5. Extraction Failure Analyst Guidance (PR #14)
When extraction fails, SCOUT now emits a structured guidance block pointing the analyst at concrete next steps:
Detected encryption (entropy 7.95/8.0). Possible vendor decryption needed.
Suggested actions:
1. Check vendor_decrypt.py for known vendor formats and add a handler for this firmware.
2. Provide a pre-extracted rootfs: ./scout analyze firmware.bin --rootfs /path/to/extracted
3. Try binwalk v3 entropy mode / alternative extractor: binwalk --entropy firmware.bin
4. File an issue with the first 4 KB hex dump: xxd firmware.bin | head -64
Hint: docs/runbook.md#extraction-failure
New docs/runbook.md#extraction-failure section with symptoms/causes/remediation table.
Verification
| Metric | v2.5.0 | v2.6.0 |
|---|---|---|
| pytest | 865 | 1027 passed, 1 skipped (+162) |
| pyright | 0 errors | 0 errors, 0 warnings (baseline preserved) |
| ruff | clean | clean |
| CI | 5/5 green | 5/5 green |
New test distribution: reasoning_trail 20 · extraction_guidance 18 · mcp_analyst_tools 33 · stage_dag 14 · run_stages_parallel 14 · scoring 19 · reasoning_trail_viewer 44
R7000 smoke test (PR #15): 3 findings, all carrying priority_score + priority_inputs. cve_confidence_above_0.55_cap = 0 (detection cap enforced). priority_bucket_counts = {critical: 0, high: 0, medium: 3, low: 0}.
Design Invariants Preserved
- Additive-only on
findings.py(PR #7a pattern continues forcategory, nowreasoning_trail,priority_score,priority_inputs). No report schema version bump. All 7 downstream consumers untouched. - Sequential
run_stages()bit-identical to pre-PR state. StageContextfrozen invariant preserved (thread-safe sharing without locks).- All file writes continue through
assert_under_dir(). - v2.5.0 LLM driver contracts (system_prompt, temperature, 5-stage parser) unchanged.
- 200-char
raw_response_excerptcap enforced atReasoningEntry.__post_init__(cannot be bypassed by call sites).
Deferred to Phase 2C
- Wall-clock comparison sequential vs
--experimental-parallel 4on R7000 (PoC merged, real firmware measurement pending) - Analyst hint injection loop smoke with real LLM driver +
AIEDGE_FEEDBACK_DIR - Benchmark freeze rule enforcement (
check_baseline_metadatawiring requires fresh corpus baseline)
Upgrade Notes
Breaking: confidence field semantics changed for CVE findings — it is now strictly detection evidence (≤0.55). Consumers previously ranking by confidence should migrate to priority_score. See docs/scoring_calibration.md for the full contract.
Everything else is additive; existing consumers that ignore the new fields continue to work unchanged.
Full diff: v2.5.0...v2.6.0
PR #6: #6
🤖 Generated with Claude Code