Quickstart: CI Terminal-Cancel Verdict

What ships

  • scripts/ci/fleet_verdict.py — classify gains infra-error (4a) + comment line + docstring.
  • scripts/ci/stale_running_sweep.py (new) — 4b stale-running detector.
  • tests/ci/test_fleet_verdict.py / test_fleet_main.py — red-first 4a tests + migration.
  • tests/ci/test_stale_running_sweep.py (new) — 4b detector + wiring guard.
  • .github/workflows/ci-stale-running-sweep.yml (new) — scheduled 4b host.

Run the tests (from repo root)

export PYTHONPATH=$(pwd)/src
uv sync --frozen --all-extras   # avoid stale-venv false reds (coverage/pytestarch)
uv run --frozen python -m pytest tests/ci/test_fleet_verdict.py tests/ci/test_fleet_main.py tests/ci/test_stale_running_sweep.py -q

Pre-push gates

export PYTHONPATH=$(pwd)/src
uv run --frozen python -m pytest tests/ci/ -q
uv run --frozen ruff check .
uv run --frozen ruff format --check .            # separate gate
uv run --frozen python -m pytest tests/architectural/test_no_legacy_terminology.py -q
uv run --frozen python -m pytest tests/contract/test_example_round_trip.py -q   # contract yaml skip marker
./actionlint .github/workflows/ci-stale-running-sweep.yml   # download binary; paste RAW output

Manual smoke of classify

# fully-terminal + a cancelled required gate -> infra-error:
python3 -c "import sys; sys.path.insert(0,'scripts'); from ci.fleet_verdict import classify; \
print(classify({'g':{'status':'completed','conclusion':'cancelled'}}, set()))"   # -> infra-error
# cancel among an in-flight gate stays running (INV-2):
python3 -c "import sys; sys.path.insert(0,'scripts'); from ci.fleet_verdict import classify; \
print(classify({'g':{'status':'completed','conclusion':'cancelled'},'h':{'status':'in_progress'}}, set()))"  # -> running

Post-merge verification (main-tip; NFR/SC)

  • A real cancelled required run posts [ci] infra-error @head (not stranded on running) and creates no P0 for a cancelled main run.
  • The scheduled sweep runs and (if any) flags a stale-running head as a [ci-sweep] watch item without releasing it.