Skip to content

ci(drift): make drift-live-pr head/base legs treat collector exit-2 as non-fatal#298

Merged
jpr5 merged 1 commit into
mainfrom
fix/drift-live-pr-exit2
Jul 15, 2026
Merged

ci(drift): make drift-live-pr head/base legs treat collector exit-2 as non-fatal#298
jpr5 merged 1 commit into
mainfrom
fix/drift-live-pr-exit2

Conversation

@jpr5

@jpr5 jpr5 commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

The bug

The drift-live-pr job's head leg let the drift collector's exit code 2 (critical drift found) propagate as a step failure. But exit-2 is the expected "drift present" signal that must feed the delta gate — it has to be non-fatal for the leg so the delta gate can decide block-vs-advisory by key presence.

GitHub Actions runs run: blocks under bash -e. The head-leg step's set -uo pipefail line does not clear that -e, and there was no set +e guard around the collector call. So the instant the collector returned 2, -e aborted the step at the npx line — before HEAD_EXIT=$? was even captured — and the delta gate never ran.

On PR #297 the head leg logged Exiting with code 2 (critical diffs found) then ##[error]Process completed with exit code 2. The prior fix (#297) only hardened the base leg (which already wrapped the collector in set +e/set -e) and the no-keys degradation path; the keys-present head-leg live run still propagated exit-2.

Makes drift-live-pr functional on drift-code PRs (was merged non-functional in #297).

The fix (scoped only to .github/workflows/test-drift.yml)

Both the base-leg and head-leg keys-present (LIVE_LEGS_RAN=true) live-run steps now capture the collector's exit code with set +e/set -e and treat it as data:

  • exit 0 → clean, continue.
  • exit 2 → drift present → non-fatal, continue (the drift-report-{base,head}.json report feeds the delta gate).
  • exit 5 (quarantine) → genuine fault → fail the leg (explicit classification).
  • any other non-{0,2} → genuine collector fault → fail the leg.

Report filenames/paths are unchanged (drift-report-base.json / drift-report-head.json), so the delta gate contract is untouched. The head leg was the actual #297 bug (it lacked the set +e guard); the base leg already tolerated exit-2, and gains only the explicit exit-5 classification for symmetry.

Diff hunk

@@ head leg ("Head drift report — run live on PR ref") @@
+          # ... capture exit as DATA (see comment) ...
+          set +e
           npx tsx scripts/drift-report-collector.ts --out drift-report-head.json
           HEAD_EXIT=$?
+          set -e
+          if [ "$HEAD_EXIT" -eq 5 ]; then
+            echo "::error::Head collector quarantined unparseable output (exit 5) — manual triage required"
+            exit "$HEAD_EXIT"
+          fi
           if [ "$HEAD_EXIT" -ne 0 ] && [ "$HEAD_EXIT" -ne 2 ]; then
             echo "::error::Head collector faulted (exit $HEAD_EXIT) — not a drift signal"
             exit "$HEAD_EXIT"
           fi
           echo "head: collector exit $HEAD_EXIT (0 clean / 2 drift-present — both non-fatal here)"

(The base leg gains the same explicit exit 5 classification block ahead of its existing -ne 0 && -ne 2 fault check.)

Red-green proof (keys-present live-run path)

The harness programmatically extracts the real base-/head-leg run: bash from the YAML (python YAML parse — not hand-reconstructed), then drives it under GitHub's exact default shell (bash --noprofile --norc -e -o pipefail) with a fake npx collector on PATH that writes the expected drift-report-{base,head}.json and returns a configurable exit code. LIVE_LEGS_RAN=true forces the keys-present branch.

RED — current main YAML, head leg, fake collector exit 2 (reproduces #297)

=== RED: HEAD leg, current main YAML, fake collector exit 2 ===
===== STEP STDOUT =====
fake-collector: writing report, exiting 2 (critical diffs found)
===== STEP STDERR =====
===== STEP EXIT CODE: 2 =====
REPORT PRESENT: drift-report-head.json (103 bytes)
HARNESS RC (RED): 2

The step FAILS with exit 2 (the head: collector exit ... line never prints — -e killed the step at the npx line). This is the #297 failure.

GREEN — fixed YAML, head leg

=== HEAD leg, fake collector exit 2 ===
fake-collector: writing report, exiting 2 (critical diffs found)
head: collector exit 2 (0 clean / 2 drift-present — both non-fatal here)
===== STEP EXIT CODE: 0 =====
REPORT PRESENT: drift-report-head.json (103 bytes)
HARNESS RC (head exit 2): 0

=== HEAD leg, fake collector exit 5 ===
fake-collector: writing report, exiting 5 (critical diffs found)
::error::Head collector quarantined unparseable output (exit 5) — manual triage required
===== STEP EXIT CODE: 5 =====
HARNESS RC (head exit 5): 5

=== HEAD leg, fake collector exit 0 ===
fake-collector: writing report, exiting 0 (critical diffs found)
head: collector exit 0 (0 clean / 2 drift-present — both non-fatal here)
===== STEP EXIT CODE: 0 =====
HARNESS RC (head exit 0): 0
  • exit 2 → step succeeds (rc captured, non-fatal), report present, control reaches the post-collector line (delta gate runs).
  • exit 5 → step still fails the leg (quarantine).
  • exit 0 → clean.

GREEN — fixed YAML, base leg (same three)

=== BASE leg, fake collector exit 2 ===
base: no reusable same-UTC-day main report — running fresh live base
fake-collector(base): writing report, exiting 2
===== STEP EXIT CODE: 0 =====
REPORT PRESENT: drift-report-base.json (103 bytes)

=== BASE leg, fake collector exit 5 ===
fake-collector(base): writing report, exiting 5
::error::Base collector quarantined unparseable output (exit 5) — manual triage required
===== STEP EXIT CODE: 5 =====

=== BASE leg, fake collector exit 0 ===
fake-collector(base): writing report, exiting 0
===== STEP EXIT CODE: 0 =====

Quality

  • actionlint clean on my changed steps. (The one reported SC2086 is at the pre-existing notify-job "Check previous run status" step — not part of this change.)
  • pnpm run format:check — clean.
  • pnpm run lint — clean.
  • git status — only .github/workflows/test-drift.yml modified. Harness is a scratch tool under /tmp, not committed.

🤖 Generated with Claude Code

@pkg-pr-new

pkg-pr-new Bot commented Jul 15, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@copilotkit/aimock@298

commit: ea974ba

@jpr5
jpr5 merged commit 06e51c7 into main Jul 15, 2026
18 checks passed
@jpr5
jpr5 deleted the fix/drift-live-pr-exit2 branch July 15, 2026 18:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant