Context
Three dependabot PRs were closed back-to-back this week because the project's stated Node support range can't accommodate modern dependency versions. The pattern is becoming structural:
| PR |
Bump |
Why it was closed |
| #683 |
eslint 9.39.4 → 10.5.0 |
eslint 10 uses util.styleText (Node 20.12+); CI tests Node 18 → TypeError: util.styleText is not a function |
| #684 |
@eslint/js 9.39.4 → 10.0.1 |
Companion of #683 — same root cause |
| #685 |
which 5.0.0 → 7.0.0 |
which@7 declares engines: { node: '^22.22.2 || ^24.15.0 || >=26.0.0' }; our engines: '>=16' would let npm install --engine-strict hard-fail for Node 16/18/20 users |
This is no longer a one-off — it's the new baseline. As the ecosystem moves past Node 18, every fresh major bump will hit the same wall, dependabot will keep opening PRs against this gap, and the AI Review Gate will keep flagging the same engine mismatch.
Current state (inconsistent)
// package.json on develop
"engines": {
"node": ">=16.0.0",
"npm": ">=8.0.0"
}
# .github/workflows/test.yml
node-version: [18.x, 20.x]
Two problems already exist before any bump:
engines.node claims >=16 but CI doesn't test 16 — Node 16 may already be silently broken.
- Both Node versions the matrix tests are now end-of-life upstream:
- Node 18 EOL: 2025-04-30 (~14 months ago)
- Node 20 EOL: 2026-04-30 (~2 months ago)
- Node 22 LTS active through 2027-04-30
- Node 24 LTS active through 2028
Options
A. Drop Node 16/18, keep Node 20+ (minimal change)
B. Drop everything <22, align with active LTS (recommended)
engines.node → >=22.0.0
- CI matrix →
[22.x, 24.x]
- Unblocks:
eslint@10, which@7, and the next year of major bumps
- Risk: anyone on shared infra still pinned to Node 20 LTS can't install ClaudeAutoPM. Counter-risk: that infra is already running EOL Node.
C. Stay on >=16, pin transitive deps below their Node 22+ thresholds (status quo)
- Don't bump engines, don't touch CI matrix
- Keep closing dependabot PRs with rationale comments
- Cost: every major bump becomes a closed PR. Security backports for the major versions we can't take become a manual problem within ~12 months.
Decision criteria
- Who actually runs ClaudeAutoPM? It's a CLI distributed via npm + a
.claude/ install payload — there's no service-style install base. Users are developers running it locally. Realistically, are any of them still on Node 16 or 18?
- What does the install payload itself require? Does any
.claude/ script use Node syntax that breaks on older Node, or is the dependency-on-modern-Node only in dev/test?
- What do users see if we go strict?
npm install -g claude-autopm on Node 18 → error vs. warning. With engines-strict=false (npm default) they get a warning and it still works. With engines-strict=true it fails. Most users default-leave it false.
Acceptance criteria (whichever option wins)
Out of scope (don't bundle)
- Migrating any deprecated Node APIs in the codebase
- Rewriting bash scripts in the payload
- Plugin-level engine bumps for non-Node ecosystems
Closing the three dependabot PRs without this tracking issue means the decision keeps being made implicitly, one PR at a time. Better to call it once and execute.
Context
Three dependabot PRs were closed back-to-back this week because the project's stated Node support range can't accommodate modern dependency versions. The pattern is becoming structural:
eslint9.39.4 → 10.5.0util.styleText(Node 20.12+); CI tests Node 18 →TypeError: util.styleText is not a function@eslint/js9.39.4 → 10.0.1which5.0.0 → 7.0.0which@7declaresengines: { node: '^22.22.2 || ^24.15.0 || >=26.0.0' }; ourengines: '>=16'would letnpm install --engine-stricthard-fail for Node 16/18/20 usersThis is no longer a one-off — it's the new baseline. As the ecosystem moves past Node 18, every fresh major bump will hit the same wall, dependabot will keep opening PRs against this gap, and the AI Review Gate will keep flagging the same engine mismatch.
Current state (inconsistent)
Two problems already exist before any bump:
engines.nodeclaims >=16 but CI doesn't test 16 — Node 16 may already be silently broken.Options
A. Drop Node 16/18, keep Node 20+ (minimal change)
engines.node→>=20.0.0[20.x, 22.x]eslint@10(deps-dev(deps-dev): bump eslint from 9.39.4 to 10.5.0 #683/deps-dev(deps-dev): bump @eslint/js from 9.39.4 to 10.0.1 #684),@eslint/js@10which@7(needs Node 22+), and most likely the next several majors as the ecosystem movesB. Drop everything <22, align with active LTS (recommended)
engines.node→>=22.0.0[22.x, 24.x]eslint@10,which@7, and the next year of major bumpsC. Stay on
>=16, pin transitive deps below their Node 22+ thresholds (status quo)Decision criteria
.claude/install payload — there's no service-style install base. Users are developers running it locally. Realistically, are any of them still on Node 16 or 18?.claude/script use Node syntax that breaks on older Node, or is the dependency-on-modern-Node only in dev/test?npm install -g claude-autopmon Node 18 → error vs. warning. Withengines-strict=false(npm default) they get a warning and it still works. Withengines-strict=trueit fails. Most users default-leave it false.Acceptance criteria (whichever option wins)
engines.nodeupdated in rootpackage.jsonAND all plugin packages (packages/plugin-*/package.json) — keep them consistent.github/workflows/test.ymlupdated to matchengines-strictpolicy decided + documented in CONTRIBUTING (or README install section)Out of scope (don't bundle)
Closing the three dependabot PRs without this tracking issue means the decision keeps being made implicitly, one PR at a time. Better to call it once and execute.