diff --git a/AGENT-BRIEF.md b/AGENT-BRIEF.md index 46516ff30..718363c3a 100644 --- a/AGENT-BRIEF.md +++ b/AGENT-BRIEF.md @@ -45,6 +45,14 @@ Only push to `main` what you have actually verified — it is the branch a human `bun run build` takes minutes, and its first step deletes `public/doxa-maps/` — which wipes the output every other agent is using. **Use the dev server; it hot-reloads.** +**This is now ENFORCED, not just asked.** `bun run build` refuses with exit 75 unless you set +`BUILD_ALLOW=1`. If you hit that refusal it is telling you the truth — take one of the cheaper +paths it prints rather than reaching for the flag. Only the coordinator, running the single +authoritative build for a wave, should set it. This rule was documented here *before* it was +enforced and got violated anyway, by a dispatch prompt that said `bun run build` while telling the +agent to read this file first. An explicit instruction beats a standing rule at the point of work, +so the build now refuses instead of asking. + ```sh bun x vite --port --strictPort # e.g. 5299, 5399, 5499 … BUNDLE= bun x vite build # one bundle's compiled output; does NOT clean diff --git a/internal/build-lock.sh b/internal/build-lock.sh index 8f2ddf8c5..d6e5a7d94 100755 --- a/internal/build-lock.sh +++ b/internal/build-lock.sh @@ -19,6 +19,53 @@ LOCK_FILE="${BUILD_LOCK_FILE:-public/.build.lock}" WAIT="${BUILD_LOCK_WAIT:-0}" mkdir -p "$(dirname "$LOCK_FILE")" +# ── THE COORDINATOR-ONLY GUARD ─────────────────────────────────────────────────────────────── +# ★ WHY A GUARD AND NOT A DOCUMENTED RULE (measured 2026-09-23, the third instance of one shape) +# "Agents never run `bun run build`" is ALREADY a top-level heading in AGENT-BRIEF.md, and the +# research doc that established it is three hours old. It was violated anyway — by the +# coordinator, who put `bun run build` into a dispatch prompt while telling the same agent to +# read the brief first. The agent obeyed the prompt, the build's clean step wiped public/ mid- +# probe, and an unrelated bundle read back as "did not mount." That near-diagnosis was a +# fictional defect in a file that was fine. +# +# So the failure mode is NOT that the rule is missing, buried, or unclear. It is that an +# explicit instruction beats a standing rule at the point of work, every time. Documentation +# cannot fix that; only something that REFUSES the action can. The lock below already makes +# concurrent builds impossible — but it does not make an UNNECESSARY build impossible, and N +# queued agents still cost N full rebuilds, each one cleaning the tree out from under every +# concurrent reader. +# +# This guard is deliberately a SPEED BUMP, not a wall: it fails with the cheaper alternative +# printed, and any caller can proceed by setting BUILD_ALLOW=1. That is the point — the person +# who genuinely needs a full build spends one line saying so, while the agent who inherited +# `bun run build` from a careless prompt gets told what to run instead. +# +# ★ WHY OPT-IN AND NOT SUBAGENT-DETECTION (a gate that could not fail, caught before shipping) +# The first version of this guard fired on `CLAUDE_AGENT_ID` / `CLAUDE_SUBAGENT`. MEASURED: both +# are UNSET in a real subagent — I invented them — so the guard would have passed silently 100% +# of the time while looking like protection. A probe agent then reported CLAUDE_CODE_CHILD_SESSION +# as the discriminator; that is ALSO wrong, and instructively so — it is set to 1 in the +# subagent AND in the coordinator, so it marks "a Claude process", not "a child". The agent could +# not have known: it only saw its own side. A discriminator is only validated by comparing BOTH +# environments, which is why this is now opt-in rather than detection. There is no env var here +# that distinguishes coordinator from subagent; anything claiming to is an untested guess. +if [ "${BUILD_ALLOW:-}" != "1" ]; then + echo "🚫 FULL BUILD REFUSED — a full build is almost never what you want here, and its FIRST" >&2 + echo " step deletes public/doxa-maps/ entirely, which tears" >&2 + echo " the tree out from under every other agent reading it (measured 3+ times, incl. a" >&2 + echo " 461-file phantom deletion and several false 'element did not mount' diagnoses)." >&2 + echo "" >&2 + echo " USE INSTEAD, cheapest first:" >&2 + echo " bun run tree # regenerates manifest.json + leaf pages, NO clean" >&2 + echo " BUNDLE= bun x vite build # rebuilds ONE bundle, leaves the others alone" >&2 + echo " node internal/settled-tree-gate.mjs # just READ the tree someone else built" >&2 + echo "" >&2 + echo " If you ARE the coordinator and genuinely want the one authoritative full build," >&2 + echo " re-run it as: BUILD_ALLOW=1 bun run build" >&2 + echo " If you are a SUBAGENT, do not set BUILD_ALLOW — ask the coordinator to build instead." >&2 + exit 75 # EX_TEMPFAIL, same code as a held lock — 'not now', not 'broken' +fi + # fd 9 = the lock handle exec 9>"$LOCK_FILE"