Skip to content

fix(triggers): surface handler self-skip reason in webhook decisionReason#1235

Merged
zbigniewsobiecki merged 3 commits intodevfrom
fix/respond-to-ci-diagnostics
Apr 29, 2026
Merged

fix(triggers): surface handler self-skip reason in webhook decisionReason#1235
zbigniewsobiecki merged 3 commits intodevfrom
fix/respond-to-ci-diagnostics

Conversation

@zbigniewsobiecki
Copy link
Copy Markdown
Member

Summary

Closes the diagnostic gap that hid why zbigniewsobiecki/ucho#155's CI failure didn't trigger `respond-to-ci`. Webhook log decisionReason was `'No trigger matched for event'` — the same string used when no matcher matched at all, even though one DID match and self-skipped inside its handler.

Three coordinated fixes

Fix A — Plumb structured `skipReason` through TriggerResult → registry → webhook-processor

`TriggerResult` gains an optional `skipReason: { handler: string; message: string }`. Handlers return a structured skip instead of bare null; the registry returns the structured skip up the stack (terminating dispatch). `webhook-processor.ts` surfaces `Trigger ${handler} skipped: ${message}` in decisionReason. Bare `return null` keeps the legacy "try-next-handler" semantic for backward compat.

Every `return null` site in `check-suite-failure.ts` and `pr-conflict-detected.ts` converted to structured skips with specific messages (PR # included, attempt counts spelled out, base branches named).

Fix B — Stop swallowing persona-resolution failures

`GitHubAdapter.resolvePersonaCached` had a bare `catch {}` that hid every persona-token failure. Now logs ERROR + Sentry-captures under tag `persona_identity_resolution_failed`. One of the prime suspects for what hit PR #155.

Fix C — Widen "PR author must be implementer" gate to "any cascade persona"

`check-suite-failure.ts` and `pr-conflict-detected.ts` previously compared only against `personaIdentities.implementer`. Now use the existing `isCascadeBot()` helper, matching either persona. `pr-comment-mention.ts` and `review-requested.ts` already had the broader semantic.

For PR #155 specifically: aaight IS the implementer for ucho, so Fix C doesn't change the outcome — but with Fix A shipped, any re-run will report the actual blocker in the dashboard webhook log.

Test plan

  • New tests in `tests/unit/triggers/registry.test.ts` for the structured-skip flow (return shape, terminate-on-skip, legacy bare-null still continues).
  • New test in `tests/unit/router/webhook-processor.test.ts` for the new decisionReason format.
  • Existing `check-suite-failure` and `pr-conflict-detected` tests rewritten to use `expectSkip(result, /pattern/)` helper and assert specific `skipReason.message` values.
  • New "fires when PR is authored by reviewer persona" test for Fix C.
  • All 8732 unit tests pass.
  • All 550 integration tests pass.
  • `npm run lint` clean (13 pre-existing warnings).
  • `npm run typecheck` clean.

🤖 Generated with Claude Code

zbigniewsobiecki and others added 3 commits April 29, 2026 19:05
Merge dev → main: Fix 1 (BullMQ coalesce unique jobIds) + Fix 2 (snapshot rmi on PR merge)
Merge dev → main: backlog-manager scope safety (#1233)
…ason

PR zbigniewsobiecki/ucho#155 had a CI failure that should have triggered
respond-to-ci. The webhook arrived; cascade-router logged
"No trigger matched for event" and bailed. The trigger DID match —
something inside the handler self-skipped, but the persisted webhook log
flattened the reason into the same generic string used when no matcher
matches at all. Debugging that required trawling cascade-router process
logs.

Three coordinated fixes:

A) Diagnostic upgrade. Extend TriggerResult with `skipReason: { handler,
   message }`. Handlers can return a structured skip instead of bare null;
   the registry returns the structured skip up the stack (terminating
   dispatch, not iterating further). webhook-processor.ts surfaces
   `Trigger ${handler} skipped: ${message}` in decisionReason. Bare
   `return null` keeps the legacy "try-next-handler" semantic for
   backward compatibility.

   Converted every return-null site in check-suite-failure.ts and
   pr-conflict-detected.ts to structured skips with specific messages
   (PR # included, attempt counts spelled out, base branches named).

B) Stop swallowing persona-resolution failures in
   `GitHubAdapter.resolvePersonaCached`. The bare `catch {}` made every
   downstream gate that depends on personaIdentities silently skip
   without any signal. Now logs ERROR + Sentry-captures under tag
   `persona_identity_resolution_failed`.

C) Widen the "PR author must be implementer persona" gate in both
   check-suite-failure.ts and pr-conflict-detected.ts to use the
   existing `isCascadeBot()` helper, matching either the implementer OR
   reviewer persona. The narrow check would have silently dropped any
   future PR authored by the reviewer persona; pr-comment-mention.ts and
   review-requested.ts already had the broader semantic.

For PR #155 specifically: aaight IS the implementer for ucho, so the
gate-widening (C) doesn't change the outcome — but with (A) shipped, any
re-run will report the actual reason in the dashboard webhook log.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@zbigniewsobiecki zbigniewsobiecki merged commit 17783cf into dev Apr 29, 2026
8 checks passed
@zbigniewsobiecki zbigniewsobiecki deleted the fix/respond-to-ci-diagnostics branch April 29, 2026 18:44
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 29, 2026

Codecov Report

❌ Patch coverage is 90.72165% with 9 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/router/adapters/github.ts 0.00% 9 Missing ⚠️

📢 Thoughts on this report? Let us know!

zbigniewsobiecki added a commit that referenced this pull request Apr 29, 2026
Merge dev → main: respond-to-ci diagnostics + persona-scope hardening (#1235)
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