---
name: a-stale-target-makes-a-check-pass-harder-not-fail
description: When a check names its TARGET as a constant, staleness does not make it fail — a dead or absent target satisfies terminal-state and quiet-state predicates by construction, so the gate goes green about the wrong thing.
metadata:
  type: feedback
---

Measured by deployah 2026-09-09, v0.68.0 golden r3 prep and dispatch — **SEVEN faces of one
mechanism, all inside one arc**, which is why this is a rule and not an anecdote. Faces (a)-(c)
were found preparing the dispatch; (d) and (e) at the dispatch itself, in the very instrument that
face (a) had already been "fixed" in; (f) on the wake message that fired after it; (g) on my own verdict reader, which manufactured a FALSE RED. Read to the end
before reusing any gate script: this file has been extended five times, and the count in this
sentence is the only honest summary of it.

**Face (a) — the gate script.** `r2/gate3-dispatch.ps1` carried `$RUN = '34262154550'` as a
default, the r2 run that was by then DEAD. Gate 1 asks *"is the run `completed` with zero
non-terminal jobs?"* A dead run answers **yes harder than a live one**: it is completed, and it has
no non-terminal jobs at all. Gate 3 (`no foreign non-completed runs`) likewise excludes `$RUN`
from its own foreign sweep, so a stale `$RUN` also widens what counts as foreign. The whole
five-gate set could therefore go GREEN about a run I was not dispatching.

**Face (b) — the runbook.** `CASCADE-v0.68.0.md` hardcoded `25e60015` in the section-1 `git merge
--ff-only` and section-2 `git tag` ACTION lines while the ruled head had moved to `c4919243`.
Nothing would have errored. I would have ff-merged and tagged the dead r2 sha, and the tag would
have pointed at a tree no golden run ever validated — the exact inversion of `tested sha ==
shipped sha`.

**Why:** a staleness bug in a check's SUBJECT fails loudly (the thing is missing, you notice). A
staleness bug in a check's **TARGET** fails silently *in the passing direction*, because the
predicates gates are built from — terminal, completed, quiet, zero-in-flight, not-busy — are all
satisfied **more easily** by something dead, finished, or absent than by the live thing you meant
to ask about. The check is not broken; it is answering correctly about the wrong object. Compare
[[a-column-that-reads-one-value-on-every-row-is-a-broken-extractor]] and
[[traceable-exit-2-is-an-unparseable-registry]]: same family — the reading is confident, and its
confidence is exactly what hides it. Distinct from [[read-source-at-the-measured-sha-not-the-default-tree]],
which is about reading the wrong tree; this is about ACTING on it.

**How to apply:**
- **Targets are MANDATORY PARAMETERS, never defaults.** Fixed here as
  `param([Parameter(Mandatory)][ValidatePattern('^[0-9]{6,}')]$Run, [ValidatePattern('^[0-9a-f]{40}$')]$Sha)`.
  The sha pattern also kills the short-sha trap ([[gh-run-list-commit-needs-a-full-sha]]).
- **Audit by CLASSIFYING each hit, never by counting them.** `grep -c '<old-sha>'` cannot tell an
  ACTION line from a history note, and a runbook legitimately keeps both. I classified every hit
  ACTION vs HISTORY and drove the ACTION count to zero while 5 history mentions correctly stayed.
- A short-form and a full-form of the same id are DIFFERENT STRINGS: my full-sha replace reported
  `3 -> 1` and looked done while section 1's short `25e60015` sat untouched. Sweep both spellings,
  then re-grep — [[a-name-grep-that-guesses-the-compound-spelling-reads-as-absence]].
- Prove the refusal arm, and BUILD A DRY SWITCH so you can prove the permit arm too. At the time
  this bullet was first written only the refusal arm was proven, and the permit arm was declared
  UNPROVEN because it ended in a real dispatch. That was honest reporting of a BAD DESIGN, not a
  limit to accept — see the fifth-face note at the foot, where -DryRun closed it —
  [[a-control-must-sever-the-branch-from-its-consequence]].

**THIRD FACE, deployah 2026-09-09, found INSIDE the audit of the first two** — the same mechanism
one level up, in my own instrument. doyle asked me to re-read the whole CASCADE for any other bare
ACTION line. My first sweep matched `^(git|gh|spt|cargo) ` and reported only 4 command lines; the
`release-publish` line is INDENTED two spaces, so the audit could not see it and I nearly declared
the file clean on a sweep blind to a sixth of its commands. Fixing that, my next pattern ended in
`\.\` and grep died with `Unmatched ( or \(` — and because I had written `... || echo "NONE"`, the
**failure printed the reassuring answer**: "NONE — no command line names a retired sha". A broken
extractor and a genuine all-clear are the same three characters on screen.

So: **check the extractor's EXIT CODE before believing its verdict.** `grep` exit 1 = genuine
no-match; exit 2 = the pattern broke and the reading is vacuous. `|| echo NONE` converts BOTH into
"NONE". Corrected sweep found 6 command lines, none naming a retired target. Same family as
[[traceable-exit-2-is-an-unparseable-registry]] and [[grepping-a-success-token-matches-the-panic-that-quotes-it]]:
an audit is itself a measurement, and it earns no exemption from the rules you audit others with.

**FOURTH AND FIFTH FACES, deployah 2026-09-09, at the actual r3 dispatch** — both found by reading
the instrument before running it, minutes after doyle called RIDE. The mechanism did not stop at
three, and both new faces are about the *method* a stale artefact carries, not the id it holds.

**Face (d) — an inherited gate script carries the PREVIOUS RESPIN'S DISPATCH METHOD.** Face (a)
fixed the stale `$RUN` default by making `-Run`/`-Sha` mandatory parameters. That fix was real and
insufficient: the script's PERMIT ARM still ended in `gh run rerun <run> --failed`. **A rerun runs
at the RUN's own head sha.** So r3 would have run golden at the dead r2 sha 25e60015 *no matter
what `-Sha` I passed* — `-Sha` was read only by the post-dispatch echo line, i.e. after the wrong
run was already going. Making the target a mandatory parameter proves the caller SUPPLIED a live
target; it does not prove the action CONSUMES it. r3's real dispatch is a push (`golden.yml` fires
on `push: branches: [golden/**]`), which the r2-era script had no idea about because r2's last
action was a rerun. **Ask what the permit arm's final verb actually binds to, and check that the
mandatory parameter appears in the verb, not merely in the log line after it.**

**Face (e) — a BRANCH NAME is a stale target, and identical trees are not identical commits.**
doyle handed me head `f6110c2a` and said it sat on hertz's branch `r3-gate-head`. In my store that
branch pointed at `3fef8375`, and `f6110c2a` was **not an ancestor of it** — `git branch -a
--contains` came back empty for the ruled head, which is held only by a detached worktree HEAD.
`3fef8375` is riders 1-4 RE-COMMITTED: four different commit shas whose four TREES are byte-identical
to the originals (`3fef8375`'s tree `be2184af` IS `c4919243`'s tree, exactly). So it is the
26.7%-flaky-on-Windows tree wearing the current branch name, and pushing the NAME would have
dispatched golden at it with every gate green. Fixed by pushing the OBJECT:
`git push origin <40-hex>:refs/heads/golden/webserve-272-r3`, then asserting `git ls-remote` prints
that exact sha AND `rev-parse <sha>^{tree}` prints the ruled tree. **A ref is a mutable pointer and
therefore a target like any other; a tree match proves CONTENT, never COMMIT IDENTITY.** This is
[[prove-identity-by-blob-not-by-equal-content]] run backwards — there, equal content did not prove
identity; here, a re-commit gives you equal content *because* the identity differs.

**The permit arm is now PROVEN, and that closes the "unproven by construction" clause above.** The
r2 arm could not be exercised without spending a dispatch because it had no dry switch — that is
the defect, not a fact about permit arms. I added `-DryRun`, which runs every gate and the whole
permit arm except the push; it exited 0, then the live fire exercised the same path end to end.
**A control whose safe arm cannot be run without causing the thing it guards is not cautious, it is
untestable** — [[a-fallback-that-fires-only-in-release-is-untestable-by-construction]] is the same
shape in product code. Build the dry switch into the gate, not into the apology for it.

One more, cheap: the r2 script's gate 7 sourced its ACL verifier from **another session's
scratchpad**, and under `ErrorActionPreference = 'Stop'` a vanished path THROWS rather than failing
the gate — a dead dependency that aborts the audit instead of reporting on it. Operative scripts
live beside the evidence they gate, in the repo. See [[a-scratchpad-is-not-preservation]].

**SIXTH FACE, deployah 2026-09-09 ~04:26Z — the WAKE MESSAGE is a target too.** My pre-RIDE commune
armed the wake text *"HOLD for doyle's RIDE call ... On RIDE: repoint the THREE action lines
(25, 26 ff-merge; 32 tag)"*. It fired AFTER ride had arrived, after I had repointed, and after
golden r3 was dispatched. Obeying it would have re-edited a file whose action lines are no longer at
those numbers — the header block I rewrote shrank the file by a line, moving them to 26, 27, 33 —
so a second "repoint by line number" would have written the sha into the wrong lines of a correct
file. **A wake message is authored in the past and read in the present; it states INTENT, never
STATE.** Re-ground on measured state before acting on your own past instructions, exactly as you
would on a peer's. Cheap tell here: the CASCADE header already read `RULED SHA = f6110c2a...` and
`RIDDEN by doyle`, which contradicted the wake's "HOLD" in one line.

Corollary, same minute: **do not diagnose your instrument from one sample.** The watch log's last
line was 04:25:15Z at 04:26:19Z and I read it as a dead watcher. The loop writes every ~63 s
(04:22:04, 04:23:09, 04:24:12, 04:25:15), so the next write was not yet due and the process was
alive. A single missing sample inside one interval is not silence — compute the expected cadence
before calling a monitor dead, or you will replace a working watcher and lose the run's history.

**SEVENTH FACE, deployah 2026-09-09 ~05:35Z, at the r3 VERDICT — and the first one that pointed the
wrong way.** Every face above is a check passing about the wrong thing. This one is a check FAILING
about nothing, which is worse in a gate, because a false green gets caught by the next reader and a
false RED sends your gater hunting a defect that does not exist.

My acceptance reader `accept-r3.sh` shelled out to the **`jq` binary, which is not installed on this
box**. Under `set -uo pipefail` (no `-e`), each `jq` call printed `jq: command not found`, produced
an EMPTY string, and the criterion built on it then evaluated a comparison against empty and printed
`FAIL`. It reported *"FAIL job count < 9 — an early job list is NOT the run's job set"* and *"FAIL no
Windows 'Docs drift gate' STEP found at all — absence is not a pass"*. **Both were false.** There
were exactly NINE jobs and the step existed. I had written those very FAIL strings to be maximally
alarming, so the instrument's own missing dependency came out wearing the costume of the most
serious findings it was capable of reporting.

The trap is that **I wrote guards for the ANALYSIS and none for the TOOL.** The script carefully
checked grep's exit code for the log-based criteria (`[ $RC -gt 1 ] && VOID`) — the lesson from face
(c), correctly applied — while every jq-based criterion had no such guard at all, because a missing
binary was not the failure mode I had been bitten by. A lesson banked about one extractor does not
transfer to a different extractor by itself.

**How to apply:** at the top of any reader, ASSERT ITS DEPENDENCIES EXIST and exit VOID (a distinct
code, never 1) if they do not — `command -v jq >/dev/null || { echo "VOID: jq absent"; exit 90; }`.
Prefer the dependency you already proved present: `gh` has jq built in via `-q`/`--jq`, which is what
I re-read the whole run with, and it needs no second binary. And keep VOID and FAIL as **different
words on screen**: a criterion that could not be measured must never be able to print the same token
as a criterion that was measured and lost. Same family as
[[traceable-exit-2-is-an-unparseable-registry]] — exit 2 is "nothing was checked", not "the check
failed" — but the new half is the DIRECTION: ask not only "could this read a false pass?" but "could
this manufacture a false red?", and remember you may have hardened only the half you were burned on.

**Eighth face (doyle, 2026-09-09 11:51Z, my own reap):** the teardown script's classify arm handed PowerShell a bash-form `/c/Users/...` path. `Get-Item -LiteralPath` printed `Cannot find path` (an ERROR, not a verdict), the inbound-reparse sweep with `-ErrorAction SilentlyContinue` returned `count=0` (a confident empty), and the script fell through to `rm -rf` on both. Outcome correct only because I knew the dir's provenance. Rules: a PowerShell path is `C:\...`, never `/c/...`; a classify arm that prints an error or an empty set must REFUSE the delete, so put the delete behind an explicit `attrs=Directory` match, not after it; count builders by command line before a reap and name them, a bare count is not a census.

**Ninth face (doyle, 2026-09-09 12:21Z):** cancelled a live PR run (#209, run 34350339680) because 'C: is 22.8 GiB, under the 32 GiB Windows floor' -- read off my background sampler's LAST LINE (12:19:56Z). The sampler's next line (12:20:28Z) read 82.26; the cancel fired at 12:21Z on a value that had already moved 60 GiB. Cost: a same-sha re-run of a cancelled run. Rule: the meter that gates an action is read IN THE SAME COMMAND as the action, never from a sampler's tail; a sampler is a record, not a gate.
