---
name: opt-in-ci-legs-skip-silently
description: "BINDING gate rule — an all-green run whose opt-in legs SKIPPED is silence about that seam, not evidence; check skipped-vs-passed before ruling a gate green"
metadata: 
  node_type: memory
  type: feedback
  originSessionId: 0ed6e725-1e95-40e9-9ab2-1d8ba3153a26
  modified: 2026-08-25T01:40:14.585Z
---

**A green CI run whose opt-in jobs came back SKIPPED is silence about the seam those jobs cover — never evidence.** Before ruling any gate green, read job conclusions individually and classify every `skipped` — do not accept a run-level `success`.

Caught 2026-07-29 on the milestone-A golden candidate: run 30496546229 @af65ac0 returned run-level SUCCESS with changes / traceability / test+n1-gate on BOTH boxes green — but `twohost-a` and `twohost-b` were **skipped**, because in `golden.yml` both are gated `contains(head_commit.message,'[twohost]') || contains(pull_request.title,'[twohost]')` and the candidate's commit message lacked the token. Milestone A ACCESS-CONTROL *is* the cross-machine story (two-key subnet join, admin-code pairing, daemon-stamped proven sender, engine-room posture), so the one seam the milestone changes is the seam the run never exercised. "Cross-platform is inside the golden run" is TRUE for per-box suites and FALSE for the two-host pair.

**Why:** this is [[verdict-from-probe-competence]] in CI form — a suite that cannot express what you are hunting returns a clean result that reads as proof. An opt-in leg is a probe you have to *ask for*; forgetting the token produces the same clean green as passing it.

**How to apply:** (1) at gate time, enumerate job conclusions and treat `skipped` as UNKNOWN, never as pass; (2) ask "which seam does this milestone change, and did a leg actually exercise it?" — if the answer is an opt-in leg, verify the token is present in the tested commit message/PR title; (3) accept a skip only with recorded justification or prior evidence at substantively the same contents (name the run id); (4) suspect any milestone-subject seam guarded by an opt-in token — consider making the token mandatory for golden runs of milestones that touch it.

⭐⭐ **THREE FACES OF THIS CLASS IN ONE DAY (2026-07-29), all producing the same outcome — a clean-looking run with no evidence in it.** doyle ruled they belong in the ADR as ONE pattern, not three incidents:
1. **Dead condition** — `contains(github.event.pull_request.title,'[twohost]')` sits inside `golden.yml`, a workflow whose trigger is `push: branches:['golden/**']` + `workflow_dispatch` and which therefore *never receives a pull_request event*. The clause was carried over from ci.yml, where PRs do fire. A dead condition inside a gate is a booby trap for the next reader.
2. **Wrong artifact** — the twohost condition was read from `ci.yml@ffc7e9e` (a working checkout) and quoted as authority for `af65ac0`, but **the batch under test is what moved the jobs** into `golden.yml` (#119). *The instrument was competent when first read and made incompetent by the very batch it was measuring.* Hence the binding rule: quote conditions as `file@sha`. See [[name-the-file-and-sha-a-condition-came-from]].
3. **Dependency** — `twohost-a`/`twohost-b` are `needs: test` with the token clause as their entire `if:` (no `always()`, no `!cancelled()`), and `test` is a `fail-fast:false` matrix whose *job* result fails if either box's leg fails. So an **unrelated** red (the [[bystander-test-reads-process-global]] one) silently skips both two-host legs. `workflow_dispatch` is a fourth trap: no inputs declared, and on dispatch both `head_commit` and `pull_request` are null ⇒ both `contains()` are false ⇒ skip.

**The root defect is that a needs-skip and a token-absent skip are INDISTINGUISHABLE.** doyle's remedy (ruled over louder reporting): `if: !cancelled() && (<token>)` *removes* one cause so a skip is unambiguous by construction — a mechanism, not a label — plus default-ON with an explicit written opt-out, which removes the token as a memory dependency. `needs: test` was verified to be a POLICY gate, not an artifact dependency (the twohost jobs build their own cargo context), so dropping it costs nothing mechanically. **A gate whose correctness rests on someone remembering a token is not a mechanism** — see [[rule-failed-its-author-needs-a-mechanism]].

Practical consequence banked: to fire an opt-in leg for evidence, prefer probing **the tree that ships** over a marker-commit probe of an earlier tree — early information that cannot be collected is not early information.

⭐ **THE CLASS SHARPENED — it is a trigger-impossible expression in a GATE POSITION, not "a
pull_request ref in a push-only workflow" (doyle-ruled 2026-07-29).** Face 1's repair was
incomplete and the recurrence proved the wording matters: `golden.yml@6bf2bd9` still carried the
dead `contains(github.event.pull_request.title,'[budget]')` disjunct at **lines 354/361/371** (the
`[budget]` perf STEP gates) after twohost was fixed — same mechanism, step gate instead of job
gate. hertz's summary said "dead PR-title clauses removed"; that was true of twohost ONLY, so
**repairing one instance of a mechanism is not repairing the mechanism** — grep the whole file for
the event object, not just the gate you came to fix. But the file has FIVE such refs and only three
are defects: line 818 `ref: ${{ github.event.pull_request.head.sha || github.sha }}` has a
fallback, and line 829's always-empty `PR_NUMBER` is guarded by `[ -n "$PR_NUMBER" ]` at
`ci-notify.sh:123`. Both are DEFENSIVE and must be left alone. **The identical expression is a
defect in a gate and correct behind a fallback or guard, because a gate cannot express its own
impossibility — it silently returns false and reads as a route that exists.** Candidate mechanism
(not yet built): a workflow lint that derives each file's trigger set and flags `github.event.<x>`
references in `if:` positions unreachable from those triggers — see
[[rule-failed-its-author-needs-a-mechanism]].

⭐ **FOURTH FACE, same shape, non-CI: SILENT ATTRIBUTION LOSS.** hertz's `6bf2bd9` commit body ends
with literal backslash-n characters rather than newlines, so its `Co-authored by:` trailer sits
mid-line and `.github/ci/ci-notify.sh:143`'s `^`-anchored `sed` never matches — every run on that
branch notifies the gater only and drops the co-author with no error. Ruled fix-forward (never
rewrite a structurally-verified sha to fix a trailer — that breaks tested==assembled for zero
functional gain): make ci-notify LOUD when a body yields no parseable trailer, and **verify a
trailer by running the real extractor against your own commit body pre-push, not by eyeballing the
message.** A trailer that looks right and does not match is the whole failure mode.

Related: [[verdict-from-probe-competence]], [[golden-ci-strategy]], [[gate-cross-platform-leg]], [[absence-needs-sibling-probe]], [[verdict-from-exit-not-from-silence]], [[milestone-a-golden-landed]].

⭐ **A FREE DISCRIMINATOR for the "indistinguishable skip" defect above: read the skip against its
LEG'S OWN conclusion.** 2026-08-24, golden r2 `32796133406` @`12ab4a7a`. At the previous sha I had
filed the three `Dormancy budget` steps under run-derived post-failure skips, because that leg died
at clippy and everything after it skipped — a reasonable read that was WRONG. At r2 the same three
steps skipped inside a leg that went **green**. A needs-skip / failure-suppressed skip cannot occur
in a passing leg, so a skip in a GREEN leg is opt-in **by construction** — no config read required.
doyle then confirmed it from config (`[budget]` in the head commit message, plus `SPT_BUDGET=1`
env-gating inside the sweep itself), which is the stronger evidence; the point is that the green-leg
observation gets you there for free and immediately.

deployah then supplied the **cleanest control**, which was available in the FAILING run all along and
which I had missed by only looking at the leg that died: r1's **Windows** leg was fully green and
skipped all three anyway. Three cells — r1-Windows green/skipped, r1-Linux red/skipped, r2-Linux
green/skipped — pin it as condition-derived AND not OS-specific, with no config read at all. **When a
matrix runs the same steps on a sibling box, the sibling leg is a free control for any skip you are
trying to classify; look there before reasoning from the leg that failed.**

**The reporting rule it feeds:** an all-green run is exactly when this gets miscounted, because
nobody audits skips in a run that passed. Say the seam carries **zero signal in BOTH runs** — an
unchanged standing gap, neither caused nor cured by the change under test — rather than letting it
ride inside "r2 is green". Same family as [[an-unexercised-prediction-is-not-a-correct-one]]: sort
what your evidence actually exercised, and name what it never touched.
