Skip to content

Automating rung 1: what a dependency-fixing bot may do, and the two things it must never do #18

Description

@bdelanghe

Detection is automated (per-PR scan plus the Tuesday rescan). Remediation is not — a red lane produces a finding and waits for a person. REMEDIATION.md says most reds clear at rung 1 (relock within declared ranges), which is mechanical. So the obvious question is whether rung 1 can be a bot.

Mostly yes. But the 2026-08-03 front-desk-scheduler remediation (#118 → front-desk#122/#123/#125) produced two constraints that are not obvious up front, and a naive implementation gets both wrong in the harmful direction.

Constraint 1 — a fresh advisory is unfixable for 24h, and "make it green" is the wrong objective

Deno's minimum dependency age policy (24h default) refuses any npm version published less than a day ago. When an advisory and its fix land the same day — which is the common case for a fast-moving package — the fix is unreachable for up to 24 hours on every Deno repo in the fleet. The failure is silent: deno install does not announce a skipped version, it resolves lower.

A bot optimising for "the check goes green" reaches for --minimum-dependency-age 0. On #118 that flag, tried and discarded by hand, pulled hono 4.13.0 published nineteen minutes earlier plus an unrelated jose bump — because the flag is global, not per-package. The result is a PR that adopts a minutes-old minor version while describing itself as a security fix. That is the exact supply-chain shape the gate exists to refuse, produced by the tool meant to improve security.

So the bot needs "blocked by the age gate — retry after <timestamp>" as a first-class outcome, not a failure and not something to route around. It must never relax the gate. (A human who genuinely must can use the smallest relaxation that admits one version — --min-dep-age 1170, or a cutoff timestamp — but that is a judgement call with a written justification, which is what #123 did.)

Corollary worth encoding: a cutoff timestamp also lets the bot verify a future resolution without committing it — resolve at the boundary, check the outcome, throw it away. That turns "will this clear tomorrow?" into a fact rather than a guess.

Constraint 2 — check the parents before assuming you are blocked on upstream

#118 concluded the fix needed an override because a plain resolve would not take it, and framed one option as waiting for @bounded-systems/verbspec-mcp / the SDK to move. Both parents' ranges already admitted the fixed version (hono ^4 and ^4.11.4). No upstream release was ever going to be the trigger; the constraint was the clock.

These two look identical from outside — "the resolver won't take the fix" — and they have opposite remedies: one waits for a release that is coming, the other waits for a timer. The tell is whether the declared ranges already admit the fixed version. A bot can compute that; it is exactly the sort of check a human skips.

Proposed scope

May do, unattended:

  • Rung 1 only — fresh resolve, no manifest edits, no age-gate flags of any kind
  • Run the repo's tests and rescan
  • Open a PR when clean; report the before/after finding table

Must escalate, never attempt:

  • Anything above rung 1 — a bump outside a declared range (brand: style-dictionary 4.4.0 → 5.4.4, CVSS 8.8), or a transitive chain (drift-gate: brace-expansionminimatchts-morph ^23 → ^28). These are judgement, and Remediation queue: clear findings and remove report-only grace (it has no expiry and no owner) #8's history shows they are not rare.
  • Any osv-scanner.toml acceptance. An ignore entry is a decision with a reason, and a bot has no reason.
  • Merging. Auto-merging bot dependency bumps into main is itself a supply-chain path, and it sits badly with an org whose posture is bounded authority and one auditable pipeline per change. Ending at a green PR is the right stopping point, not a limitation.

Prerequisite, and it is blocking: ~44 callers are pinned behind the shared lane (#10). A fixer shipped today would run a stale scanner in most of the fleet, and its fixes would be validated against whatever 62990dd pinned rather than current main. Re-pinning has to be decided first.

Cheaper first move

For the npm and Cargo repos this is largely turning on Dependabot, which already did most of conformance-kit's table (#48–#52) and needs no new code. The custom lane only earns its keep where Dependabot cannot go — deno.lock, which is why the converter exists. Worth splitting on that line before building anything.

The honest caveat

Three factual errors were corrected during that one remediation: #118's diagnosis of the resolver, #123's self-contradicting deletion condition, and a claim in a comment on infra#104. Each surfaced because someone re-derived an inherited claim. A bot would have propagated all three at speed. That is an argument for keeping the human at the PR, not an argument against the bot.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions