Public: anyone can read this; the crew can change it.
The roll recipe computes whether a surge pod can schedule, and prints the headroom it lacks #1640
Check · Review · Land · Live done — Landed and live in production.
Before a roll, the operator can now check whether a second forge pod fits on the volume's node beside the running one. When it doesn't, the check prints how much more CPU and memory the node would need for a zero-downtime roll. It only reads what `kubectl` printed and never contacts the cluster.
shipped production is serving it deploy 2fa0c24
Approved. This is an operator script and recipe with no page, endpoint or terminal surface, so the security, both-doors and preview items do not apply. It reads local files only, runs no shell, and validates every input before judging. Exit codes 0, 3 and 2 are tested, along with the hand-worked 8 GB case, the exact-fit boundary and the refusal paths. The new test is in the gate, the chart and release workflow are untouched, and there are no new dependencies. Next two: (1) in scripts/roll.ts, say in the report when the forge pod's own steady memory is being compared against free that already excludes it, so an operator does not double-count; (2) have the recipe in docs/ops/ROLL.md pipe kubectl top into --steady, so the steady figure is not typed by hand and gone stale.
Before a roll, the operator can now check whether a second forge pod fits on the volume's node beside the running one. When it doesn't, the check prints how much more CPU and memory the node would need for a zero-downtime roll. It only reads what
kubectlprinted and never contacts the cluster.What to look at
scripts/roll.ts: the pure module. It counts whole millicores and bytes, rounding up the way the scheduler does.- Free: the node's allocatable minus the requests of every pod on it that hasn't Succeeded or Failed. A pod's request includes init containers, sidecars and runtime overhead. - Need: the forge pod's CPU request, and the larger of its memory request and its steady memory. - Answer:
schedulable(fits on requests alone),fits(also fits once both pods hold their steady memory), andshortfall(the missing headroom, never below zero). - Input that isn't whatkubectlprints is refused with a reason.scripts/roll-preflight.ts: the command the recipe runs. It readsnode.json,pods.json,--pod ns/nameand--steady, then prints the answer (or--json). Exit 0 means it fits, 3 means it doesn't, 2 means bad input.docs/ops/ROLL.md: the roll recipe. It gives thekubectlcommands that collect the inputs, runs the check before rolling, and explains the exit codes, the rule and a worked 8 GB example.test/roll.test.ts: fixtures that fit, fit exactly and miss by one byte, schedule on the request but not on steady memory, and fall short on CPU alone; plus refused inputs, the command's exit codes, and a check that the chart still hasmaxSurge: 1, maxUnavailable: 0. Each number is worked by hand beside it. The file is added toGATEinscripts/gate.ts;bun scripts/flake-hunt.ts admit test/roll.test.tsgave 10/10 green runs in random order under load.Not done here
docs/ops/ROLL.mdandroll-preflightserve as the roll recipe.Try it: run
bun scripts/roll-preflight.ts --node node.json --pods pods.json --pod forge/<pod> --steady 3712Mion the output of the commands indocs/ops/ROLL.md.bun x tsc --noEmitis clean, andtest/roll.test.tsplus the five cross files (288 tests) pass.🤖 Generated with Claude Code
Closes #1588