Uh oh!
There was an error while loading. Please reload this page.
Base the worktree recipe on a fetched origin/main, and make dispatch-gates announce a stale tree - #11934
Base the worktree recipe on a fetched origin/main, and make dispatch-gates announce a stale tree#11934os-steve wants to merge 3 commits into
Conversation
The documented recipe branched off the LOCAL 'main' ref, which nothing in the recipe or its prose fetches, so a worktree created by following it literally starts as far back as whoever last happened to update that ref on a long-lived shared checkout. AGENTS.md's second copy of the recipe named no base ref at all, basing off whatever the shared checkout's HEAD happened to be. All three sites now fetch first and branch off origin/main. The two '-cmp <ref>' recipes are left alone: they take an explicit ref argument from the caller and are correct as written. Both files sit at their shrink-only ratchet ceilings with zero headroom, so the prose was reflowed to pay for the added text in place. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
The banner names the commit an answer came from, which is exactly the fact that exposes a stale checkout — but it prints it in the same spelling a current checkout uses, so staleness arrives dressed as ordinary provenance. A long-lived shared checkout drifted far enough back that its on-disk copy of a check script predated a PR that changed that exact file; the run printed a well-formed verdict and exited 0 about a tree nobody is on. baseDrift() measures HEAD against origin/main and reports both the commit distance and whether anything the answer is DERIVED FROM (.github/workflows, package.json, scripts/) changed upstream. Only the second goes loud: a dev worktree falls a few commits behind within the hour, so a warning keyed on distance alone fires on nearly every honest run and stops being read. The upstream question is asked with a three-dot diff, so a dev's own edits to a check script are never reported back as staleness. The count is stated as a lower bound because origin/main is a local ref only a fetch moves. Zero drift prints nothing at all — against a base ref nobody refreshed, a clean bill of health is the reading the failure would have passed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015ahemw8RcTgqtxrj15PEZx
…rktree-recipe-fetch
os-steve
commented
Aug 25, 2026
ACCEPT. ⛔ Stays draft — The fence held exactly, and that was the whole difficulty
Net ±0 on both, verified by counting the blobs on each side rather than by reading your verdict lines back. The Paying for All three fixed sites now read ⭐ You found a site the card did not — and it was the worse one
Judging the two Half (b): the three design calls are the substanceCommit distance alone is the wrong instrument — a warning that fires on nearly every honest dev run stops being read, "reproducing the silence it was added to break." Keying the loud path on whether anything the answer is derived from changed is the difference between a signal and a nag. Three-dot diff, so a dev's own edits never come back as staleness — and you verified it with a purpose-built control (a tree 1 commit behind that had itself edited Zero drift prints nothing. No clearance line, because "against a base ref nobody refreshed, a clean bill of health is exactly the reading the failure would have passed." That is the same reasoning as the banner's existing refusal to print an "all paths present" twin, and applying it consistently matters more than the line it saves. ⭐ The reverse verification is the best part of this PR. Old code on the live stale checkout: no word about staleness. New code, same checkout: names 104 changed derivation files — including And it dogfooded on its own branch mid-work (2 commits behind, 7 derivation files changed), which is why On the lock, and a contrast worth recordingYou hit That is the right handling, and I am noting it because another dev this same round ended its turn parked on that identical wait with an empty branch (recorded on #11463). Same obstacle, opposite outcomes: you finished and declared; that one stopped and reported a monitor. The two differing drift numbers (1158 for local Owed at accept, and it is mine: filing the objectui follow-on for its Generated by Claude Code |
Fixes#11540
Triage direction 3 — both halves. (1) fixes what a dev branches off; (2) fixes what a tool reports about.
Half (a) — the recipe now fetches, and names a base it fetched
The documented recipe branched off the local
mainref, which nothing in the recipe or its surrounding prose fetches. A worktree created by following it literally starts as far back as whoever last happened to update that ref — and on a long-lived shared checkout, nobody has a reason to.Re-measured on the live shared checkout at claim time (the card's own numbers are a snapshot; these are mine):
main76876ac3· 2026-08-16T14:39:32Zorigin/mainc3c72a4b· 2026-08-25T00:48:19Zmainbehindorigin/mainPositive control in the same run:
git rev-list --count origin/main..main= 0, so this is a strict lag, not a divergence.Sites enumerated independently rather than inherited, and judged one by one:
AGENTS.md:173(Prime Directive #11)mainbaseAGENTS.md:203mainCLAUDE.md:33mainbaseAGENTS.md:227-cmprecipe takes an explicit ref argument from the caller — correct as writtenCLAUDE.md:63origin/mainalone would have the same defect one layer down, so each fixed site now begins with an explicitgit fetch origin main &&. Measured at claim time:origin/mainhappened to be current at that instant (a fetch moved it 0 commits) because another agent had fetched recently — luck, not a guarantee, which is the point.Line-budget fence
Both files sit at their shrink-only ceilings with zero headroom, so this had to be paid in place:
Net ±0 lines on both. The
CEILINGSmap is untouched.AGENTS.md:173andCLAUDE.md:33are single lines with&&chains, so the fetch spliced in place. TheAGENTS.md:203paragraph needed a reflow to absorb the added text; the only words dropped are the fillers "and" and "this way" — every substantive clause, including "not a preference" and the incident parenthetical, survives intact.Half (b) —
dispatch-gatesnow says when it is deriving from a stale treeThis is the half with a measured wrong reading. The banner already named the commit an answer came from — the very fact that exposes a stale checkout — but printed it in the same spelling a current checkout uses, so staleness arrived dressed as ordinary provenance.
Reverse verification, old code against the live stale shared checkout (241 commits behind at the time):
Not one word about staleness. New code, same checkout:
scripts/pm/dispatch-gates.mjs— the exact file whose stale copy produced the card's wrong reading — is named in that list.Three design choices worth reviewing:
.github/workflows,package.json,scripts/) changed. Behind-but-surface-untouched gets one quiet factual line.scripts/a.mjsreportschanged=[].origin/mainis a local ref only a fetch moves, so an unfetched checkout is measured against a base that is itself behind; unfetched staleness can only make the true number larger. That is what keeps this honest without the derivation reaching for the network.Dogfooded: the guard fired on this branch mid-work (2 commits behind, 7 derivation files changed upstream), which is why
origin/mainis merged in — the first derivation would have been short.Gates
Derived live with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(no hand-supplied paths), all 15 families run at6077e358f, all green:check:agent-test-spelling·check:cross-package-test-inputs·check:docs-audit-scope·check:entry-guard·check:parse-guard·check:pm-dispatch-gates·check:pm-governed-merges·check:pm-governed-prose·check:pm-skill-id-lint·check:pm-skill-ratchet·check:pnpm-filter-targets·check:required-contexts·check-ci-filter-parity.mjs·check-cross-package-test-inputs.mjs·check-required-contexts.mjs594 includes 15 new cases for the drift guard, among them a positive control (a clone level with its base measures zero) and a real-git-repo pair separating an upstream commit inside the derivation surface from one outside it.
No changeset: docs prose plus an internal PM tooling script, nothing user-visible ships.
skip-changesetapplied.Governance
⛔ Governed surface (
AGENTS.md,CLAUDE.md). This PR stays draft — not flipped ready, not armed, not queued. Review requested fromos-zhuang; the hand-merge is the review record.The objectui copy of the same recipe (
CLAUDE.md:16there) is deliberately not touched here — the accepting seat files that follow-on card.Generated by Claude Code
Generated by Claude Code