Agent Skillsedtechre/pybroker › dependency-migration-triage

dependency-migration-triage

GitHub

深度审查依赖版本升级PR,通过比对源码差异、变更日志及测试覆盖度,评估潜在风险并生成迁移计划。

.claude/skills/dependency-migration-triage/SKILL.md edtechre/pybroker

Trigger Scenarios

用户粘贴 Dependabot/Renovate PR 链接或编号要求检查 询问依赖升级的具体变更内容及适配需求 希望验证版本升级安全性而非仅看 CI 状态

Install

npx skills add edtechre/pybroker --skill dependency-migration-triage -g -y
More Options

Non-standard path

npx skills add https://github.com/edtechre/pybroker/tree/master/.claude/skills/dependency-migration-triage -g -y

Use without installing

npx skills use edtechre/pybroker@dependency-migration-triage

指定 Agent (Claude Code)

npx skills add edtechre/pybroker --skill dependency-migration-triage -a claude-code -g -y

安装 repo 全部 skill

npx skills add edtechre/pybroker --all -g -y

预览 repo 内 skill

npx skills add edtechre/pybroker --list

SKILL.md

Frontmatter
{
    "name": "dependency-migration-triage",
    "metadata": {
        "internal": true
    },
    "description": "Rigorously triage a single Dependabot (or Renovate) dependency version-bump PR: pull the real changelog\/release notes across the full old->new version range (not the PyPI summary blurb), always read the actual source diff between the two tags as well - a changelog records what changed and can never record what stayed the same, which is what most \"we are unaffected\" arguments actually rest on - map both against actual usage sites in this codebase, write a migration plan that distinguishes API-signature changes from conceptual\/behavioral ones (a version bump can quietly change defaults or strictness with no signature change at all), verify whether existing tests would actually catch a regression in that exact spot or merely execute the line with stale data, write a regression test that fails on the old behavior when a real issue is found, fix what's fixable, and open a PR mirroring the migration that links back to the originating Dependabot PR. Use this whenever the user wants to review, assess, migrate, or avoid \"merging blindly\" a Dependabot\/Renovate PR; asks \"what changed\" or \"what needs to adapt\" for a dependency bump; wants confidence a version bump is safe beyond \"CI is green\"; or just pastes a Dependabot PR number\/link and asks to check it out. Trigger even for dependencies that look boring (docs tooling, linters, CI actions) — some of the highest-value findings here come from bumps everyone assumes are safe.\n"
}

Dependency Migration Triage

Request: $ARGUMENTS (a Dependabot PR number/URL, or a dependency name + old version -> new version)

You're triaging one dependency version bump. The goal is not "does it still import" — it's "what actually changed underneath this version number, and does anything in this codebase need to adapt because of it." A green CI run on the bump PR itself is necessary but not sufficient: CI only proves the currently-selected checks still pass, and says nothing about whether those checks were ever capable of catching the specific thing that changed. Two real findings from this repo's own history illustrate why that distinction matters — read references/case-studies.md before Phase 4 if this is your first time running this skill, or whenever a phase result feels uncertain.

Work through these phases in order. Scale depth to actual signal (see the "Right-size the investigation" note before Phase 1) — a trivial patch bump of a barely-used tool doesn't warrant the same effort as a 4-major-version jump of a runtime dependency, and pretending otherwise produces padded reports, not better decisions.

Phase 0 — Identify the bump

Resolve the PR (or dependency name) to: package name, old version, new version, and where in the repo it's declared (requirements.txt, setup.cfg, pyproject.toml, .github/workflows/*.yml for Actions). If given a PR number, gh pr view <n> --repo <owner>/<repo> gets you the title and diff.

Right-size the investigation. Before going deep, get a fast read on stakes:

  • Is this a runtime dependency (imported by src/) or dev/docs tooling only (linters, type checkers, doc builders, CI actions)? Runtime deps that ship in the actual package deserve more scrutiny than a docs-only tool nobody's users ever touch.
  • How big is the jump — a patch release, or several majors/many minors? Bigger jumps need more changelog reading, not more assumption.
  • Does this repo's test suite even exercise the dependency's usage sites at all (see Phase 5)? If a dependency has zero test coverage today, that's itself a finding worth surfacing regardless of whether this particular bump breaks anything. A one-line patch bump of an unused-by-default dev tool can reasonably get a light pass. A major-version bump of something src/ imports directly cannot.

Phase 1 — Get the real changelog, not the PyPI blurb

PyPI's description field and a Dependabot PR's auto-generated "Changelog" section are starting points, not the source of truth — they're often truncated, and they only show the target version's notes, not the full range you're crossing. Go to the dependency's actual GitHub repo and read the CHANGELOG.md / release notes / "what's new" docs covering every minor/major version between old and new (patch releases are usually folded into their minor version's notes — confirm this is true for the specific project rather than assuming). Use WebFetch/WebSearch. For a wide range, you don't need to quote every entry — you need to have actually read enough to know whether something in it touches what this repo uses.

Phase 2 — Read the diff between the two tags

Do this on every bump, including ones with a good changelog. A changelog records what changed; it can never record what stayed the same — and most reachability arguments are claims about the absence of change ("we're fine because the class we subclass is untouched"). No prose will ever state that for you. The diff is also the only thing that pins a change to an exact version and an exact symbol.

So the diff does two different jobs depending on what else exists:

  • No usable changelog — an absent or empty GitHub release, notes that live only in an in-repo CHANGES.rst/HISTORY.rst, a bare dump of PR titles. Then the diff is the only primary source there is, and "I read the changelog" is a claim you are not in a position to make.
  • A usable changelog — then it gives you leads and the diff turns them into proof, far more cheaply, because the prose already told you which files to open. An entry saying "add lazy random state in samplers" becomes "sampler._rng changed from numpy.random.RandomState to a LazyRandomState wrapper, at exactly v3.4.0" — which is the difference between suspecting a problem and being able to act on one.

Compare the tags directly — no clone needed:

gh api repos/<owner>/<repo>/compare/<old-tag>...<new-tag> \
  --jq '.commits[] | "- " + (.commit.message | split("\n")[0])'
gh api repos/<owner>/<repo>/compare/<old-tag>...<new-tag> \
  --jq '.files[] | "\(.status) \(.filename) +\(.additions)/-\(.deletions)"'

Add | select(.filename == "<path>") | .patch to read one file's hunks.

compare truncates silently on wide ranges, and the failure mode is a false negative. Over many versions GitHub drops the patch field and then drops files from .files altogether — with no error and no marker. Measured on joblib: 1.2.0...1.5.3 returns 134 files, 37 with patch == null, and omits joblib/parallel.py entirely despite it changing by 542 lines; narrowing to 1.4.2...1.5.3 returns it with its patch intact. Read naively, "the file isn't in the list" becomes "the file didn't change" — the exact opposite of the truth, on the file that mattered most.

Two habits make that safe:

  • Step through the range one minor at a time rather than spanning it in one call, and treat a single wide compare as a survey, never as evidence of absence.
  • Never conclude "unchanged" from a file's absence. If a file you expected to change isn't listed, narrow the range until it appears, or fetch it at both tags and diff locally (gh api repos/<o>/<r>/contents/<path>?ref=<tag> --jq .content | base64 -d). Absence of evidence here really is not evidence of absence.

Extract, in the order that decides things:

  1. Which files changed. Cross-reference against Phase 3's usage sites. A change confined to a subpackage this codebase never imports is unreachable, full stop.
  2. The patch for the files that are reachable — and, for anything you intend to call safe, confirmation that its source is genuinely untouched rather than merely unmentioned.
  3. Packaging metadatarequires-python, dependency pins, version ceilings. These decide what can resolve at all and are routinely absent from the prose.

Worked outcomes, all from real runs of this skill:

  • Sole source. mypy 2.3.0 → 2.3.1 had no GitHub release. The diff was six commits; the only non-mypyc change replaced an assert isinstance(...) with a conditional to fix a crash — a proof the bump cannot newly reject previously-accepted code, which no changelog existed to state.
  • Scoping a vague warning. alpaca-py 0.44.0's notes flagged one item "Breaking Change" with no scope. The file list showed it touching alpaca/trading/requests.py only, against a codebase that imports alpaca.data exclusively. Unreachable, settled in one command.
  • Proving non-change. For a pytest-randomly 3.12 → 4.1 major bump, the questions that mattered were whether -p no:randomly still worked and whether xdist seed propagation survived. The diff answered both by showing the pytest11 entry-point name and the XdistHooks class body byte-identical across the tags. A changelog cannot tell you that something is unchanged; only the diff can.

Track which source each later claim rests on. "Read in the diff" and "stated in the changelog" are different strengths of evidence, and Phase 8 asks you to report them apart.

Phase 3 — Map to actual usage

Grep src/, tests/, and (if that's the only place the dependency shows up) docs/ notebooks for every real call site — imports, function/class usage, config file settings that reference the tool. Don't reason from the dependency's own docs in the abstract; ground every claim in this codebase's actual call sites, with file:line references. If a dependency isn't used anywhere in src/ or tests/, say so plainly — that changes everything downstream (see Phase 5).

Phase 4 — Migration plan: API changes AND conceptual/behavioral ones

For each usage site found in Phase 3, cross-reference against Phase 1's changelog and Phase 2's diff, and classify: safe as-is / trivial fix (renamed param, deprecated arg, config key rename) / real code change needed / new concept to adopt.

The trap to avoid: only checking whether a function signature changed. Some of the most consequential changes in a version bump are changes to default behavior with no signature change at all — stricter type-checking defaults, changed numeric precision, a narrower default timeout, a schema a third-party API now returns that your code doesn't expect. Read references/case-studies.md for two concrete examples of exactly this class of bug in this repo's own history. Ask explicitly, for each usage site: "if I changed nothing in my code, could this dependency's new version make this line do something different than before?" — that question catches what a signature diff alone won't.

Phase 5 — Coverage check: does a test actually cover this, or just execute the line?

For every usage site flagged as anything other than "safe as-is" in Phase 4, find the test(s) that exercise it and read them — don't just check whether coverage tooling marks the line as executed. Ask: does the test construct the specific input/shape that the changed behavior would affect, with live logic (or a mock/fixture reflecting the new reality), or does it pass with data/mocks that predate the change and therefore can't reveal a regression even though the line technically runs? A line at 100% coverage can still be worthless for catching this exact class of bug — references/case-studies.md has a worked example. This phase's output is not a percentage; it's a specific yes/no per flagged usage site, with reasoning.

When coverage turns out to be zero or inadequate — which is common for docs tooling, CI actions, and other things outside the test suite's reach — you have to become the coverage yourself, and that means verifying both directions, not just one. The natural instinct is to check "does the new version work" and stop once it does. That only catches regressions; it silently misses the mirror case, where the old version was already broken and the bump is actually a load-bearing fix rather than a no-op. The only way to tell those apart is to run the exact same check against both versions and compare — if you're about to spend the effort building/executing something to confirm the new version is fine, spend the same few minutes running the identical check against the old version first. references/case-studies.md (case study 4) documents this skill catching itself getting this wrong: an earlier run verified the new version of a dependency built cleanly, concluded "safe, nothing to fix," and never noticed the old version actually crashed against the current environment — something a same-effort comparison against the old version would have caught immediately.

Phase 6 — Write a regression test, only when there's a real finding

If Phase 4/5 turned up an actual breaking change with inadequate coverage: write a test that constructs the exact input/shape that exposes it, and prove it's a real regression test, not a plausible-sounding one — reproduce the old buggy behavior directly (revert your Phase 7 fix locally, or hand-run the old logic in a scratch snippet) and confirm the new test fails against it, then reinstate the fix and confirm it passes. This is the difference between "I think this would have failed" and "I watched it fail."

If Phase 4/5 found nothing wrong: say so and stop here. Don't manufacture a test to look thorough — a fabricated regression test for a non-issue is noise that looks like rigor, and future readers can't tell the difference between "this guards something real" and "this pads the diff" unless you're honest about which is which right now.

Phase 7 — Fix what's fixable

Mechanical/trivial migrations (deprecation warnings, renamed parameters, config key renames, newly-required explicit arguments) get fixed inline as part of this same pass — don't leave free wins for a human to redo. Real breaking changes get a proper code fix paired with the Phase 6 regression test. If something needs a genuinely new concept adopted (not just a find-replace), implement it, but flag in the PR description that this is a judgment call the maintainer may want to review more closely than a mechanical fix.

If this phase produced a real code change (skip this entirely for a comment-only, nothing- to-fix outcome — running either of these against a no-op finding is pure overhead), close the loop on your own work before moving to Phase 8:

  • Run the silent-failure-hunter agent (from the pr-review-toolkit plugin, if available in this environment — otherwise apply the same lens yourself: does the fix swallow an error anywhere, degrade to a fallback without surfacing it, or introduce a new way for something to fail quietly?) against the diff. This is the same failure class the whole skill exists to catch in third-party dependencies — pointing it at your own fix before a maintainer sees it is the same discipline turned inward, not a generic review step.
  • If the fix touches secrets, authentication, or a publish/release pipeline (long-lived tokens, credential handling, OIDC/trusted-publishing config, CI steps that push artifacts somewhere), run /security-review (or apply that scrutiny yourself if unavailable) before proceeding. Skip this for everything else — most triages never go near credentials, and running it by default would be blanket overhead for no signal.

Phase 8 — Open a PR that mirrors the migration

Open a PR (same repo/branch conventions as the rest of this project — check recent merged PRs for the base branch, typically dev) containing the Phase 7 fix and Phase 6 test. The PR description must:

  • Link/reference the originating Dependabot PR number explicitly, so the maintainer can find it from either direction.
  • State plainly which of three tiers each claim rests on, and don't blur them: verified by executing something (a test that really failed then passed, a notebook actually run, mypy/ruff actually invoked with the new version), read in the diff (Phase 2 — a file list proving unreachability, a hunk read directly), or inferred from changelogs. The middle tier is the one that gets silently promoted to the first or demoted to the third; name it as its own thing. If nothing needed fixing, the PR (or a comment on the Dependabot PR, if a whole new PR would be empty) should say plainly "verified — the following was checked and nothing needs to change," not stay silent.
  • Let the maintainer choose their own depth: deep-review this PR, or just trust it and merge the Dependabot PR directly.

If this run is a dry-run / calibration (no real PR should be opened — e.g. while testing this skill itself), stop after producing the would-be PR title + body + diff, and say so explicitly instead of calling gh pr create.

A note on trusting static tools

Lint/type-checker findings (ruff rules, mypy errors, security scanners) are hypotheses, not verdicts, until you've traced the actual dataflow. A rule can flag a pattern that's provably safe in context (e.g. a zip() over two sequences whose lengths are structurally guaranteed equal by their type definitions) — references/case-studies.md has a worked example of exactly this. Before calling anything a "bug" in your report, verify it against the real code path, not just the rule's generic description of what it usually catches.

Version History

  • 95e00fe Current 2026-08-28 19:21

    新增无条件读取两个标签间源码差异的 Phase 2,防止因缺少变更日志导致漏判;更新 CLAUDE.md 规则以触发任何版本约束变更。

  • db53c67 2026-08-20 08:26

Same Skill Collection

skills/pybroker-indicator-creator/SKILL.md
skills/pybroker-model-trainer/SKILL.md
skills/pybroker-multi-interval/SKILL.md
skills/pybroker-optimize/SKILL.md
skills/pybroker-rotational-trading/SKILL.md
skills/pybroker-strategy-creator/SKILL.md

Metadata

Files
0
Version
95e00fe
Hash
f6ff9eb7
Indexed
2026-08-20 08:26

inicio - Wiki
Copyright © 2011-2026 iteam. Current version is 2.155.2. UTC+08:00, 2026-08-30 11:53
浙ICP备14020137号-1 $mapa de visitantes$