Quickstart: verifying Stage 1 on the merged main tip

Stage 1 is a main-tip behavior. The change alters the very lane that would validate the PR carrying it, so it is only fully proven on main after the operator merges the PR ("a gate never run is not a gate"). This runbook is the acceptance test.

Pre-merge (on the PR head — necessary but NOT sufficient)

# 1. Golden-YAML + dedup pins (the ci module shard)
PWHEADLESS=1 .venv/bin/python -m pytest tests/ci/test_fleet_verdict.py tests/ci/test_fleet_main.py \
  tests/architectural/test_dual_mode_contract.py -q

# 2. Manual workflow lint (no CI gate exists for these) — PASTE THE RAW OUTPUT IN THE PR
actionlint .github/workflows/ci-router.yml .github/workflows/ci-fleet-verdict.yml
shellcheck $(…any changed inline shell…)   # both must report 0 findings

# 3. Never-green + aggregate guard untouched
PWHEADLESS=1 .venv/bin/python -m pytest tests/ci/test_reconcile_shards.py -q

# 4. Diff-scope check (NFR-001 file-scope, Renata) — must be EMPTY
git diff --name-only upstream/main...HEAD | grep -E 'ci-aggregate|reconcile_shards|aggregate_source' || echo "OK: no aggregate/source-eligibility files touched"

Record the passed/failed counts in the PR body, and paste the raw actionlint/shellcheck invocation + full output (not a bare "0 findings" — an anti-laziness self-report is not evidence).

Post-merge (on the merged main tip — the real gate)

Run these against upstream after the operator merges. Use /attempts/ (plural) for any jobs-API call.

# SC-001 — one coalesced fleet verdict per landed tip (was ~60)
gh run list --repo spec-kitty/spec-kitty --workflow "CI Fleet Verdict" --branch main -L 60 \
  --json headSha,status,conclusion,createdAt | \
  jq 'group_by(.headSha) | map({sha: .[0].headSha, runs: length})'
# EXPECT: each landed SHA shows a small, bounded count (≈1 coalesced), not dozens.
#   (Fix round 1: CI-Aggregate-triggered runs are singleton-scoped on the aggregate
#   run id — NOT coalesced into the tip group — because their head_sha is the current
#   main tip, not the subject they verified; each active PR's aggregate completion adds
#   one run to the tip it landed on. "Bounded, not dozens" is the criterion, and no
#   run's cancellation may be attributable to another subject's trigger.)

# SC-002 — no main tip landed without a terminal evaluation during a burst
gh run list --repo spec-kitty/spec-kitty --workflow "CI Router" --branch main -L 30 \
  --json headSha,status,conclusion | \
  jq 'map(select(.conclusion=="cancelled"))'
# EXPECT: [] for cancels caused by a superseding main push (per-SHA groups don't cancel siblings).

# SC-003 — #4208 router-gate correct under the new distribution
#   Inspect a landed tip's router-gate job conclusion; a legitimately cancelled dependency
#   still blocks, and no tip is falsely blocked by an external-cancel that 1a removed.
gh run view <run-id> --repo spec-kitty/spec-kitty --json jobs | jq '.jobs[] | {name, conclusion}'

# SC-006 — every landed main SHA carries a TERMINAL verdict (detects the survivor-no-successor wedge)
#   For each landed main SHA, confirm a terminal fleet verdict exists and is NOT stranded on running.
#   (main-push path posts to the from:ci incident issue / commit; inspect the ledger per SHA.)
gh api repos/spec-kitty/spec-kitty/commits/<sha>/comments --jq '.[].body' | grep -E '^\[ci\] (green|red) @'
#   EXPECT a match per landed SHA; a bare '[ci] running @<sha>' with no later terminal line is the
#   residual wedge (report it; the running-sweep self-heal lands in Stage 3 / ADR 4b).

Acceptance ledger (fill on the merged tip)

CriterionCommandExpectedObserved
SC-001 coalesced verdict/tipgh run list … CI Fleet Verdict≈1 per SHA (was ~60)_to fill on merge_
SC-002 no unevaluated landinggh run list … CI Router0 superseding-push cancels_to fill on merge_
SC-003 #4208 classifier correctgh run view … jobscorrect pass/block_to fill on merge_
SC-004 lint cleanactionlint/shellcheck (raw output in PR)0 findings_pre-merge_
SC-005 pins greenpytest tests/ci …all green_pre-merge_
SC-006 terminal verdict/tipgh api …/commits/<sha>/comments`[ci] green\red @sha, never stranded running`

A main tip reported green on unproven content, or a permanently-wedged main verdict, is a violation of this ADR and of ADR 2026-07-17-1 — treat it as a finding, not a licence to improvise.

If a stage-1 defect is found on main

Do not hot-patch around it. Open a finding against ADR 2026-09-15-1, and (per the staged rollout) do not start Stage 2 (ci-aggregate-source-eligibility) until Stage 1 is verified green on the merged main tip.