[agent] Filed by the scheduled architecture audit routine (CLI and core). Register: C11.
Kind: tracking. Source: review §2.2, §2.6 and R5; register C11.
Problem
run_scan is one 1,540-line function on 045d7ec (the review measured 1,499 at 2463257). It does everything a scan does, in order:
- mode folding, socket.yml policy loading, the PATH fan-out, path-scope parsing, the rollout cap and the offline refusal (L1429–L1521);``
- the crawl, the lockfile supplement, the vendored-ledger supplement and the package/path filters (L1560–L1771);``
- a "no packages" exit with its own JSON and human arms (L1772–L1893);``
- the concurrent batch query with the proxy fallback, the fold and the patch counts (L1932–L2132);``
- a JSON arm and a human arm that each dispatch all three modes again.
The JSON arm is L2141–L2428 and the human arm follows it:
The mode is still three booleans (apply/vendor/hosted, L1525–L1527),`` derived from args.mode and branched on in about 27 conditions. Output mode leaks into the engine: `discover_selected` takes progress, warning and `json_warnings: Option<&mut Value>` parameters, so the selection step can't be shared between the two arms.
Impact
Every scan behavior change has to be made twice, once per output arm, and every mode-specific fix touches the same 1,540-line body. The open arch-audit issues on scan's proxy fallback, batch chunking and the JSON error shape (#647, #675, #704) all edit this function. Its size also forces the Box::pin/boxed_* indirections that keep the debug-build poll frame under Windows' 1 MiB main-thread stack.
Target design
run_scan becomes a short pipeline over typed phase results, with rendering only at the end:
prepare (policy, scope, cap, offline) → ScanSetup;
collect_inventory (crawl, supplements, filters) → ScanInventory;
query_patches (batches, fallback, fold, counts) → PatchQuery;
select (rows, partitions, rollout plan, computed once) → Selection;
- one
match mode that hands Selection to the hosted, agent or vendored consumer, which returns a typed outcome;
render_json or render_human over that outcome.
Each step lands as its own PR. Steps 2 and 3 are mechanical moves; steps 4–6 change structure, but must not change output.
Checklist
Acceptance criteria
Dependencies
Children 2 and 5 wait on #647, #675 and decision #704. It relates to C12 (engine code out of the CLI) and C10/#793 (RunCtx): the phase functions should take the RunCtx when it exists.
Consolidated work — backlog review, 2026-10-08
The following standalone issues are now tracked here. Their closure consolidates scheduling; it does not mean their implementation is complete. Original reports and discussion remain linked below.
#844: Move scan's crawl, supplements and filters out of run_scan into collect_inventory
Preserved scope and acceptance criteria from #844
Proposed change
- Add
struct ScanInventory, holding the locals the later phases read (all_crawled, eco_counts, npm_crawl, lockfile_only, layout_refusals, scanned_purls, vendored_purls, vendor_owned_purls, unwired_vendored, hosted_pins, update_manifest, filtered_crawled, all_purls, …).
- Add
async fn collect_inventory(args, ctx, policy, path_scope, mode, prune, status) -> ScanInventory in a new scan/inventory.rs, and move the block there verbatim.
run_scan calls it once and destructures the result. Deleted: the ~210 inline lines.
- No behavior change: same crawl, same order of warnings, same status-line text.
Size and scope
scan/mod.rs (−~210) and a new scan/inventory.rs (+~240), with no changes to other files. Out of scope: the batch query, the selection and the mode dispatch (later children), and any change to what is crawled.
Acceptance criteria
[agent] Filed by the scheduled architecture audit routine (CLI and core). Register: C11.
Kind: tracking. Source: review §2.2, §2.6 and R5; register C11.
Problem
run_scanis one 1,540-line function on045d7ec(the review measured 1,499 at2463257). It does everything a scan does, in order:The JSON arm is L2141–L2428 and the human arm follows it:
run_redirect@2187boxed_run_redirect_selected@2675discover_selected@2226/2246,``classified_rows@2265, `partition_agent_selection` @2282, `plan_kept_rows` @2283discover_selected@2489,classified_rows@2510,partition_agent_selection@2702,plan_kept_rows@2730download_and_apply_patches_with@2339boxed_vendor_json_path@2375boxed_vendor_interactive_path@2912The mode is still three booleans (
apply/vendor/hosted, L1525–L1527),`` derived fromargs.modeand branched on in about 27 conditions. Output mode leaks into the engine: `discover_selected` takes progress, warning and `json_warnings: Option<&mut Value>` parameters, so the selection step can't be shared between the two arms.Impact
Every scan behavior change has to be made twice, once per output arm, and every mode-specific fix touches the same 1,540-line body. The open
arch-auditissues on scan's proxy fallback, batch chunking and the JSONerrorshape (#647, #675, #704) all edit this function. Its size also forces theBox::pin/boxed_*indirections that keep the debug-build poll frame under Windows' 1 MiB main-thread stack.Target design
run_scanbecomes a short pipeline over typed phase results, with rendering only at the end:prepare(policy, scope, cap, offline) →ScanSetup;collect_inventory(crawl, supplements, filters) →ScanInventory;query_patches(batches, fallback, fold, counts) →PatchQuery;select(rows, partitions, rollout plan, computed once) →Selection;match modethat handsSelectionto the hosted, agent or vendored consumer, which returns a typed outcome;render_jsonorrender_humanover that outcome.Each step lands as its own PR. Steps 2 and 3 are mechanical moves; steps 4–6 change structure, but must not change output.
Checklist
collect_inventory→ScanInventory(mechanical). Filed as a sub-issue.query_patches→PatchQuery(mechanical). Lands after Only scan, get <uuid> and vex fall back to the public proxy on 401/403; get search, apply, rollback, repair and vendor eject fail #647 and Chunk patch batch searches through one core helper with one set of batch limits #675, which edit the same block.discover_selected→classified_rows→partition_agent_selection→plan_kept_rows) and remove the output parameters fromdiscover_selectedby returning warnings as data.Selection; delete the secondrun_redirect/boxed_vendor_*/download_and_apply_patches_withcall of each pair.render_json/render_humanover the typed outcome, including the "no packages" exit. Coordinate the JSON shape with decision Decide: one shape for the--jsontop-levelerror(scan and get emit both a string and a {code, message} object) #704.Acceptance criteria
run_scanis under 200 lines and dispatches each mode once.scantest targets and the benchmark gate from Add ascanbenchmark suite and a CI performance gate #485 stay green at every step, with no snapshot or golden JSON changes except where a child says so.Dependencies
Children 2 and 5 wait on #647, #675 and decision #704. It relates to C12 (engine code out of the CLI) and C10/#793 (
RunCtx): the phase functions should take theRunCtxwhen it exists.Consolidated work — backlog review, 2026-10-08
The following standalone issues are now tracked here. Their closure consolidates scheduling; it does not mean their implementation is complete. Original reports and discussion remain linked below.
#844: Move scan's crawl, supplements and filters out of run_scan into collect_inventory
Preserved scope and acceptance criteria from #844
Proposed change
struct ScanInventory, holding the locals the later phases read (all_crawled,eco_counts,npm_crawl,lockfile_only,layout_refusals,scanned_purls,vendored_purls,vendor_owned_purls,unwired_vendored,hosted_pins,update_manifest,filtered_crawled,all_purls, …).async fn collect_inventory(args, ctx, policy, path_scope, mode, prune, status) -> ScanInventoryin a newscan/inventory.rs, and move the block there verbatim.run_scancalls it once and destructures the result. Deleted: the ~210 inline lines.Size and scope
scan/mod.rs(−~210) and a newscan/inventory.rs(+~240), with no changes to other files. Out of scope: the batch query, the selection and the mode dispatch (later children), and any change to what is crawled.Acceptance criteria
run_scanno longer contains the crawl or the filters. It reads them from oneScanInventory.scanintegration targets, the--jsonsnapshots and the benchmark gate (Add ascanbenchmark suite and a CI performance gate #485) stay green.collect_inventoryon a small fixture and checksall_purlsagainst the path-scope and package-spec filters.