Examples
Every result on this page was captured from the running service, not written by hand. They show what a plan looks like and how each decision is explained.
A sample file
The sample from the web tool, with a gate of 7 days and the largest step minor. Captured on 2026-10-26.
{
"name": "my-app",
"version": "1.0.0",
"dependencies": {
"axios": "^1.6.0",
"express": "^4.18.2",
"lodash": "^4.17.20",
"zod": "^3.22.0"
},
"devDependencies": {
"@types/node": "^18.19.130",
"eslint": "^9.10.0",
"prettier": "^3.1.0",
"typescript": "^5.3.0"
},
"engines": {
"node": ">=20"
}
}The decision for every package:
upgrade axios ^1.6.0 -> ^1.20.0
upgrade express ^4.18.2 -> ^4.22.3
upgrade lodash ^4.17.20 -> ^4.18.1
upgrade zod ^3.22.0 -> ^3.25.76
upgrade @types/node ^18.19.130 -> ^20.19.43
held eslint ^9.10.0 -> ^9.10.0
upgrade prettier ^3.1.0 -> ^3.9.10
upgrade typescript ^5.3.0 -> ^5.9.3Each decision comes with one sentence. Three of them, to show the range of things they say: the plain case, a newer major that was not taken, and @types/node following engines.node:
axios 1.6.0 -> 1.20.0 (minor step, 61 days old, age gate 7); newest version that qualifies
express 4.18.2 -> 4.22.3 (minor step, 42 days old, age gate 7); newest version that qualifies; not taken: express@5.3.0 is a major step from 4.18.2, above the allowed minor level
@types/node 18.19.130 -> 20.19.43 (major step, 137 days old, age gate 7); newest version that qualifies; not taken: @types/node follows engines.node (lowest supported major 20)How to read them is on how it works, and the terms are in the glossary.
Majors move together
A framework and the packages it depends on cannot be moved one by one. With major steps allowed, next and react are chosen together. Captured on 2026-10-24:
{
"name": "web",
"dependencies": {
"next": "^15.0.0",
"react": "^18.3.1",
"react-dom": "^18.3.1"
}
}@@ -1,8 +1,8 @@ {
"name": "web",
"dependencies": {
- "next": "^15.0.0",- "react": "^18.3.1",- "react-dom": "^18.3.1"+ "next": "^16.4.2",+ "react": "^19.3.1",+ "react-dom": "^19.3.1" }
}
The newest next was released the same day, so it was left out by the gate:
next 15.0.0 -> 16.4.2 (major step, 8 days old, age gate 7); newest version that qualifies; not taken: next@16.4.3 is 0 days old, the age gate is 7 daysWhat is held back
The same file with the step limited to minor. next moves inside its major. The other two stay, and the answer says what stood in the way:
upgrade next ^15.0.0 -> ^15.5.28
held react ^18.3.1 -> ^18.3.1
held react-dom ^18.3.1 -> ^18.3.1next 15.0.0 -> 15.5.28 (minor step, 9 days old, age gate 7); newest version that qualifies; not taken: next@16.4.3 is a major step from 15.0.0, above the allowed minor level
react held at 18.3.1; not taken: react@19.3.1 is a major step from 18.3.1, above the allowed minor levelA held package is a decision, not a failure. Raise the step for a run when you want the major, as in theprevious example.
One package
Name the package you need and only it moves, together with the neighbours it requires. Everything else is listed as outside-focus and stays as it is. Here the focus is axios with a gate of 0 days, as you would run it for a fix you need today:
upgrade axios ^1.6.0 -> ^1.20.0
outside-focus express ^4.18.2 -> ^4.18.2
outside-focus lodash ^4.17.20 -> ^4.17.20
outside-focus zod ^3.22.0 -> ^3.22.0
outside-focus @types/node ^18.19.130 -> ^18.19.130
outside-focus eslint ^9.10.0 -> ^9.10.0
outside-focus prettier ^3.1.0 -> ^3.1.0
outside-focus typescript ^5.3.0 -> ^5.3.0With a focus on zod and a gate of 7 days, zod is the only line that changes:
outside-focus axios ^1.6.0 -> ^1.6.0
outside-focus express ^4.18.2 -> ^4.18.2
outside-focus lodash ^4.17.20 -> ^4.17.20
upgrade zod ^3.22.0 -> ^3.25.76
outside-focus @types/node ^18.19.130 -> ^18.19.130
outside-focus eslint ^9.10.0 -> ^9.10.0
outside-focus prettier ^3.1.0 -> ^3.1.0
outside-focus typescript ^5.3.0 -> ^5.3.0More on this in focus and in the guide take one package now.
Checked on real projects
The planner is also run against the package.json files of public projects, and its answers are compared with an independent reference written separately from the planner. The first run was on 2026-10-24 with ten repositories: axios, express, fastify-cli, a Redux realworld example, the NestJS starter, zustand, the Remix indie stack, shadcn taxonomy, json-server and a Next.js subscription template. Each file was planned at all three step levels, 30 plans, and again with a gate of 7 days.
- All plans were answered, and a second run gave identical plans.
- No chosen version broke a rule: every one existed, was stable, within the step, old enough and compatible with Node.
- Every package that ended below the newest candidate had an independent reason, such as a peer, an aligned dependency or
engines.node. - An independent search for a better combination found none. In 28 of 30 runs the two agreed on the optimum; in the other two the reference ran out of time, which proves nothing either way.
The run also found real problems: a step that did not mean what it said at patch, peers on @types packages that were not checked, and a tie that was broken too coarsely. They were fixed and the run was repeated. One result is still not guaranteed optimal because the search budget ran out (meta.exact says so).

This is a check on ten projects at one point in time, not a proof. The procedure is repeatable, and the algorithm it checks is described step by step on how it works. Try it on your own file in the web tool.
