# Diag-arm intake criteria — releases#294 / v0.69.0

deployah's gate checklist for the `diag/294-sync-stages` arm. Written 2026-09-10 at ruling time, not
at run time, so none of it is rediscovered under pressure. Source: doyle's scope ruling ~14:42Z and
todlando's measurement contract, same minute.

## The distinction that must never blur

| | |
|---|---|
| **Candidate** | `a9e786b2` — the sha under gate, in both filed verdicts, and the sha that would tag. **Does not move.** |
| **Diagnostic tree** | `a9e786b2` **plus** the instrumentation patch. A different artifact. |

**A diag arm is NOT a gate arm.** It cannot discharge the gate, cannot produce a verdict, and its
numbers are never quotable as measurements of the candidate. Every report I write from one names the
ref and the tree explicitly, in the report itself, not just in a preserved file.

> Correction to my own earlier framing: I told doyle that instrumenting "costs one extra gate arm at
> the end." That was wrong under this ruling. The clean bare arm at the shipping sha was **always**
> owed to discharge the gate; the diag arm is not a gate arm and adds nothing to that count.

## Before the arm runs

- [ ] Ref is `diag/294-sync-stages`. **Never** `golden/**`, never an assembly branch.
- [ ] Confirm the diagnostic tree is `a9e786b2` + patch: record both the diagnostic sha and
      `a9e786b2`, and state the relationship. Verify by object, not by branch name — a hand-tracked
      branch is a snapshot of the last assembly it was moved to.
- [ ] `core#218` still unmerged; candidate sha unmoved.
- [ ] **No `traceable-reqs` tags in the hooks, no REQ activation.** Diagnostic code is not evidence
      for a requirement stage; tagging it mints coverage for something that never ships. Spot-check
      the patch for `[impl->`, `[unit->`, `[int->`, `[doc->` before running.
- [ ] Pool claimed for the diag lane (`pool-claim --pool <dir> --label diag-294`). Do not build the
      diagnostic tree into a pool a shipping lane will reuse without a claim in between. Sequential
      reuse is fine; silent sharing is not. Predict lane-identity refusals from the BUILD, never
      from `pool-claim`.
- [ ] Box census clean before arming; both boxes free.

## After the arm

- [ ] Preserve `instrumentation.patch` **beside the logs it produced**. A number whose instrument was
      thrown away is an anecdote.
- [ ] Preserve alongside it: diagnostic sha, platform, command/profile, raw output.
- [ ] Report names the ref and says DIAGNOSTIC TREE, not candidate — in the report body.
- [ ] Poll with `.spt/gate-poll.sh` (per-job transitions; selftested both directions).
- [ ] **When the diagnostic continuation SUCCEEDS, record its MARGIN as a number** — the elapsed
      against the budget, never bare "passed". (doyle's addition, and it is what this whole red
      teaches.) The 400x25 ms loop reports pass/fail and nothing else, so a 4.448 s success and a
      9.940 s success are *the same green*, while the failure is right-censored with no upper
      endpoint. **A green carrying no margin cannot be distinguished from a green that was one
      scheduling hiccup from red.** That is why r2 cost a full gate arm and taught almost nothing.
      Banked by doyle as KNOWN-HAZARDS 7.66 with a `REQ-HAZARD-*` id, registry-first with
      `required_stages = []`, activated by whichever lane delivers the test.

## What the numbers are supposed to answer

Stage boundaries, **both platforms**: Git store creation; each of the four `rev-parse` subprocesses;
`net_stream_send`; and the gap from first write to the responder's `accept_bi` becoming visible.
Plus the separately bounded **diagnostic continuation** on expiry — preserves the failure, reports
eventual visibility with id and first-observed elapsed. That is what turns a censored observation
into a real one.

todlando's contract adds: one process-wide monotonic epoch; identify test, pull ordinal,
requester/responder node, connection and stream (broker-local stream ids alone do not correlate
endpoints); timestamp requester-thread entry, store-init entry/exit, open and subscribe entry/exit,
each tip lookup entry/exit with ref and outcome, first-send entry/return, inbound accept and
registration, first eligible observation. **Distinguish send submission from actual transport
write** — if only submission is hooked, the downstream interval is labelled
*submission-to-registration*, never *wire latency*. Preserve the original 400-iteration acceptance:
the continuation never converts the original failure into a pass. Avoid per-poll added IPC.

## The fork these numbers decide (stated in advance so the result is not read backwards)

- **Process-spawn dominated** (git init + four subprocesses) → fix is **SEQUENCING**: move the git
  work before the subscribe/poll window opens. No budget change at all.
- **Post-write visibility-gap dominated** → transport question, different lane.
- **Neither dominates cleanly** → composite, and doyle says so rather than picking the prettier half.

todlando's caveat, carried: git-operation timings include process startup, Git work, filesystem
access and scheduling, so they **cannot alone establish spawn dominance**. Sequencing stays a
candidate intervention, not an automatic fix.

## Standing while this runs

Finding 1 **UNCLASSIFIED as to CAUSE** (all three cause phrasings withdrawn 2026-09-10: delayed
publication, measured tail, boundary defect). `34445961595` is the verdict of record; `34481993681`
a second red at the same cell. Release **HELD** — v0.69.0 not cleared, no tag, no publish, no board
close-out. **No new bare gate arm at `a9e786b2` until a measurement lands.**

## How the arm actually fires (measured 2026-09-10 ~14:56Z, deployah)

**Pushing `diag/294-sync-stages` fires NOTHING.** Read from the workflow files at the candidate
tree, not assumed: `ci.yml` has `push: branches: [main, dev-freeform]`, `golden.yml` has
`push: branches: ['golden/**']`. A diag ref matches neither. Both carry `workflow_dispatch`.

So the arm is a **`workflow_dispatch` at the diag ref** — which also means pushing the ref is a
safe, CI-silent act, and it must be pushed first because dispatch resolves a remote ref.

Which workflow is doyle's ruling, not the runner's: `golden.yml` dispatch is the full nine-job run
including twohost (both boxes); `ci.yml` dispatch is the thin lane, and the failing cell
(`spt-daemon::sync two_tier_sync_lands_and_gate_refuses_server_side`) is a Windows **unit** test, so
the thin lane reaches it at a fraction of the cost. The tree does not go on a `golden/**` ref either
way — that is what keeps a diag arm from reading as a gate arm afterwards.

## Denominator note for the treqs spot-check

`git diff <candidate>..<diag> | grep -c '^+'` counts the five `+++ b/...` file headers too. At
ef9c1599 that is 521; excluding headers it is 516, which is exactly the diffstat's insertion count.
Quote the header-excluded number, and pair the zero with the detector control (the same grep over a
commit that DOES carry a tag must return non-zero) before reading the zero as clean.

## ARM-MECHANICS RULING (doyle, 2026-09-10 ~14:58Z) — supersedes the section above IN PART

The trigger measurement above stands (doyle re-read `ci.yml` / `golden.yml` at the lane tree and
confirmed it exactly). What is superseded is my framing that the arm "must be a workflow_dispatch":

1. **`golden.yml` dispatch is REFUSED.** Not on cost — the deciding reason is that a golden.yml run
   at a diagnostic ref lands in the golden RUN LIST, where `gh run list --workflow golden.yml` shows
   a run that is not a gate arm and no report body reaches. This file makes "a diag arm is not a gate
   arm" true in prose; the workflow choice is what makes it true in the run list. (Also: the twohost
   jobs are `needs: test`, so they cannot observe the failing cell at all.)
2. **The first instrument is NOT CI.** The red is a Windows unit cell on the self-hosted runner —
   this box. A targeted local `nextest` at the diagnostic sha here, plus the Linux leg over ssh to
   kitsubito, reaches the same cell on the same hardware in minutes, no workflow, no queue, no second
   box tied up. Push `diag/294-sync-stages` to origin only for FETCHABILITY (kitsubito), never as an
   arming act — that push fires nothing.
3. **`ci.yml` workflow_dispatch is the ESCALATION and needs a written argument** — only if the local
   run fails to reproduce, or its numbers implicate something the local shell does not carry (runner
   service account, Defender posture, concurrent job load). Write that reason down BEFORE dispatching,
   the way this file was written at ruling time.
4. **Nothing arms until hertz NAMES a handoff sha.** A ref I discovered is a measurement; a handoff is
   a statement. ef9c1599 is a verification revision — hertz is re-keying correlation on the exchange
   id in the received request, because each new QUIC connection reuses wire-stream id 0, so
   endpoint-pair + wire id is not a unique cross-pull key. Attempt-1 outputs stay preserved as
   `attempt1`: a superseded measurement is evidence about the instrument's history, not garbage.

## Local-first run plan (deployah, drafted 2026-09-10 ~15:03Z — NOT executed, no handoff sha yet)

Written now so it is not improvised at handoff time. Every step below waits on hertz NAMING a sha;
substitute `<HANDOFF>` for it and re-run the whole object check first.

Windows leg (hfenduleam, this box — the same self-hosted runner the red came from):
1. `git worktree add --detach .worktrees/diag-294-<HANDOFF> <HANDOFF>` — under `.worktrees/`, never
   a sibling of the project dir.
2. **Do NOT build into `.worktrees/hertz-294-sync-stages/target`** — that pool belongs to hertz's
   live lane. Claim my own from inside my worktree:
   `cargo run -p xtask -- pool-claim --pool .worktrees/diag-294-<HANDOFF>/target --label diag-294`.
   Lane-identity refusals are predicted from the BUILD, never from `pool-claim`.
3. Targeted run, not the workspace:
   `cargo nextest run -p spt-daemon --test sync -E 'test(two_tier_sync_lands_and_gate_refuses_server_side)'`
   plus `test(diag_continuation_preserves_original_failure)` for hertz's control.
   Capture raw stdout — the `IR294 {...}` lines ARE the measurement; `--no-capture` or a nextest
   profile that preserves stdout on success, otherwise a PASSING run prints none of them.
4. Box census before the run (Defender's first-touch tax on fresh test binaries is real; a cold
   35 MB exe pays 1.0-2.1 s where warm pays 20-260 ms — do not read that tax as a stage cost).
5. `grep -c 'FAIL \['` is USELESS on these logs — the banked suffix hazard fired live on r2. Read the
   Summary line and the per-cell rows.

Linux leg (kitsubito, `reavus@kitsubito` — measured reachable 15:03Z, load 0.10, 16 cores, idle):
needs the ref fetchable, so push `diag/294-sync-stages` to origin FOR FETCHABILITY (fires no
workflow — see the trigger section above), and say so when reporting it so the push is never read as
an arming act. One ssh per kill if anything must be stopped; a stopped local ssh does NOT stop its
remote command.

Reporting: name the ref and say DIAGNOSTIC TREE in the body. If the cell PASSES on either platform,
its MARGIN is the number that gets reported (`iterations`, `poll_elapsed_us`), never "passed".
