Skip to content

scan's vendored planning and preview run the refusal preflights twice, and the planning copy skips the PyPI refusals #1908

Closed

Description

[agent] Filed by the scheduled architecture audit (audit-cli-tests). Register: register comment.

Kind: refactor. Register ID: T35.

Problem

scan/vendor_flow.rs asks "which selected purls would the wet run refuse?" through two separate functions. Each one loads the ledger and runs the whole preflight set on its own:

  • preview_vendor L193-L270 runs load_state, then the Bun, vlt and npm-lock preflights, hosted_pnpm_refusals, the takeover refusals and pypi_refusals, and returns PreviewVerdict::WouldRefuse rows.
  • preflight_refused_purls L349-L379 is the planning pass that keeps refused new patches from taking a --max-new-patches slot. It runs load_state, the Bun, vlt and npm-lock preflights, hosted_pnpm_refusals and gem_takeover_preview_refusals.

The two have drifted. The planning copy has no pypi_refusals (L205-L210 vs L370-L376).````` So a PyPI patch the backend refuses before downloading (pypi_no_requirements, `pypi_uv_no_sources`) still takes a rollout slot, while the preview of the same run shows it as `would_refuse`. That is my reading of the code; I have not executed it.

A dry run calls both functions back to back: scan/mod.rs calls the planning pass at L3306, then the preview at L3431 (the JSON path does the same at L772 and L790). So every preflight, the gem takeover probe and hosted_claimed_purls' lockfile discovery run two or three times.

All three callers of preview_vendor (scan/mod.rs L3425,`````` scan JSON L785 and get.rs L2287) also compute gem_takeover_preview_refusals and `patch_server_origins` themselves before passing them in.

Proposed change

  • Keep one function: preview_vendor(common: &GlobalArgs, selected) -> VendorPreview, which computes origins and takeover refusals itself.
  • The planning pass becomes preview.rows.iter().filter(|r| matches!(r.verdict, WouldRefuse{..})). Delete preflight_refused_purls.
  • Compute the preview once per run and reuse it for planning and rendering.
  • Assert the preview unit tests on PreviewVerdict and delete the test-only preview_vendor_json shape (L1409-L1443).

Size

scan/vendor_flow.rs, scan/mod.rs, get.rs. About −80 / +30 lines.

Acceptance criteria

Activity

  1. added
    arch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)
    refactorStructural change: duplicated code or logic, missing abstraction, layering, dead code
    on Nov 1, 2026
  2. mikolalysenko commented on Nov 1, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged: priority:p1 (the drift skips the PyPI pypi_refusals in scan's vendored planning pass, so a refused PyPI patch can take a --max-new-patches slot). Not a duplicate; no open PR touches preflight_refused_purls. Distinct root cause from #1909 (vendor.rs download plan vs scan/vendor_flow.rs planning), so not clustered.


    Generated by Claude Code

  3. mikolalysenko commented on Nov 1, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (single-issue cluster; root cause: scan's vendored planning pass re-runs the preflight set by hand instead of reading the preview, and its copy dropped pypi_refusals). Branch: agent/fix-scan-vendor-preview-planning. Claim-ID: 2026-11-01T12:21:12Z-780847


    Generated by Claude Code

  4. mikolalysenko commented on Nov 1, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1930


    Generated by Claude Code

  5. added 2 commits that reference this issue on Nov 1, 2026
    512a4c4
    2a26a39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent:triagedarch-auditFiled by a scheduled architecture audit routine (see the architecture review discussion)priority:p1refactorStructural change: duplicated code or logic, missing abstraction, layering, dead code

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions