Skip to content

fix: only treat a crash with a cascade frame as non-transient - #147

Merged
joshua-temple merged 1 commit into
mainfrom
fix/e2e-crash-classifier-cascade-frames
Jun 13, 2026
Merged

joshua-temple merged 1 commit into
mainfrom
fix/e2e-crash-classifier-cascade-frames

Conversation

@joshua-temple

Copy link
Copy Markdown
Collaborator

Problem

The e2e crash classifier from #146 is over-aggressive and causes false CI reds. It flagged any act exit carrying a Go crash signature as a non-transient hard failure, including a bare goroutine-dump header with no real trigger.

On CI run 27480827218, Hotfix_Refusal_Guards step 2 failed with a bare goroutine dump (goroutine 470 [wselect]:) emitted by act's own monitorJobCancellation teardown watchdog under cancel-in-progress: no panic:/fatal error:/signal trigger line, and zero stablekernel/cascade frames (only nektos/act frames). That scenario passes 4/4 locally with 0 retries, so it was a pure act-infra teardown flake that retry should absorb, not a real bug.

Fix

Refine detectCrashSignature so a crash is classified non-transient only when BOTH hold:

  1. A real runtime trigger is present: panic: or fatal error: at the start of a (trimmed) line, or a signal SIG(SEGV|ABRT|BUS|FPE|ILL) report. The standalone goroutine-dump-header trigger is removed (act prints full goroutine dumps routinely on cancellation teardown).
  2. The dump contains a real cascade stack frame, matched strictly as a function frame (stablekernel/cascade/(internal|cmd)... followed by a function/receiver identifier), so it does not match git-URL prose, stdlib internal/poll, or vendored .../internal/... packages.

A non-zero act exit lacking either condition stays transient and retryable, preserving #146's benign-transient path and the real-job-failure path. A genuine cascade-CLI panic still carries both a trigger and cmd/cascade+internal/... frames, so it is still caught and fast-failed.

Verification

  • New/updated unit tests in e2e/harness/crash_test.go cover: panic+cascade frame (crash), [signal SIGSEGV]+cascade frame (crash), act-only panic (transient), bare act monitorJobCancellation dump (transient), trigger-without-frame and frame-without-trigger (both transient), and git-URL prose (not a crash).
  • go build ./... + go test ./... (root), go build/vet ./... + go test ./harness/... (e2e): all green.
  • golangci-lint run ./... clean on both modules.
  • Both hotfix scenarios pass locally on attempt 1 with no retries.

The crash classifier introduced in #146 flagged any act exit carrying a Go crash signature as a non-transient hard failure. On CI run 27480827218 this hard-failed Hotfix_Refusal_Guards on a bare goroutine dump from act's own monitorJobCancellation teardown watchdog (under cancel-in-progress): no panic/fatal/signal trigger and zero cascade frames, just nektos/act frames. That scenario passes locally with no retries, so it was an act-infra teardown flake that retry should absorb, not a real bug.

Refine detectCrashSignature to classify a crash as non-transient only when BOTH a real runtime trigger (panic:/fatal error: at line start, or a signal SIG report) AND a real cascade stack frame are present. Drop the standalone goroutine-dump-header trigger. A non-zero act exit lacking either stays transient and retryable, preserving #146's benign-transient and real-job-failure paths. A genuine cascade-CLI panic still carries both a trigger and cmd/cascade or internal/... frames, so it is still caught and fast-failed.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
@joshua-temple
joshua-temple merged commit 61362e9 into main Jun 13, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant