Skip to content

Toolchain version matrix

The Enkidu monorepo runs seven packages (rails_api, design_system, admin_dashboard, ionic_frontend, agent_runner, docs_site, guide_site) that had drifted onto different majors/minors of the same core dependencies. The v4.24 Dependency Convergence & Upgrade milestone (OBJ-1891) is bringing every package to one shared, newest-stable matrix, phase by phase. This page is the human-facing summary; the machine-of-record pins (with sourcing notes) live in .planning/milestones/v4.24-version-matrix.md.

Rule: target = newest published stable release, except where a dependency's most-advanced sibling already in this repo caps it below the absolute newest major — no chasing a hypothetical next-major nobody in the repo runs yet.

Target matrix (OBJ-1893, Phase 1)

DependencyCurrent (most-advanced sibling)TargetApplies to
Ruby4.0.2 4.0.6 — landed, Phase 2 (OBJ-1894, PR #1878)4.0.6rails_api
Rails8.1.2.18.1.3.1rails_api
Nodemixed 20 / 22 across CI + Docker24 (24.18.1 latest patch, LTS "Krypton")all 7 packages
Vue3.5.32 3.5.40 — landed, Phase 4 (admin_dashboard, ionic_frontend, OBJ-1896, PR #1881/#1882)3.5.40design_system, admin_dashboard, ionic_frontend, doc sites
vue-router5.0.4 5.2.0 — landed, Phase 4 (admin_dashboard); 4.2.0 5.2.0 — landed, Phase 4 (ionic_frontend, OBJ-1896, PR #1881/#1882)5.2.0admin_dashboard, design_system, ionic_frontend — all landed
Vite8.0.16 8.1.5 — landed, Phase 4 (admin_dashboard, PR #1881); stays 7.3.5 by design (ionic_frontend, NEGATIVE verdict — see below)8.1.5design_system, admin_dashboard — landed; ionic_frontend deliberately not converged, see Phase 4
TypeScript5.9.x 6.0.2 — landed, Phase 4 (ionic_frontend, OBJ-1896, PR #1882) and Phase 5 (agent_runner, OBJ-1897, PR #1880); already at matrix (admin_dashboard)6.0.2admin_dashboard, agent_runner, ionic_frontend — all landed
vue-tsc3.2.6 3.3.8 — landed, Phase 4 (admin_dashboard); 2.1.10 3.3.8 — landed, Phase 4 (ionic_frontend, OBJ-1896, PR #1881/#1882)3.3.8design_system, admin_dashboard, ionic_frontend — all landed
vitest4.1.4/4.1.8 4.1.10 — landed, Phase 4 (admin_dashboard, ionic_frontend, OBJ-1896, PR #1881/#1882) / latest (agent_runner, floating)4.1.10admin_dashboard, ionic_frontend — landed; agent_runner
vitepress2.0.0-alpha.17 (both doc sites)2.0.0-alpha.18 ✅ shipped Phase 5docs_site, guide_site — npm's latest dist-tag is still 1.6.4 (2.0 hasn't reached GA); both sites already track the 2.0 alpha line, so the target stays on that track rather than "downgrading" to the tagged latest. Do not "upgrade" either doc site to the npm latest 1.6.4 tag — that would be a downgrade off the 2.0 alpha line both sites intentionally run.

Node retargeted mid-phase (22 → 24): Phase 1 originally normalized on Node 22, but 22 is Maintenance LTS with only ~9 months left before EOL (2027-04-30) — landing on it would front-load another forced Node bump almost immediately. Node 24 is the current Active LTS (EOL 2028-04-30) and every toolchain in this matrix already supports it, so the target moved to 24 before this branch merged. Node 26 was considered and rejected: still on the Current line, not Active LTS until October 2026.

Phase 1 landed the Node 24 normalization and agent_runner's exact vitest/vite pins. Phase 2 landed the Ruby 4.0.6 bump (Rails was already at the pinned 8.1.3.1 from a prior security PR, #1872). Phase 5 has since landed agent_runner's TypeScript 6.0.2 bump and the docs_site/guide_site vitepress alpha.18 + Vue 3.5.40 align (see below). Phases 3–4 apply the rest of this matrix to their respective packages; see .planning/milestones/v4.24-dependency-convergence-ROADMAP.md for the phase breakdown.

Per-major cost-benefit verdicts

Three breaking majors gate how far ionic_frontend converges in Phase 4 (admin_dashboard + ionic_frontend). Each got an explicit effort/benefit/risk pass against ionic_frontend's actual config, not just the changelog in the abstract.

vue-router 4 → 5 — POSITIVE

Vue Router 5 is a transition release: no breaking changes for a project using plain createRouter, which is what ionic_frontend/src/router/index.ts does — the only breaking surface is for projects using unplugin-vue-router's file-based routing, and this repo doesn't use it. admin_dashboard already runs vue-router@5.0.4 in the same monorepo tooling, which de-risks the jump further.

  • Effort: near-zero — no code changes required for standard router usage.
  • Benefit: closes the widest version gap in the matrix (^4.2.05.2.0) and buys prep time ahead of Vue Router 6, which will be ESM-only and drop deprecated APIs outright.
  • Verdict: upgrade in Phase 4.

Vite 7 → 8 — NEGATIVE

Vite 8 replaces the esbuild (dev) + Rollup (build) split with a single Rolldown/Oxc-based bundler, and that shows up as concrete required changes in ionic_frontend/vite.config.ts specifically:

  • A top-level esbuild: { supported: { destructuring: true } } block and optimizeDeps.esbuildOptionsesbuild config renames to oxc in Vite 8.
  • build.rollupOptions.output.manualChunks using the object form (six named vendor chunks: vendor-ionic, vendor-apollo, vendor-clerk, vendor-sentry, vendor-charts, vendor-vue) — Vite 8 drops the object form of manualChunks and renames rollupOptions to rolldownOptions.
  • The optimizeDeps.exclude: ['@ionic/pwa-elements'] workaround exists because Vite's dependency optimizer previously flattened that package and broke its internal relative dynamic imports (documented in the file's own comment). Swapping the optimizer's underlying engine is exactly the kind of change that can resurface or alter that class of bug, and it's a Capacitor/PWA-elements interaction that's hard to verify outside a real device build.

design_system and admin_dashboard already run vite@8.0.16 cleanly, but neither uses rollupOptions/manualChunks/custom esbuild config or ships vite-plugin-pwa — their success doesn't cover ionic_frontend's actual surface. The upstream-recommended path is a two-step migration (adopt the rolldown-vite package on Vite 7 first to isolate bundler-specific breakage, then move to Vite 8), not a direct bump.

  • Effort: real — three config sections need rewriting, plus Capacitor/PWA regression testing.
  • Risk: the highest of the three, on the app that carries production mobile builds.
  • Verdict: stays out of Phase 4. ionic_frontend remains on Vite 7.3.5 (bumped only as far as the latest 7.x patch, if any) until a dedicated follow-up phase can do the two-step migration with real-device smoke coverage. This is a deliberate partial convergence, not a gap.

TypeScript 5 → 6 — POSITIVE

TypeScript 6.0's headline breaking changes are default flips: strict mode on by default, target defaults to es2025 (ES5 support dropped), module defaults to esnext, and classic moduleResolution: "node" (a.k.a. Node10) is deprecated. ionic_frontend/tsconfig.json already opts into strict: true, target: "ESNext", and moduleResolution: "bundler" explicitly — the default flips are no-ops here.

The one concrete fix needed: ionic_frontend/tsconfig.node.json (scoped to vite.config.ts only) still sets "moduleResolution": "Node" — the deprecated classic resolution — and needs to move to "bundler", matching the main tsconfig.json and both doc-sibling packages. admin_dashboard already runs TypeScript 6.0.2 in production with a still-deprecated baseUrl in its tsconfig.app.json, confirming deprecated (not yet removed) options don't hard-fail on 6.0. TypeScript 6.0 also ships a --ts6-migration flag that statically scans for other breaking patterns before the bump.

  • Effort: small and contained — one line in one file that only type-checks the Vite config, plus a --ts6-migration pre-flight scan.
  • Benefit: closes drift (~5.9.06.0.2, matching admin_dashboard) and lands on TS 6.0 — documented as the last JavaScript-based compiler release before the Go-native TypeScript 7 rewrite — rather than skipping straight to a much larger 5→7 leap later.
  • Verdict: upgrade in Phase 4.

What this means for Phase 4

Per the milestone's locked decision, a NEGATIVE verdict means that major stays out of scope and ionic_frontend converges only as far as the POSITIVE verdicts allow — not silently, but as a documented, deliberate partial convergence. Phase 4 should ship: Vue 3.3→3.5, vue-router 4→5, vue-tsc 2.1→3.3, TypeScript 5.9→6.0. Vite stays at 7.x pending a dedicated follow-up phase.

Node 24 normalization (shipped, Phase 1)

Every .nvmrc, every package.json engines.node, and both Node-based Dockerfiles (agent_runner, ionic_frontend) now read Node 24. The sweep ended up wider than the original 20-file touched-set: docs_site/guide_site and 18 more CI workflows were already on 22 before Phase 1 started and weren't part of the initial 20→22 pass, so they're repinned to 24 here too rather than left as a new split. See the CHANGELOG entry below for the full file list.

One deliberate, permanent exception: Firebase-deploy steps stay on Node 20. firebase-tools 15.22.x's WIF/ADC credential exchange fails on Node ≥22 ("Failed to authenticate") — this is a firebase-tools bug (see firebase/firebase-tools#10726), not a repo config issue, and it isn't fixed upstream yet. Every job that runs npx firebase-tools deploy sets up Node 20 immediately before the deploy step, even though the rest of that same job (and every other job in the file) builds on 24:

  • .github/workflows/docs.yml, guide.yml — a dedicated second "Set up Node 20 for Firebase deploy" step right before the deploy, after an earlier Node-24 build step.
  • .github/workflows/admin.yml, api-docs.yml (deploy-api-docs job only — validate-api-docs stays on 24, it never deploys), design-system.yml (deploy-design-system job only — validate-design-system stays on 24), preview.yml, production.yml, rollback.yml, staging.yml (deploy-frontend job only — the unrelated admin-dashboard-playwright-smoke job in the same file stays on 24, it has no Firebase deploy step), storybook.yml — one shared Setup Node step per deploy job serves both the build and the deploy, so the whole job pins to 20.

A future Node sweep (Phase 5 or later) should not blindly flip these 10 steps to whatever the new target major is — check whether firebase-tools has fixed the WIF/ADC Node ≥22 failure first (firebase/firebase-tools#10726), and only move them once it's confirmed fixed.

Ruby 4.0.6 (shipped, Phase 2)

rails_api/.ruby-version, rails_api/Dockerfile, and all 3 rails_api CI jobs in .github/workflows/ci.yml now read Ruby 4.0.6. Rails itself needed no change — a prior security PR (#1872, CVE-2026-66066) had already landed the Phase 1 target, Rails 8.1.3.1. rails_api/Gemfile.lock's RUBY VERSION stanza still reads ruby 4.0.0; this is pre-existing drift (bundle install doesn't rewrite it under the repo's ~> 4.0 constraint) unrelated to this bump, tracked as a follow-up (bundle lock --update-ruby), not blocking.

admin_dashboard + ionic_frontend (shipped, Phase 4)

Both design_system consumers now converge onto the Phase 3 foundation (OBJ-1896, PR #1881 admin, PR #1882 ionic).

admin_dashboard — light patch bump onto the already-modern matrix: vue 3.5.32→3.5.40, vue-router 5.0.4→5.2.0, vite 8.0.16→8.1.5, vue-tsc 3.2.6→3.3.8, vitest/coverage-v8 4.1.4→4.1.10. TypeScript was already ~6.0.2, unchanged. package.json + lockfile only, no source changes.

ionic_frontend — the heavy laggard crosses both POSITIVE-verdict majors: vue-router 4.2.0→5.2.0 and typescript 5.9→6.0.2, alongside vue 3.3.0→3.5.40 and vue-tsc 2.1.10→3.3.8. The vue-router 4→5 jump needed no code remediation — confirmed against the official migration guide, v5 is a transition release with no breaking changes for apps using explicit route config (not unplugin-vue-router file-based routing), which is how ionic_frontend/src/router/index.ts is built. TypeScript 6 + vue-tsc 3's stricter checking surfaced two real latent type gaps, fixed rather than suppressed: a currentUserId prop normalized with || null in TimelineEvent.vue (matching the existing pattern in sibling components that feed the same ReactionPicker prop), and a /// <reference types="node" /> directive added to build-fallback-pool.ts for its Node CLI fs/path/process usage.

ionic_frontend deliberately stays on Vite ^7.3.5 — the Vite 7→8 NEGATIVE cost-benefit verdict from Phase 1 held. The verdict's premise (ionic can take every other Phase 4 target while staying on Vite 7) was verified, not assumed: the app builds and tests clean against Vue 3.5, vue-router 5, TypeScript 6, vue-tsc 3, and vitest 4.1.10, all on Vite 7 — no hard Vite-8-only incompatibility surfaced, so no escalation back to Orion was needed. See Vite 7 → 8 — NEGATIVE above for the full rationale (Rolldown/Oxc config rewrite, Capacitor/PWA-elements regression risk); this is the milestone's fidelity contract in effect — a documented partial convergence, not a silently skipped major.

Desi's post-upgrade visual-smoke pass confirmed no regressions on either app — TimelineEvent (the only ionic component with a source change) renders pixel-identical to master in light and dark.

agent_runner + doc sites (shipped, Phase 5)

agent_runner moved to TypeScript 6.0.2 (Node 24 Docker toolchain and the vitest/vite pins were already done in Phase 1 and re-verified, not redone). docs_site and guide_site moved to vitepress 2.0.0-alpha.18 and Vue 3.5.40, matching this matrix's alpha-track special case above — neither site "upgraded" to npm's latest 1.6.4 tag, which would have been a downgrade off the 2.0 alpha line. tsc/vitest are green in CI for agent_runner; both doc sites build and preview clean and their doc-build scripts (docs:graphql, diagram render, route-drift) still run. See PR #1880.

See also

  • .planning/milestones/v4.24-version-matrix.md — machine-of-record pins with full sourcing notes (registry/API citations).
  • .planning/milestones/v4.24-dependency-convergence-ROADMAP.md — phase-by-phase plan and the milestone's locked decisions.
  • npm Override Floor Gotcha — a related dependency-pinning trap (security override floors), not covered by this matrix.

Last updated: 2026-07-30 (OBJ-1897: Phase 5 of v4.24 — agent_runner TS6 + doc-site vitepress/Vue align shipped, PR #1880)

Loading…