Summary
On a live install, /usr/bin/wp-codebox is a symlink to the workspace primary checkout's built dist (/var/lib/datamachine/workspace/wp-codebox/packages/cli/dist/index.js), not an npm-global package. There is currently nothing that detects or prevents that primary from drifting behind origin/main, and nothing that rebuilds dist/ when it does. The result: a fixed bug can stay broken in production indefinitely because the binary points at stale, never-rebuilt source.
This issue tracks adding skew detection and/or auto-rebuild so a stale primary can't silently serve an out-of-date CLI.
How it bit us (concrete incident)
Production was emitting plain-text errors on the recipe-run --json error path instead of a JSON envelope, which broke a PHP caller doing json_decode(stdout) with did not return valid JSON: Syntax error. Repro on the stale binary:
$ wp-codebox recipe-run --recipe /tmp/x.json --artifacts /tmp/a --json
Unsupported recipe schema in /tmp/x.json # plain text, not JSON, exit 1
$ wp-codebox recipe-run --json
Missing required option: --recipe # plain text, not JSON
The actual code fix for this had already landed on main in commit 3118a438 ("Add command artifact schema contracts (#1059)", 2026-06-16) — cli-main.ts's error sink already consults wantsJsonOutput(args) and calls writeJsonFailure(...), and regression tests (cli-json-failure-smoke, cli-unsettled-command-smoke) are in the check smoke group.
But the primary checkout was pinned at eb9a717f (2026-06-15) with a 2026-06-15 dist/, i.e. one day before the fix:
git merge-base --is-ancestor 3118a438 HEAD → NO (fix not in primary source)
grep -c "wantsJsonOutput(args)" packages/cli/src/cli-main.ts → 0
dist/cli-main.js built 2026-06-15, no wantsJsonOutput
So /usr/bin/wp-codebox kept serving the pre-fix behavior even though main had been fixed. Meanwhile the deployed WP plugin's recipe-writer had moved ahead, which is what surfaced the Unsupported recipe schema mismatch in the first place.
Resolution applied to the affected host (manual, not a code change)
workspace git pull wp-codebox --allow-primary-refresh (fast-forward eb9a717f → 700fe40d, clean tree). Fix 3118a438 now in source.
npm run build → fresh dist/ carrying wantsJsonOutput in dist/cli-main.js.
- Verified the live
/usr/bin/wp-codebox:
recipe-run --json (missing recipe) → JSON envelope to stdout, exit 1 ✅
recipe-run --recipe <bad-schema> --json → JSON envelope to stdout, exit 1 ✅
recipe-run (no --json) → plain text to stderr, stdout empty, exit 1 (regression preserved) ✅
That fixed this host, but nothing stops it recurring on any host whose primary drifts.
Proposed work (this issue)
Pick one or both:
-
Skew detection in doctor. Have wp-codebox doctor compare the running binary's source revision against origin/main (or the configured tracked ref) and flag/warn when the primary that backs the binary is behind, or when dist/ is older than src/. The binary already reports a source fingerprint in doctor — extend it to a freshness assertion.
-
Auto-rebuild on primary refresh. When the primary checkout is refreshed (--allow-primary-refresh), rebuild dist/ so the symlinked binary can't lag its own source. At minimum, fail loudly if dist/ is stale relative to src/.
Acceptance criteria
- A stale primary (HEAD behind tracked ref, and/or
dist/ older than src/) is detected and surfaced, not silently served.
- Refreshing the primary leaves
/usr/bin/wp-codebox running the refreshed source (either by rebuild or by a hard error that forces one).
- No behavior change when the primary is already fresh.
Notes
- This is an operational/tooling gap, not a defect in the CLI's runtime behavior — the JSON-envelope fix itself is already correct and tested on
main.
- The
/usr/bin/wp-codebox → primary-dist/index.js symlink model is the load-bearing detail: it means "deploy" and "binary freshness" are decoupled, so a fresh deploy of the plugin does not imply a fresh CLI binary.
Summary
On a live install,
/usr/bin/wp-codeboxis a symlink to the workspace primary checkout's built dist (/var/lib/datamachine/workspace/wp-codebox/packages/cli/dist/index.js), not an npm-global package. There is currently nothing that detects or prevents that primary from drifting behindorigin/main, and nothing that rebuildsdist/when it does. The result: a fixed bug can stay broken in production indefinitely because the binary points at stale, never-rebuilt source.This issue tracks adding skew detection and/or auto-rebuild so a stale primary can't silently serve an out-of-date CLI.
How it bit us (concrete incident)
Production was emitting plain-text errors on the
recipe-run --jsonerror path instead of a JSON envelope, which broke a PHP caller doingjson_decode(stdout)withdid not return valid JSON: Syntax error. Repro on the stale binary:The actual code fix for this had already landed on
mainin commit3118a438("Add command artifact schema contracts (#1059)", 2026-06-16) —cli-main.ts's error sink already consultswantsJsonOutput(args)and callswriteJsonFailure(...), and regression tests (cli-json-failure-smoke,cli-unsettled-command-smoke) are in thechecksmoke group.But the primary checkout was pinned at
eb9a717f(2026-06-15) with a 2026-06-15dist/, i.e. one day before the fix:git merge-base --is-ancestor 3118a438 HEAD→ NO (fix not in primary source)grep -c "wantsJsonOutput(args)" packages/cli/src/cli-main.ts→ 0dist/cli-main.jsbuilt 2026-06-15, nowantsJsonOutputSo
/usr/bin/wp-codeboxkept serving the pre-fix behavior even thoughmainhad been fixed. Meanwhile the deployed WP plugin's recipe-writer had moved ahead, which is what surfaced theUnsupported recipe schemamismatch in the first place.Resolution applied to the affected host (manual, not a code change)
workspace git pull wp-codebox --allow-primary-refresh(fast-forwardeb9a717f→700fe40d, clean tree). Fix3118a438now in source.npm run build→ freshdist/carryingwantsJsonOutputindist/cli-main.js./usr/bin/wp-codebox:recipe-run --json(missing recipe) → JSON envelope to stdout, exit 1 ✅recipe-run --recipe <bad-schema> --json→ JSON envelope to stdout, exit 1 ✅recipe-run(no--json) → plain text to stderr, stdout empty, exit 1 (regression preserved) ✅That fixed this host, but nothing stops it recurring on any host whose primary drifts.
Proposed work (this issue)
Pick one or both:
Skew detection in
doctor. Havewp-codebox doctorcompare the running binary's source revision againstorigin/main(or the configured tracked ref) and flag/warn when the primary that backs the binary is behind, or whendist/is older thansrc/. The binary already reports a source fingerprint indoctor— extend it to a freshness assertion.Auto-rebuild on primary refresh. When the primary checkout is refreshed (
--allow-primary-refresh), rebuilddist/so the symlinked binary can't lag its own source. At minimum, fail loudly ifdist/is stale relative tosrc/.Acceptance criteria
dist/older thansrc/) is detected and surfaced, not silently served./usr/bin/wp-codeboxrunning the refreshed source (either by rebuild or by a hard error that forces one).Notes
main./usr/bin/wp-codebox→ primary-dist/index.jssymlink model is the load-bearing detail: it means "deploy" and "binary freshness" are decoupled, so a fresh deploy of the plugin does not imply a fresh CLI binary.