Automattic / Automattic/wp-codebox

Stale primary checkout silently ships an out-of-date /usr/bin/wp-codebox (no skew detection / no auto-rebuild)

Open
#1,222 1 comment 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
16
Forks
4
Avg merge
59m
Merged PRs (30d)
131

Description

## 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)

1. `workspace git pull wp-codebox --allow-primary-refresh` (fast-forward `eb9a717f` → `700fe40d`, clean tree). Fix `3118a438` now in source.
2. `npm run build` → fresh `dist/` carrying `wantsJsonOutput` in `dist/cli-main.js`.
3. Verified the live `/usr/bin/wp-codebox`:
- `recipe-run --json` (missing recipe) → JSON envelope to stdout, exit 1 ✅
- `recipe-run --recipe --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:

1. **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.

2. **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.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the `wp-codebox doctor` entry point and the primary refresh path behind `--allow-primary-refresh`; inspect how the binary reports its source fingerprint and how `npm run build` updates `dist/`. Trace the `/usr/bin/wp-codebox` symlink model, then verify that a behind-remote or stale-dist primary is surfaced and that refresh leaves the binary fresh without changing already-fresh behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
git, node.js, typescript
Domain
build-system, cli, devops, tooling
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.