fix(triggers): correct pr-conflict-detected gating event so resolve-conflicts actually fires#1217
Merged
zbigniewsobiecki merged 1 commit intodevfrom Apr 27, 2026
Conversation
…onflicts actually fires
The gating call passed `'scm:conflict-resolution'` to `checkTriggerEnabled`, but
every other surface (the agent YAML, the trigger-types registry the dashboard /
CLI write through, and the handler's own emitted `triggerEvent` on AgentInput)
uses `'scm:pr-conflict-detected'`. So `isTriggerEnabled('resolve-conflicts',
'scm:conflict-resolution')` always missed the operator's enabled DB row, fell
back to the agent definition's `defaultEnabled: false`, and silently returned
null on every `pull_request synchronize` webhook.
Confirmed via `cascade webhooklogs` on 2026-04-27: 6+ synchronize events for
ucho on PRs #81 and #85 in an 11-minute window each returned `decisionReason:
"No trigger matched for event"` despite `cascade projects trigger-list ucho`
showing `resolve-conflicts | scm:pr-conflict-detected | enabled=yes`. The
trigger handler's `matches()` returned true; only the gating string was wrong.
Conflict detection has been silently broken in production for every project
that "enabled" it via the dashboard, since the same commit that standardized
the emitted `triggerEvent` to `'scm:pr-conflict-detected'` everywhere else
left this one gating call out of the rename.
Fix: one-line string change. The existing handler test was codifying the bug
(it asserted `checkTriggerEnabled` was called with `'scm:conflict-resolution'`)
and is updated to pin the correct value.
Audit: cross-checked all 20 `checkTriggerEnabled` / `checkTriggerEnabledWithParams`
call sites in `src/triggers/` against each handler's emitted `triggerEvent`.
This was the only mismatch.
Guard: added `tests/unit/triggers/trigger-event-consistency.test.ts` — a
static-analysis test that scans every trigger handler file under
`src/triggers/`, extracts the gating event string from each
`checkTriggerEnabled(...)` call and the emitted `triggerEvent: '...'`
property, and asserts every gating event appears in the file's emitted set.
The test fails loudly with a precise file:line reference on any future drift,
listing the actual events found and pointing the contributor at either fixing
the mismatch or adding an explicit `EXEMPT_FILES` entry with a reason.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
nhopeatall
approved these changes
Apr 27, 2026
Collaborator
nhopeatall
left a comment
There was a problem hiding this comment.
LGTM — the one-character fix is correct and the guard test is well-designed.
🕵️ claude-code · claude-opus-4-6 · run details
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Conflict detection has been silently disabled in production for every project that "enabled" it via the dashboard, since the same commit that standardized the emitted
triggerEventto'scm:pr-conflict-detected'everywhere else left one gating call out of the rename.The handler at
src/triggers/github/pr-conflict-detected.ts:43passed'scm:conflict-resolution'tocheckTriggerEnabled, but every other surface (the agent YAML, thetriggerTypes.tsregistry the dashboard / CLI write through, and the handler's own emittedtriggerEventonAgentInput) uses'scm:pr-conflict-detected'. SoisTriggerEnabled('resolve-conflicts', 'scm:conflict-resolution')always missed the operator's enabled DB row, fell back to the agent definition'sdefaultEnabled: false, and silently returned null.Production confirmation
cascade webhooklogson 2026-04-27 — sixpull_request synchronizeevents on ucho PRs #81 and #85 in an 11-minute window each returned:despite
cascade projects trigger-list uchoshowing:The handler's
matches()returned true (it only checkspayload.action === 'synchronize'); only the gating string was wrong.Audit
Cross-checked all 20
checkTriggerEnabled/checkTriggerEnabledWithParamscall sites insrc/triggers/against each handler's emittedtriggerEvent. This was the only mismatch. Every other handler's gating event matches its emitted event matches its agent YAML's declared event:pr-conflict-detected.tsGuard against future drift
Added
tests/unit/triggers/trigger-event-consistency.test.ts— a static-analysis test that scans every trigger handler file undersrc/triggers/, extracts the gating event string from eachcheckTriggerEnabled(...)call and the emittedtriggerEvent: '...'property, and asserts every gating event appears in the file's emitted set. The test fails loudly with a precisefile:linereference on any future drift, listing the actual events found and pointing the contributor at either fixing the mismatch or adding an explicitEXEMPT_FILESentry with a reason.The guard test catches this exact bug — when run against the pre-fix code, it produces:
Also
checkTriggerEnabledwas called with'scm:conflict-resolution'). Updated to pin the correct value.Test plan
npx vitest run tests/unit/triggers/pr-conflict-detected.test.ts→ 16/16).npm test→ 469 files / 8631 tests pass / 23 skipped)."No trigger matched for event"to"Job queued: resolve-conflicts agent for work item N".🤖 Generated with Claude Code