Adds the architect's plan for bump-next-js (P0 ship-blocker #8) and the single-brief decomposition that the implementer worked from. - ## Architecture section: file plan, API surface (none), schema diff (none), test plan, risk list (R1-R16), Decomposition table, and slice_dependencies YAML. - Brief 1: bump Next.js 15.4.3 -> 16.2.6, migrate images.domains -> images.remotePatterns, install ESLint flat config, replace removed 'next lint' command with 'eslint .', add typescript devDep. - Decisions log A-D, dated 2026-05-23, recording four gate-1 scope changes driven by Boot-the-brief findings and an empirical R15 firing: - A: expand scope to include ESLint v8 -> v9 + flat-config migration - B: pivot eslint pin v9.39.4 -> v10.4.0 (latest dist-tag) - C: add typescript@^5.9.3 devDep (peerDependenciesMeta.optional annotation only suppresses npm warning; runtime hard-requires it) - D: re-pin eslint v10.4.0 -> v9.39.4 (R15 fired empirically; @typescript-eslint/scope-manager@8.59.4 predates v10 GA, lacks new addGlobals API) - Follow-up convoys queued: bump-eslint-10, bump-typescript-6 (both upstream-blocked on typescript-eslint shipping a v10-tested release). Co-authored-by: Cursor <cursoragent@cursor.com>
56 KiB
| name | classification | success_metric | skip | status | created | |||
|---|---|---|---|---|---|---|---|---|
| bump-next-js | feature | npm install next@16.2.6 ships, Vercel deploys complete, no runtime regressions in dev or build. |
|
open | 2026-05-22 |
Convoy: bump-next-js
Closes P0 ship-blocker #8 from .convoys/ship-readiness.md. Highest-priority convoy in the launch sequence — promoted to slot 0 because Vercel is currently refusing to deploy any branch (including main) until Next.js is bumped, which makes every downstream preview-smoke / visual-diff gate non-functional.
Why
Vercel's platform-level security gate is blocking every deployment with "Vulnerable version of Next.js detected, please update immediately". The lockfile currently resolves next@15.4.3; latest is 16.2.6. The build itself completes (Vercel CLI confirms Build Completed in /vercel/output [29s]), but the deployment is rejected before going live.
Concrete impact, as of 2026-05-22:
- The last successful deploy on
mainwas 2025-08-01. Production is stale. - Preview deployments are unavailable on every PR.
preview-smoke.ymlandvisual-diff.ymlhave nothing to point at, so they fail-quiet on every PR. - PR #1 (the bootstrap PR) cannot validate its own L3 visual gates because of this.
This convoy unblocks the entire launch sequence. Until it ships, the other 13 convoys are running half-blind. Success looks like:
package.jsondeclares"next": "^16.2.6"(or whatever the architect picks — see scope).package-lock.jsonregenerated.npm run devboots without warnings about deprecated APIs.npm run buildexits 0 with no breaking-change errors.- A PR opened from a feature branch produces a successful Vercel preview deploy.
preview-smokeandvisual-diffworkflows have a live URL to hit (they'll still fail on missing@playwright/testuntiladopt-vitestlands, but the Vercel half is no longer broken).- CI green: lint passes (wrapper is in place from bootstrap), aggregate gate passes.
Scope
In:
- Bump
nextfrom15.4.3to16.2.6inpackage.json+package-lock.json. - Bump
eslint-config-nextfrom15.4.2to a matching16.xrelease to keep the lint config aligned with the framework. - Audit Next.js 15 → 16 migration guide (blog, upgrade guide) and identify which surfaces in
tcg-vaultare affected. Educated guess at affected paths (validate during architect):next.config.js— theimages.domainsfield has been deprecated for several major versions; if Next 16 drops it, migrate toimages.remotePatterns.next/imageusage acrosspages/cards.js,pages/card/[id].js,components/CollectionSelectionModal.js,components/ManaSymbols.js,components/UploadImageModal.js— verify props are still supported.- Pages Router specifics — Pages Router is intentionally more stable than App Router across major bumps, but
getServerSideProps/getStaticPropssemantics may have edge-case changes. - API routes —
req/resAPI stays stable in Pages Router; should be a no-op surface. - Middleware —
tcg-vaulthas nomiddleware.jscurrently; nothing to migrate.
- Update
AGENTS.md"Tech stack quick reference" to bump the Next.js version string. - Validate via local
npm run build, then push to confirm Vercel preview deploys successfully.
Out (deferred to their own convoys):
- React 18 → 19 upgrade.
next@16peer-deps acceptreact@^18.2.0 || ^19.0.0. Currentreact@18.3.1is in range. A React 19 bump is its own convoy (bump-react) because of compiler / Suspense /use()API changes. - App Router migration.
tcg-vaultis on Pages Router. Migrating to App Router is a multi-month effort and outside this convoy. - Test runner adoption (
adopt-vitest/adopt-playwright-smoke) — those convoys remain queued. .eslintrc.jsonrule tuning — the bootstrap added a stub extendingnext/core-web-vitals. Ifeslint-config-next@16ships new rules that surface additional errors, defer the cleanup tofix-lint-baseline.
Hard "do not touch" in this convoy:
- No auth code (
lib/permission-middleware.js,pages/api/auth/,pages/api/auth-utils.js) — that'sfix-auth-bypass. - No DB code.
- No new features or UI changes beyond what's strictly required to keep existing pages rendering after the bump.
- No CODEOWNERS / workflow / convoy file edits.
- No feature flags. The bump ships unflagged.
Roles invoked
Per feature classification with custom skips (ia, ux, flag):
- role-architect — produces a slice plan. Reads the Next 16 migration guide, lists every breaking change that touches
tcg-vault, decides which need code changes vs. configuration changes vs. no-ops. Output: 1–3 briefs under.convoys/bump-next-js/brief-N-*.md. Likely shape:- Brief 1: the bump itself (package.json + lockfile + any required
next.config.jsmigration). - Brief 2 (if needed): code changes for any deprecated APIs (e.g.
<Image>prop rename). - Brief 3 (if needed): visual-diff baseline refresh if rendering changed.
- Brief 1: the bump itself (package.json + lockfile + any required
- role-implementer — single-writer flow. The bump itself is one file change + lockfile; can't be meaningfully parallelized.
- Audit fan-out (
/multitask, group idaudit-bump-next-js-<pr>) — runs in parallel after the PR is drafted:- role-reviewer — correctness, regression risk
- role-design-system-auditor — verify CSS / theming / token usage still renders correctly
- role-a11y-auditor — verify accessibility didn't regress (Next.js 16 may change focus-management defaults)
- role-doc-writer — last. Updates
AGENTS.md"Tech stack" section. Adds an entry to a CHANGELOG if one is started here (it'll be backfilled separately inlaunch-polish).
Todos
High-level checklist for the architect to refine into briefs:
- Brief 1 — Migration audit. Read the Next.js 16 release notes and upgrade guide. Produce a short table: deprecated API → file(s) that use it → migration step. Specifically check:
images.domainsdeprecation,next/fontchanges,next/imageprop changes, any default-runtime changes (edge vs node). - Brief 2 — Bump + lockfile.
npm install next@16.2.6 eslint-config-next@^16. Commitpackage.json+package-lock.json. Verifynpm ls nextshows the new version. - Brief 3 — Verify build + dev locally.
npm run buildmust exit 0 with no breaking-change errors.npm run devmust boot without deprecation warnings on the routes we ship today. If errors surface, this is where they get fixed. - Brief 4 — Vercel preview deploy. Push the branch and confirm the Vercel deploy completes successfully (status moves from
pending→success, notError). Capture the preview URL in the PR description. - Brief 5 — Visual diff baseline. If
preview-smoke.yml/visual-diff.ymlaren't installed yet (they need@playwright/test), this brief is informational — flag any obvious visual changes to the reviewer + design-system-auditor. Onceadopt-playwright-smokelands, this becomes a real verification step. - Doc-writer pass. Update
AGENTS.mdtech-stack line. Note the bump in the bootstrap PR's "Notes for reviewer" or, if PR #1 has merged by then, open a small standalone docs PR.
Hand-off
Next role: role-architect (IA + UX are skipped; routing straight to Architect).
To run it in a new chat, paste:
"Run role-architect on convoy
bump-next-js. Read.convoys/bump-next-js.mdfor scope and todos, then read the Next.js 15 → 16 upgrade guide and produce a slice plan. Output briefs to.convoys/bump-next-js/brief-N-*.md. Mark any briefs that are parallel-safe (probably none — this is mostly a single-writer flow except the audit fan-out). Be conservative about scope creep: if the migration guide flags an API not used intcg-vault, note it in the brief but don't add a 'while we're here' fix."
After architect publishes the brief(s), the user runs role-implementer serially. Once the PR is drafted, the user uses Cursor 3.2 /multitask to dispatch the audit cohort (reviewer + design-system-auditor + a11y-auditor) in parallel under group id audit-bump-next-js-<pr>.
Conductor exits here.
Architecture
Produced by role-architect on 2026-05-22 against Next.js 16.2.6 (latest stable; verified via npm view next version). Updated 2026-05-23 after four gate-1 scope changes (A, B, C, D — see Decisions log below):
- A: scope expanded to include the ESLint v8 → v9 + flat-config migration so
eslint-config-nextcan move to^16matchingnext. - B: pivoted from ESLint v9 to v10 (then-
latest) after gate-1 re-review of risk R14. - C: added
typescript@^5.9.3as a devDep after an implementer escalation surfaced thateslint-config-next@16'speerDependenciesMeta.typescript.optional: trueannotation does not maketypescriptruntime-optional. - D: reverted the v10 pivot back to v9.39.4 after a pass-2 implementer escalation showed Risk R15 firing empirically (
TypeError: scopeManager.addGlobals is not a functionfrom@typescript-eslint/scope-manager@8.59.4predating v10 GA). v10 deferred to the upstream-blockedbump-eslint-10follow-up convoy.
File plan
| File | Action | Purpose |
|---|---|---|
package.json |
modified | Bump dependencies.next from ^15.4.2 to ^16.2.6. Bump devDependencies.eslint from ^8 to ^9.39.4 (npm's maintenance dist-tag; per Decisions log entry D, reverted from the v10 pin set under entry B after R15 fired empirically). Bump devDependencies.eslint-config-next from 15.4.2 to ^16.2.6 to match next — peer-dep eslint: >=9.0.0 accepts v9.39.4 trivially. Add devDependencies.typescript: "^5.9.3" (per Decisions log entry C — eslint-config-next@16 bundles typescript-eslint, which hard-requires typescript at module load under both v9 and v10; the peerDependenciesMeta.typescript.optional: true flag only suppresses npm's install-time warning, not the runtime require). Replace scripts.lint from next lint to eslint . (Next 16 removed the next lint command). React, react-dom, and all other packages stay unchanged. |
package-lock.json |
modified | Regenerated by npm install. Reflects the new next@16.2.6, eslint@^9.39.4, eslint-config-next@^16.2.6, and typescript@^5.9.3 resolutions. Under Decision D the lockfile stays on the v9 dep-tree (@eslint/eslintrc is still a v9 transitive dep; the v10 dep-tree changes that would have removed it are deferred to the queued bump-eslint-10 follow-up convoy). The new typescript subtree is small — typescript itself has no dependencies and no peerDependencies. Do not hand-edit. |
next.config.js |
modified | Migrate images.domains: [...] (deprecated in 16, deprecation warning at startup) to images.remotePatterns: [...]. Three patterns, one per CDN currently in images.domains. |
.eslintrc.json |
deleted | The 40-byte legacy stub ({"extends": "next/core-web-vitals"}) is replaced by eslint.config.mjs because eslint-config-next@16 only supports flat config. Leaving both files in place would be a footgun. |
eslint.config.mjs |
new | Flat-config replacement for .eslintrc.json. Reproduces the prior next/core-web-vitals extends behavior using the verbatim shape from the official Next.js docs (defineConfig([...nextVitals, globalIgnores([...])])). globalIgnores covers the no-go-zone paths the user specified at gate 1 plus eslint-config-next's documented defaults. |
Doc-writer's AGENTS.md "Tech stack" string update is a separate PR by role-doc-writer after this one merges (per the convoy's Roles list).
API surface
No API changes. This convoy does not touch pages/api/**. The Next.js 16 upgrade guide does not change the Pages-Router req/res handler signature; tcg-vault's ~30 API handlers all use the legacy (req, res) => { ... } shape and continue to work unchanged.
Schema diff
No schema changes. This convoy does not touch the database. Neon Postgres + scripts/setup-neon-db.js are out of scope.
Test plan
tcg-vault has no automated test runner installed yet (vitest + Playwright adoption is tracked under adopt-vitest and adopt-playwright-smoke convoys). For this convoy:
- Manual smoke per
TESTING_GUIDE.mdis the verification mechanism. Specifically: home (/), login (/login), signup (/signup), browse (/cards), and/collectionsmust render without runtime errors after the bump. npm run buildexiting 0 is the integration test for the Turbopack default-bundler change. No custom webpack config exists innext.config.js, so Turbopack should "just work."npm run lintrunning to completion (regardless of the error count) is the integration test for the ESLint v8 → v9 + flat-config migration (per Decision D — Decision B's pivot to v10 was reverted after R15 fired empirically). The pre-existing baseline of ~100 errors is expected to shift modestly under v9 due to plugin major bumps (eslint-plugin-react-hooksv5 → v7,@next/eslint-plugin-next15 → 16) but not to shift the way v10 would have (no neweslint:recommendedrules, no JSX reference tracking, nono-shadow-restricted-names.reportGlobalThis: truedefault — those land later underbump-eslint-10). CI's|| truewrapper continues to tolerate any non-zero exit. Counting baseline drift isfix-lint-baseline's job, not this convoy's. Ifnpm run lintdoes not run to completion under v9.39.4 — e.g. it crashes with aTypeError— see Brief 1's failure-mode classifier under "Local verification." Decision D's expectation is that R15's empirical signature (scopeManager.addGlobals is not a function) does NOT recur on v9 because v9 doesn't calladdGlobals. If a differentTypeErrorfires on v9, escalate rather than patching transitive deps.- Vercel preview deploy reaching Success state is the end-to-end integration test. The convoy's
success_metric("npm install next@16.2.6 ships, Vercel deploys complete, no runtime regressions in dev or build") is exactly this. - Audit fan-out (
/multitask, group idaudit-bump-next-js-<pr>) is the qualitative gate:role-reviewerfor correctness,role-design-system-auditorfor token rendering,role-a11y-auditorfor focus / scroll-behavior regression. They run AFTER the PR is drafted, not as part of this brief. - Once
adopt-vitestlands, retrofit a smoke test fornext.config.jsparsing and one for<img>(or future<Image>) rendering against a fixture page.
Risk list
- R1: Turbopack-by-default may surface unexpected build/runtime differences vs webpack. Per gate-1 decision: accept the default. tcg-vault has no
webpack:block innext.config.js, no custom loaders/aliases, no Sass tilde imports, noresolve.fallbackworkarounds. Likelihood of regression: low. Fallback per command:next build --webpackandnext dev --webpack. If a regression appears, the implementer should reproduce on both bundlers (run the failing flow once with the default, once with--webpack) before deciding whether to revert the bump or pin the script to webpack. Capture the reproduction in the PR description forrole-reviewerto triage. Do not pre-emptively add--webpackto the scripts. - R2 — RESOLVED at gate 1, via the A → B → D path. Originally: "
eslint-config-nextcannot be bumped to^16in this convoy." Gate-1 decision A expanded scope to include the ESLint v8 → v9 + flat-config migration. Decision B pivoted from v9 to v10. Decision D reverted v10 → v9.39.4 after R15 fired empirically on the implementer's pass-2 lint run. Final pins:eslint@^9.39.4,eslint-config-next@^16.2.6,typescript@^5.9.3,.eslintrc.jsondeleted,eslint.config.mjscreated. See R12, R13, R14, R15 below for the residual + reinstated risks. The deferredmigrate-to-eslint-flat-configconvoy is closed before opening — its work has been folded in. Thebump-eslint-10follow-up convoy is queued as upstream-blocked — see "Follow-up convoys queued" section. - R3:
next lintremoval hard-breaksnpm run lint. Without thescripts.lintchange, both local devs and CI'snpm run lint --if-presentjob would invoke a removed command. Mitigation: change script toeslint .. CI's existing|| truewrapper continues to tolerate the pre-existing lint baseline (~100 errors, tracked underfix-lint-baseline). - R4:
images.domainsis innext.config.jsbutnext/imageisn't actually used. Strictly speaking, the migration is preemptive — silences the deprecation warning but adds no functional change. Acceptable: keeps the config valid for the eventualnext/imageadoption. Don't delete the block; that would force re-adding it later. - R5:
images.minimumCacheTTLdefault changed from 60s to 4h. Behavior change. Not impactful intcg-vaultbecausenext/imageisn't used. No mitigation required; flag here only so future readers don't re-investigate. - R6: Vercel deploy might fail for an unrelated reason. The convoy's premise is that the platform-level "Vulnerable version" gate is the sole blocker. If the build itself fails on 16 (e.g. an undocumented Turbopack edge case), the fix lands in this brief. If the failure is environmental (env vars, build settings), escalate — that's a different convoy.
- R7: React 18 stays — intentional.
next@16peer-dep acceptsreact ^18.2.0 || ^19.0.0. Current18.3.1is in range. Do not bump React in this convoy. React 19 has compiler / Suspense /use()API changes and is its own convoy (bump-react). - R8: TypeScript >=5.1.0 required by Next 16. Partially applicable.
tcg-vaultsource code remains plain JavaScript — notsconfig.json, no.ts/.tsxfiles, no source migration in this convoy. However, per Decision C (2026-05-23),typescript@^5.9.3is now installed as a devDep becauseeslint-config-next@16's bundledtypescript-eslintchain hard-requires it at module load. The original parenthetical claim — "eslint-config-next@16liststypescriptas an optional peer (peerDependenciesMeta.typescript.optional: true) so JS-only consumers are fine" — was wrong: that flag only suppresses npm's install-time warning; the transitive@typescript-eslint/typescript-estree@8.59.4(a regulardependency, not a peer) does an unconditionalrequire('typescript')at module load. See R16 for the full devDep impact analysis. - R9: Node.js floor — DEFANGED under Decision D. Originally elevated under Decision B because ESLint v10 raised the floor to
^20.19.0 || ^22.13.0 || >=24. Under Decision D's v9.39.4 pin, the ESLint floor reverts to^18.18.0 || ^20.9.0 || >=21.1.0(Next 16 also requires>=20.9.0— the same floor). CI'ssetup-node@v4node-version: '20', localnode@22.14.0, and Vercel's default Node 22 all satisfy with margin to spare. The "moving target onnode-version: '20'" concern from Decision B is inert under v9. The constraint will reactivate whenbump-eslint-10lands; the queued follow-up convoy should pick up the CI pin question (node-version: '20.19'or'lts/iron') at that point. - R10:
next devandnext buildnow use separate output dirs (.next/dev/vs.next/)..gitignoreline 28 has/.next/, which is a directory rule that covers both subdirs. No.gitignorechange needed. - R11: Convoy file's audit list (line 45) is wrong about
next/imageusage. Pages listed (pages/cards.js,pages/card/[id].js, etc.) use plain<img>tags, not<Image>. Architect verified viarg "from ['\"]next/image['\"]"— zero hits inpages/,components/,lib/. Surface this to the convoy author so future planning is not based on the same assumption. - R12 (post-gate-1 expansion; revised under Decision D): ESLint flat-config migration + plugin major bumps will shift the lint baseline modestly. Drivers under v9.39.4:
eslint-config-next@16.2.6bundleseslint-plugin-react-hooks@^7(vs v5) and@next/eslint-plugin-next@16(vs 15.4.2). The v10-specific drivers from Decision B's wording are deferred to the queuedbump-eslint-10follow-up (the three neweslint:recommendedrules, JSX reference tracking,no-shadow-restricted-names.reportGlobalThis: truedefault).eslint-envcomments would be errors under v10 — we have zero (rg "eslint-env"returned zero hits, ✓), so thebump-eslint-10follow-up will not snag here either. The ~100-error baseline is approximate and will move modestly under v9, more substantially when v10 lands. CI's|| truewrapper tolerates any non-zero exit, so this is non-blocking either way. Do not "fix while we're here."fix-lint-baselinewill reconcile against whichever baseline is current. - R13: Native flat-config import path is verbatim from Next.js docs — no
FlatCompatshim added. Boot-the-brief verified by extracting the published tarball thateslint-config-next/core-web-vitalsexports a flat-config array (module.exports = config). Under Decision D's v9 pin,@eslint/eslintrcis still part of v9's own dep tree (v10 dropped it), so the lockfile retains it as a transitive dep — but we still don't importFlatCompatfrom it. If for any reason the native flat-config export resolution fails at install time (e.g. a transitive dep mismatch), the implementer should NOT swap in@eslint/eslintrc'sFlatCompat— instead, raise it in the PR description and the architect will revisit. - R14 — REINSTATED under Decision D (2026-05-23). ESLint v10.4.0 is the current
latestdist-tag; this convoy pinseslint@^9.39.4(themaintenancedist-tag) per Decision D until upstreameslint-config-nextships a release that bundles a v10-testedtypescript-eslint. Tracked under follow-up convoybump-eslint-10(currently upstream-blocked) — see "Follow-up convoys queued" section. The previous "RESOLVED at gate 1 (Decision B)" framing was correct given Boot-the-brief evidence at the time; Decision D reverses it specifically because empirical lint runs surfaced R15 firing. Cost of pinning to maintenance: small. v9.39.4 still receives security backports if any are needed during the window beforebump-eslint-10lands; the v9 → v10 jump is a single-linepackage.jsonedit when prerequisites are met (no flat-config edits required — samedefineConfig+globalIgnoresshape works on both majors). - R15 — FIRED EMPIRICALLY (pass-2 implementer run, 2026-05-23); RESOLVED BY DECISION D. Originally framed as: "
eslint-config-next@16.2.6's bundled plugin set was published before ESLint v10 (Oct 2025 vs Feb 2026); v10 runtime compatibility is statically unprovable." What actually fired was a different (and worse) failure mode than the originally fearedcontext.getCwd()/SourceCode#getJSDocComment()deprecated-API removals:- Crash signature:
TypeError: scopeManager.addGlobals is not a function - Call site: ESLint v10's
lib/source-code/source-code.js:221callsscopeManager.addGlobals(...)as part of v10's redesigned global-ingestion path. - Missing-method site:
@typescript-eslint/scope-manager@8.59.4(transitive dep oftypescript-eslint@8.59.4, whicheslint-config-next@16.2.6bundles as a regulardependency) does not implementaddGlobalson itsScopeManagerclass. The method is a v10-introduced extension; v9 used a different ingestion path thattypescript-eslint@8.xwas authored against. - Why bundled-plugin set didn't help:
@typescript-eslint/scope-manager@8.xwas published Oct/Nov 2025; v10 GA was 2026-02-06.typescript-eslinthas not yet shipped a v10-tested release. The peer-dep rangeeslint: >=9.0.0is technically satisfied by v10, but the runtime compatibility was not. - Resolution (Decision D): revert
eslintto^9.39.4. The sametypescript-eslint@8.59.4works correctly on v9 because v9 doesn't calladdGlobals.typescript@^5.9.3(Decision C) is retained — that install was confirmed correct on pass 2 and is required under both v9 and v10. - R15 stays in the convoy's risk list as FIRED-RESOLVED so the historical record is preserved and so the queued
bump-eslint-10follow-up convoy inherits the diagnostic verbatim. The originally feared deprecated-API removals (context.getCwd(), etc.) are still live risks for the eventual v10 cutover, but they did not fire on pass 2 —addGlobalsfired first.
- Crash signature:
- R16 (new, post-gate-1 Decision C; status unchanged under Decision D): Adding
typescriptas a devDep bringstypescript@^5.xand its tooling into the dep tree. This is universally how JS-only Next.js projects handleeslint-config-next@16— the package'stypescript-eslinttransitive dep (specifically@typescript-eslint/typescript-estree@8.59.4'sdist/convert.js:40) hard-requirestypescriptat runtime despite being flaggedpeerDependenciesMeta.optional: trueat theeslint-config-nextwrapper level (theoptionalannotation only suppresses npm's install-time warning, not the runtime require). Confirmed correct under Decision D's v9 pin — pass-2 implementer evidence shows thetypescriptinstall resolved the originalCannot find module 'typescript'crash; the residualaddGlobalscrash was a different failure mode (R15) and is the reason for the v9 revert. No downstream impact expected:typescriptonly runs when lint runs (the JS source code is unchanged, notsconfig.jsonis created, no.jsfiles are renamed);fix-lint-baselineandadopt-vitestconvoys will not be affected. Engines:typescript@5.9.3requiresnode >= 14.17, well below ESLint v9's^18.18.0floor (and v10's^20.19.0floor whenbump-eslint-10lands) — no new Node constraint introduced. Lockfile impact: small —typescripthas nodependenciesand nopeerDependencies. Verified runtime require evidence: see Brief 1's Boot-the-brief finding #17 for the verbatim 9-site grep ofrequire('typescript')in the publishedtypescript-estree@8.59.4tarball, all unconditional (notry/catch, no dynamic import, norequire.resolveguard).
Decomposition
| Brief # | Title | Files | Depends on | Estimated PR size |
|---|---|---|---|---|
| 1 | Bump Next.js to 16.2.6 + migrate next.config.js, ESLint flat config, and lint script |
package.json (mod — next, eslint, eslint-config-next bumps + new typescript devDep per Decision C), package-lock.json (mod), next.config.js (mod), eslint.config.mjs (new), .eslintrc.json (deleted) |
(none) | 5 files touched (3 mod, 1 new, 1 deleted), lockfile regen (large auto-diff). True non-lockfile diff: ~31 LOC (~12 of which is the new eslint.config.mjs; +1 LOC for the typescript devDep line in package.json). |
Still one brief, even after four gate-1 scope changes (A, B, C, D). Both the Next bump and the ESLint migration touch package.json, so they cannot run in parallel anyway — keeping them in one brief gives reviewers one PR, one Vercel preview, and one revert boundary if anything regresses. The cumulative expansion adds ~21 LOC (delete a 40-byte file, add a ~12-LOC eslint.config.mjs, three devDep changes in package.json — two version bumps for eslint/eslint-config-next and one new line for typescript@^5.9.3). Decision D does not change the LOC count — it re-pins an existing line (devDependencies.eslint) from ^10.4.0 back to ^9.39.4, no addition or deletion. Total non-lockfile diff stays well under the 400-LOC guideline. Decisions C and D do not change the brief count, the brief's files: set, or the slice_dependencies graph — package.json and package-lock.json were already in scope from the start. Doc-writer's AGENTS.md pass remains a separate PR by role-doc-writer per the convoy's Roles list.
The audit fan-out (role-reviewer + role-design-system-auditor + role-a11y-auditor) is parallel via /multitask, but that's a downstream concern triggered by the conductor after the PR is drafted — not part of the implementer decomposition.
Slice dependencies (multitask-ready)
slice_dependencies:
- brief: 1
depends_on: []
files:
- package.json
- package-lock.json
- next.config.js
- eslint.config.mjs
deletes:
- .eslintrc.json
Single brief, no parallelization opportunity at the implementer stage. The conductor should dispatch role-implementer serially (no /multitask fan-out for the implementer phase). The audit-cohort fan-out happens later, after PR draft, under group id audit-bump-next-js-<pr>.
Decisions (post-IA round)
A — 2026-05-23: Expand convoy scope to include ESLint v8 → v9 + flat-config migration
Context. During the architect's initial Boot-the-brief check, two findings landed at human gate 1:
eslint-config-next@16.2.6requireseslint >= 9.0.0(flat config). The convoy file (line 42) prescribed bumpingeslint-config-nextto^16"matching next" but didn't account for this peer-dep cliff. The architect's first-pass plan pinnedeslint-config-next@15.4.2and flagged the deviation.next lintwas removed in Next 16.package.json'slintscript and CI'snpm run lintboth invoke a removed command in 16.
Decision. Expand this convoy to include the ESLint v9 + flat-config migration, rather than spinning out a separate migrate-to-eslint-flat-config convoy. Rationale: both the Next bump and the ESLint migration touch package.json, so they cannot ship in parallel anyway; one PR gives reviewers a single revert boundary; the expansion adds only ~20 LOC of non-lockfile diff (delete .eslintrc.json, add eslint.config.mjs, two devDep version bumps); and eslint-config-next@16 ships native flat-config exports so no FlatCompat shim or @eslint/eslintrc install is needed.
Specific changes baked into Brief 1:
- Bump
devDependencies.eslintfrom^8to^9.39.4(latest 9.x; ESLint v10 was released between convoy authoring and now — see Boot-the-brief #5 — but per this decision we stay on 9.x). - Bump
devDependencies.eslint-config-nextfrom15.4.2to^16.2.6. - Change
scripts.lintfrom"next lint"to"eslint .". - Delete
.eslintrc.json(40-byte stub:{"extends": "next/core-web-vitals"}). - Add
eslint.config.mjsusing the verbatim shape from the official Next.js docs:defineConfig([...nextVitals, globalIgnores([...])]). Imports come fromeslint/config(built-in helpers since 9.21.0) andeslint-config-next/core-web-vitals. globalIgnorescovers.next/**,node_modules/**,out/**,build/**,next-env.d.ts, andscripts/migrations/**per gate-1 instruction.
Out-of-scope (still deferred):
- Fixing the ~100-error pre-existing lint baseline. Stays under
fix-lint-baseline. CI'snpm run lint || truewrapper continues to tolerate non-zero exit; the baseline number will shift with the v9 plugin upgrades but counting that drift isfix-lint-baseline's job. - Bumping ESLint to v10. Surfaced as Risk R14; revisit in a later
bump-eslint-10convoy if desired. - Bumping React 18 → 19. Stays under
bump-react. - App Router migration, test-runner adoption, auth fixes, schema migrations — all unchanged from the original convoy scope.
Canonical authority. tcg-vault does not maintain docs/04-architecture/*.md files, so this Decisions entry IS the canonical authority. Decision recorded in chat on 2026-05-23 between user and role-architect. Boot-the-brief recheck performed against this decision before publishing the revised Brief 1.
Consequences for downstream roles.
role-implementer: must runnpm install next@^16.2.6 eslint@^9.39.4 eslint-config-next@^16.2.6(the three explicit version pins), then delete.eslintrc.json, writeeslint.config.mjsper the verbatim shape in Brief 1, and updatepackage.json'sscripts.lint. No mid-flight scope decisions.role-reviewer: includes the ESLint config change in correctness review. Verifynpm run lintruns (regardless of error count); verify the lockfile diff is consistent with the three version pins.role-design-system-auditorandrole-a11y-auditor: unchanged. The lint config doesn't affect render output.role-doc-writer: still updatesAGENTS.md"Tech stack" string (Next.js 15 → 16) in a separate PR. May optionally also update the line that says "JavaScript (not TypeScript)" remains accurate; no change needed there.
Superseded by Decision B (2026-05-23, same day). The implementer command above changed from
eslint@^9.39.4toeslint@^10.4.0. See entry B below for details.
B — 2026-05-23: Pivot ESLint pin from v9.x to v10.x
Context. Decision A (above, same day) expanded scope to include the ESLint v8 → v9 + flat-config migration, with eslint pinned to ^9.39.4. During the architect's Boot-the-brief recheck of A, finding #5 surfaced that ESLint v10.4.0 had been released to the latest dist-tag on 2026-02-06 — between when this convoy was authored (2026-05-22) and when gate 1 was reached (2026-05-23). v9.39.4 had moved to the maintenance tag. The architect surfaced this as Risk R14 with a "stay conservative on v9" recommendation. On gate-1 re-review, the user pivoted to v10 to avoid a back-to-back bump-eslint-10 convoy.
Decision. Pin devDependencies.eslint to ^10.4.0 (current latest) instead of ^9.39.4. All other pins from Decision A stand: next@^16.2.6, eslint-config-next@^16.2.6, .eslintrc.json deleted, eslint.config.mjs created with the same verbatim shape (no v10-specific signature change in defineConfig or globalIgnores).
Boot-the-brief recheck against v10 (no blocker found):
- Peer-dep compatibility.
npm view eslint-config-next@16.2.6 peerDependenciesreturns{"eslint": ">=9.0.0", ...}with no<10upper bound. v10 is accepted. - Node engine compatibility.
eslint@10.4.0engines:node ^20.19.0 || ^22.13.0 || >=24(tighter floor than v9's^18.18.0 || ^20.9.0 || >=21.1.0). CI'ssetup-node@v4withnode-version: '20'resolves to latest 20.x ≥ 20.19; localnode@22.14.0is in^22.13.0; Vercel default Node 22 is ≥ 22.13. All ✓. Residual concern (CI's "latest 20.x" is a moving target) is documented as Risk R9; pinning CI tonode-version: '20.19'would eliminate it but is out of scope per the convoy's "Hard do not touch" list. eslint/configexports retained. Extractedeslint@10.4.0tarball, openedlib/config-api.js: still re-exportsdefineConfigandglobalIgnoresfrom@eslint/config-helpers. The brief's verbatim shape is unchanged.eslint-envcomments are errors in v10.rg "eslint-env"returned zero hits intcg-vaultsource. ✓- No App-Router-only or Cache-Components surfaces affected. v10's
eslint:recommendedupdates, JSX reference tracking, andno-shadow-restricted-names.reportGlobalThis: truewill shift the lint baseline more than v9 would have, but that'sfix-lint-baseline's problem (Risk R12, expanded).
The one new risk v10 surfaces (R15 in the convoy file's Risk list): eslint-config-next@16.2.6 was published before ESLint v10. Its bundled plugin set (@next/eslint-plugin-next@16.2.6, eslint-plugin-react@^7.37.0, eslint-plugin-react-hooks@^7.0.0, eslint-plugin-import@^2.32.0, eslint-plugin-jsx-a11y@^6.10.0, typescript-eslint@^8.46.0) was not statically vetted against v10. If any plugin uses a v9-deprecated API that v10 removed (context.getCwd(), SourceCode#getJSDocComment(), etc.), npm run lint will throw TypeError. Acceptance criterion: npm run lint runs to completion. If it crashes, the implementer escalates and we revert to v9 (one-line change). Cost of being wrong: small.
Out-of-scope (still deferred):
- All items deferred under Decision A remain deferred.
- CI workflow changes (e.g. pinning
node-version: '20.19'for ESLint v10's stricter floor) — see Risk R9 residual concern. Pickup point: next CI-touching convoy (adopt-vitest). - Any
bump-eslint-10convoy is now closed before opening — its work is folded into this one.
Canonical authority. Same as Decision A — this Decisions entry IS the canonical authority. Decision recorded in chat on 2026-05-23 between user and role-architect, immediately after Decision A's gate-1 review surfaced finding R14.
Updated consequences for downstream roles (delta from Decision A):
role-implementer: command becomesnpm install next@^16.2.6 eslint@^10.4.0 eslint-config-next@^16.2.6. Theeslint.config.mjsshape is unchanged. New explicit acceptance check:npm run lintrunning to completion (escalate onTypeError, do not patch transitive deps).role-reviewer: lockfile diff will additionally show@eslint/eslintrcbeing removed from the dep tree (v10 dropped it). Lint baseline will shift more than under v9;|| truewrapper still tolerates.role-design-system-auditor,role-a11y-auditor,role-doc-writer: unchanged from Decision A.
C — 2026-05-23: Add typescript as a devDep (narrow scope expansion in response to implementer escalation)
Context. After Decisions A and B were applied, role-implementer ran the migration locally and npm run lint immediately crashed with Cannot find module 'typescript' during config load — before any rule executed. The implementer escalated. Root-cause diagnosis: eslint-config-next/core-web-vitals → typescript-eslint@^8.46.0 → @typescript-eslint/typescript-estree@8.59.4 does an unconditional require('typescript') at module load (verified after the fact by extracting the published typescript-estree tarball — dist/convert.js:40 and 8 other sites are top-level require('typescript') calls, none gated on try/catch or require.resolve). The peerDependenciesMeta.typescript.optional: true annotation in eslint-config-next@16.2.6's package.json only suppresses npm's install-time peer-dep warning; it does NOT make typescript runtime-optional. The architect's Boot-the-brief finding #8 misread this annotation and stated "tcg-vault is JS-only, no typescript install needed." That assumption was wrong, and the implementer caught it on first run.
This is NOT a manifestation of Risk R15 (no TypeError on a deprecated v9 API; the crash happened before any rule loaded). Reverting to ESLint v9 would not have fixed it — the same typescript-eslint chain ships with eslint-config-next@16 regardless of the ESLint major version.
The decision. User chose option (a) — add typescript as a devDep at the gate. One-line scope expansion, ~minimal-risk:
package.jsonaddsdevDependencies.typescript: "^5.9.3".package-lock.jsonregenerates accordingly. The newtypescriptsubtree is small (TypeScript itself has nodependenciesand nopeerDependencies).- Pin choice:
^5.9.3. Note:npm view typescript@latest versionreturns6.0.3(TypeScript 6 is the currentlatestmajor, contrary to the gate's parenthetical claim that 5 was latest). Latest 5.x is5.9.3. Two reasons to pin^5.9.3and defer v6: (1) honor the literal gate-1 instruction (^5); (2)typescript-eslint@8.59.4's peer range is>=4.8.4 <6.1.0— strictly,typescript@6.0.3is in range, buttypescript-eslint@8.xwas published before TS 6 GA and has not advertised explicit v6 support, so staying inside the well-trodden 5.x range is safer until a future convoy bumpstypescript-eslint.^5.9.3resolves to the latest 5.x patch. - Pin range scope: full SemVer caret (
^5.9.3), matching the convention used bynext(^15.4.2→^16.2.6) andreact(^18.3.1) elsewhere inpackage.json. - No new files. No
tsconfig.json. No.js→.tsmigration. The brief'sfiles:set is unchanged (package.jsonandpackage-lock.jsonwere already in scope as modifications). Theslice_dependenciesgraph is unchanged. - No
eslint.config.mjschange. The flat-config shape is independent of whethertypescriptis installed.
Why option b (replace eslint-config-next with a JS-only ESLint preset) was dismissed. eslint-config-next@16 does not ship a JS-only entry point. Its core-web-vitals export bundles typescript-eslint as a regular dependency (not a peer), so consumers cannot opt out without forking the package or reimplementing the rule set. Maintaining a fork is a much larger scope expansion than adding typescript as a devDep, and gives up the upstream guarantee that the rule set tracks Next.js best practices.
Why option c (keep things broken; CI's || true wrapper tolerates lint failures) was dismissed. CI's || true wrapper tolerates a non-zero exit code from eslint, but it does NOT tolerate a MODULE_NOT_FOUND thrown during config load — the crash happens before ESLint emits any structured output, and the wrapper still passes the exit code to the shell, but lint stops being a useful signal entirely. Every CI lint run would be a no-op pass. That regresses the lint surface to "always green, regardless of code quality" and silently invalidates the fix-lint-baseline convoy's premise (which assumes lint at least executes). Unacceptable.
Out-of-scope (still deferred):
- All items deferred under Decisions A and B remain deferred.
- TypeScript adoption as a project language (no
tsconfig.json, no.ts/.tsxsource files, no// @ts-checkdirectives, no.d.tsdeclaration files).typescriptis installed purely soeslint-config-next's lint chain can load. If the team later decides to migrate to TypeScript, that's an explicit, separate convoy — not a "while we're here." - Adding
@typescript-eslint/parseror@typescript-eslint/eslint-plugindirectly. They're already pulled in transitively byeslint-config-next@16; no direct dep needed. - CI workflow changes (still per Decision B's deferral note — pickup point is
adopt-vitest).
Canonical authority. This Decisions entry IS the canonical authority. Decision recorded in chat on 2026-05-23 between user and role-architect, immediately after the implementer's escalation on first lint run. Boot-the-brief #8's misreading of peerDependenciesMeta.optional is corrected in place in brief-1-bump-next-and-migrate-config.md (finding #8 marked "🔴 SUPERSEDED by Decision C"; new findings #16–#18 added under "Decision C narrow recheck").
Updated consequences for downstream roles (delta from Decision B):
role-implementer: command becomesnpm install next@^16.2.6 eslint@^10.4.0 eslint-config-next@^16.2.6 typescript@^5.9.3(or equivalently, run the previous three-package install, then runnpm install --save-dev typescript@^5.9.3as a follow-up — order doesn't matter; the lockfile is regenerated either way). Re-runnpm run lintafter the install; expectation is now that lint completes with the pre-existing baseline of errors (noCannot find module 'typescript'crash). Failure-mode classification: see Brief 1's "Local verification" section —Cannot find module 'typescript'post-install means the install didn't take and is not R15; aTypeError: context.getCwd is not a function(or similar v9-deprecated-API error) is R15 and triggers a v9 fallback (keeping thetypescriptinstall).role-reviewer: lockfile diff will now additionally show thetypescriptpackage being added. The diff is small (TypeScript has no transitive deps). Verify the brief's no-scope-expansion guardrails were respected — specifically that notsconfig.jsonwas created and no.jsfiles were renamed to.ts/.tsx.role-design-system-auditor,role-a11y-auditor,role-doc-writer: unchanged from Decisions A and B.
D — 2026-05-23: Revert ESLint v10 → v9.39.4 after R15 fired empirically; queue bump-eslint-10 as upstream-blocked follow-up
Context. After Decisions A, B, and C were applied, role-implementer ran the migration locally a second time (pass 2). The Decision-C typescript install resolved the original Cannot find module 'typescript' crash from pass 1 — but the lint run then surfaced a different TypeError:
- Crash signature:
TypeError: scopeManager.addGlobals is not a function - Call site: ESLint v10's
lib/source-code/source-code.js:221callsscopeManager.addGlobals(...)as part of v10's redesigned global-ingestion path. - Missing-method site:
@typescript-eslint/scope-manager@8.59.4(transitive dep oftypescript-eslint@8.59.4, whicheslint-config-next@16.2.6bundles as a regulardependency) does not implementaddGlobalson itsScopeManagerclass. - Why:
@typescript-eslint/scope-manager@8.xwas published Oct/Nov 2025; ESLint v10 GA was 2026-02-06. TheaddGlobalsmethod is a v10-introduced extension of theScopeManagerinterface; v9 used a different ingestion path.typescript-eslinthas not yet shipped a v10-tested release that adds the method.
This is the empirical firing of Risk R15, in a different shape than originally feared. The principal failure mode anticipated at Decision B was a v9-deprecated-API removal (context.getCwd(), SourceCode#getJSDocComment(), etc.); the actual failure was a v10-introduced-method gap on the typescript-eslint side. Either way, the diagnosis is the same: eslint-config-next@16.2.6's pre-v10-GA bundled plugin set is not runtime-compatible with v10. Reverting eslint to v9 is the only working option until upstream catches up.
The decision. User chose option (a) — execute the brief's documented v9 fallback path AND formally queue a follow-up bump-eslint-10 convoy at the gate. This partially reverses Decision B's "avoid back-to-back convoys" rationale, but Decision B was correct given Boot-the-brief evidence at the time — the v10 incompat was statically unprovable until a real lint run hit addGlobals. Empirical evidence from pass 2 reverses the call.
Specific changes:
package.json: re-pindevDependencies.eslintfrom"^10.4.0"back to"^9.39.4"(npm'smaintenancedist-tag).devDependencies.typescript: "^5.9.3"(Decision C) is RETAINED. Boot-the-brief recheck #17 confirmed at Decision C, and the implementer's pass-2 evidence reconfirmed, that the sametypescript-eslint@8.59.4chain hard-requirestypescriptunder v9 too. The typescript install is correct independent of the eslint pin.eslint-config-next@^16.2.6is unchanged — its peer-depeslint: ">=9.0.0"accepts v9.39.4 trivially; no<10upper bound shift since Decision B's verification.next@^16.2.6is unchanged.eslint.config.mjsshape is unchanged.defineConfigandglobalIgnoresfromeslint/configwere introduced in 9.21.0 and retained in v10.4.0; the same import line resolves correctly on both v9.39.4 and v10.4.0. This is the load-bearing reason Decision D is a one-linepackage.jsonre-pin and not a multi-file rollback. Whenbump-eslint-10lands, this file should not need to change.- Install command for the implementer:
npm install --save-dev eslint@^9.39.4 eslint-config-next@^16.2.6 typescript@^5.9.3(single atomic command preferred; running them separately is equivalent — the lockfile regenerates either way).
Why option b (npm overrides to force a v10-compat @typescript-eslint/scope-manager) was dismissed. The brief explicitly forbids transitive patching ("do NOT patch the plugin or pin transitive deps mid-flight"). There's no guarantee that any released version of @typescript-eslint/scope-manager exists with the v10 fix at a version eslint-config-next@16.2.6 will resolve to under its bundled typescript-eslint@^8.46.0 constraint. Even if such a version existed, npm overrides bypass the upstream maintainer's compatibility testing entirely — we'd be hand-rolling a custom dep tree that no other project uses, which moves the maintenance burden to us.
Why option c (wait for upstream) was dismissed. Doesn't unblock the Vercel deploys gate that's the convoy's success metric ("npm install next@16.2.6 ships, Vercel deploys complete, no runtime regressions in dev or build"). The convoy's premise is that Vercel was rejecting next@^15.4.2 as a "Vulnerable version of Next.js"; we have to ship next@^16.2.6 now. Pinning eslint to v9 lets us do that today; v10 can land later when prerequisites are met.
Why option d (ship with the lint crash) was dismissed. Same reasoning as Decision C's option-c dismissal: CI's || true wrapper tolerates a non-zero exit code, but a TypeError crash before any rule executes means lint emits no useful signal at all — every CI lint run becomes a no-op pass, and fix-lint-baseline's premise (lint at least executes) collapses. Unacceptable.
Reversal of Decision B's "avoid back-to-back bump-eslint-10 convoy" rationale. Acknowledged. Decision B's argument was: pivot to v10 now to avoid a follow-up convoy. That argument was correct given the static evidence available at the time of Decision B (peer-dep ranges accepted v10; Boot-the-brief found no obvious incompatibilities). Decision D's empirical evidence — a TypeError from a real lint run — supersedes the static evidence. The follow-up bump-eslint-10 convoy is now formally queued (see "Follow-up convoys queued" section below); it will land as a single-brief mechanical bump once upstream prerequisites are met.
Out-of-scope (still deferred):
- All items deferred under Decisions A, B, and C remain deferred.
- The
bump-eslint-10follow-up is queued, not authored. The conductor will materialize a convoy file when a human authors one (likely aftertypescript-eslintships a v10-tested release andeslint-config-nextbundles it). - npm
overridesfield manipulation, transitive-dep pinning, plugin forking — all forbidden as scope expansion under both this convoy and the futurebump-eslint-10.
Canonical authority. This Decisions entry IS the canonical authority. Decision recorded in chat on 2026-05-23 between user and role-architect, immediately after the pass-2 implementer escalation surfaced R15's addGlobals firing.
Updated consequences for downstream roles (delta from Decision C):
role-implementer: re-runnpm install --save-dev eslint@^9.39.4(the other three packages —next@^16.2.6,eslint-config-next@^16.2.6,typescript@^5.9.3— are already at correct versions from Decision C's run); re-runnpm run lint. Expectation: exit code 1 or 2 (baseline lint errors present) is fine; exit code 0 is improbable untilfix-lint-baseline. NOT expected: aTypeErrorcrash. R15'sscopeManager.addGlobalssignature should not recur on v9 because v9 doesn't calladdGlobals. If a differentTypeErrorfires on v9, escalate via the brief's failure-mode classifier.role-reviewer: lockfile diff under Decision D stays on the v9 dep-tree;@eslint/eslintrc(a v9 transitive) remains present. Verify thatpackage.jsonhaseslint: "^9.39.4"(not^10.x),typescript: "^5.9.3",eslint-config-next: "^16.2.6", andnext: "^16.2.6".role-design-system-auditor,role-a11y-auditor,role-doc-writer: unchanged from Decisions A, B, C. Doc-writer'sAGENTS.mdpass should mention thebump-eslint-10queued convoy if the doc-writer pass surfaces lint-toolchain documentation.- Future
bump-eslint-10implementer: inherits R15's diagnostic verbatim. The convoy will become a single-brief mechanical bump oncetypescript-eslintships v10 support andeslint-config-nextbundles it; until then, the convoy is upstream-blocked and not authored.
Follow-up convoys queued
The following convoys are formally queued by role-architect as upstream-blocked follow-ups to this convoy. They are NOT authored as convoy files yet — they exist in this list only. The conductor will materialize a convoy file when a human authors one and the upstream prerequisites are met.
bump-eslint-10 — upstream-blocked
- Origin. Queued under Decision D (2026-05-23) after R15 fired empirically. Decision B's pivot to v10 was reverted; v10 is still the supported
latestand we want to land it eventually. - Prerequisites (both must be met before the convoy can run):
typescript-eslintships a v10-tested release. Likely shape:@typescript-eslint/scope-manageradds theaddGlobalsmethod (and any other v10-introducedScopeManagerinterface members) on the v8.x line as a backport, OR the typescript-eslint v9 line ships and adds them. Either way, the release notes will explicitly mention ESLint v10 compatibility.eslint-config-nextbundles a v10-testedtypescript-eslint. Likely shape: a16.3+release that bumps thetypescript-eslintdirect dependency. Confirmed by reading theeslint-config-nextpackage.jsondependencies.typescript-eslintrange and cross-referencing typescript-eslint's release notes.
- Convoy shape (when materialized): single-brief mechanical bump matching this convoy's Brief 1 shape. Files:
package.json(re-pineslintfrom^9.39.4to^10.x.y; re-pineslint-config-nextif a new minor bundles the v10-tested typescript-eslint),package-lock.json(regenerate). No code-shape changes expected —eslint.config.mjsis verified compatible on both v9 and v10. No CI workflow changes unless the Node-version pin question (originally raised under R9) bites at v10's stricter floor. - Risks inherited from this convoy: R12's "lint baseline shifts more under v10" warning resurfaces; the three new
eslint:recommendedrules, JSX reference tracking,no-shadow-restricted-names.reportGlobalThis: truedefault, andeslint-env-comments-as-errors transition all happen at this convoy. CI's|| truewrapper still tolerates. R9's CI moving-target concern (node-version: '20'resolution) reactivates under v10's^20.19.0floor; thebump-eslint-10brief should pin CI tonode-version: '20.19'or'lts/iron'if the target convoy permits CI workflow changes. - Cost of being wrong about prerequisites: small. If
typescript-eslintships a v10-tested release andeslint-config-nextbundles it butbump-eslint-10still surfaces a different incompat at runtime, the convoy itself documents another decision letter and re-pins back to v9 again. The cost is one extra Boot-the-brief recheck and one extra Decisions entry.
bump-typescript-6 — upstream-blocked
- Origin. Queued under Decision D (2026-05-23). Surfaced during Decision C's Boot-the-brief recheck (#16):
npm view typescript@latestreturned6.0.3(latest major), but^5.9.3was pinned becausetypescript-eslint@8.59.4's peer range>=4.8.4 <6.1.0accepts but doesn't certify v6. - Prerequisites:
typescript-eslintadvertises explicit v6 support in a release. Currently the peer range accepts<6.1.0(sotypescript@6.0.xis technically in range) but typescript-eslint has not announced v6 testing. Likely shape: a release-notes entry titled "TypeScript 6 support" or a peer-range bump to<7.0.0once they're confident.- (Optional)
eslint-config-nextbundles atypescript-eslintversion that advertises v6 support. Not strictly required —typescript@^5.xinbump-eslint-10andtypescript@^6.xhere can be re-pins on different days.
- Convoy shape (when materialized): single-brief mechanical bump. Files:
package.json(re-pintypescriptfrom^5.9.3to^6.x.y),package-lock.json(regenerate). No code-shape changes — TypeScript is only used by ESLint's lint chain, not by source files (notsconfig.json, no.tsfiles; thebump-typescript-6convoy must preserve those guardrails). - Risks inherited from this convoy: none specific. The verified runtime require evidence in Brief 1 #17 stays valid (typescript-estree's
require('typescript')sites are unconditional regardless of TS major). - Cost of being wrong about prerequisites: small. Same fallback shape as
bump-eslint-10.
Notes on materialization
These two convoys can land independently, in either order. Neither blocks the other. The conductor should expect a human to author the convoy file (frontmatter + IA + UX) when they decide to land the upgrade; role-architect does not pre-author convoy files for upstream-blocked follow-ups (no Boot-the-brief evidence exists yet to verify against).