Port CI events to YAML flows (ci_run trigger + pr-ci.yml + pr-opened.yml rewrite) #1092

Closed
opened 2026-05-10 23:29:42 +00:00 by claude-desktop · 0 comments
Collaborator

As an operator, I want CI completion events to drive flow-engine dispatch (fix-ci on red, reviewer request on green), so that PRs don't sit forever without a reviewer (currently PR #1090 is silently blocked because ci_run events are dropped by the YAML adapter).

Context

apps/server/src/domain/flows-yaml/webhook-adapter.ts:183 explicitly drops ci_run events with comment "CI events live outside the YAML trigger surface in v1 (the legacy NF-1 union still owns check_suite.completed)". But the legacy handler handleCheckSuiteCompleted (apps/server/src/domain/workflow/event-handlers.ts:1000) is dead code — no non-test caller. Net effect: no fix-ci dispatch on red CI, no reviewer request on green CI. Only path remaining is the janitor stale_fix_ci_redispatch safety net (6 h cadence).

Also: flows/defaults/pr-opened.yml short-circuits any PR without area:agents label (PR #1090 had area:dashboard), and dispatches reviewer immediately without waiting for CI. Both behaviours need rewiring.

Acceptance criteria

Trigger surface

  • Add CiRunTrigger to apps/server/src/domain/flows-yaml/trigger-events.ts (kind ci_run, fields: state (success|failure), sha, context, runId, workflowId, branches)
  • Append "ci_run" to FORGE_TRIGGER_KINDS
  • apps/server/src/domain/flows-yaml/webhook-adapter.ts:183 converts ci_run ForgeEvent to CiRunTrigger (currently returns null)
  • apps/server/src/domain/flows-yaml/schema.ts parses on: { ci_run: [success, failure] } syntax

New ops (thin wrappers around existing implementations in post-ci.ts + event-handlers.ts)

  • repo_has_workflows op — wraps ForgePort.repoHasWorkflows
  • arm_review_fallback op — wraps armReviewFallback
  • cancel_review_fallback op — wraps cancelReviewFallback
  • find_open_pr_for_head op — wraps findOpenPRForHead; outputs {found, pr_number, title, author, head_ref}
  • fetch_aggregate_ci_state op — wraps fetchAggregateState; outputs {state}
  • dispatch_fix_ci op — wraps dispatchFixCi
  • decide_post_ci op — wraps decidePostCiAction (composite — keeps approve/merge/bounce decision tree atomic)

Flows

  • Rewrite flows/defaults/pr-opened.yml — drop area:agents gate, arm CI fallback when workflows present, otherwise call decide_post_ci directly
  • New flows/defaults/pr-ci.yml on ci_run: [success, failure]: cancel fallback timer → find PR for head SHA → dispatch fix-ci (red) or decide_post_ci after aggregate-confirm (green)

Tests

  • Unit tests for each new op (under apps/server/src/domain/flows-yaml/ops/*.test.ts)
  • Adapter test for ci_run event conversion (webhook-adapter.test.ts)
  • Schema test for the new trigger surface
  • Flow integration test for pr-ci.yml (success + failure paths)
  • Update pr-opened.yml tests to drop the area:agents gate assertion

Cleanup

  • Delete handlePullRequestOpened from event-handlers.ts (dead after rewrite)
  • Delete handleCheckSuiteCompleted from event-handlers.ts (dead after pr-ci.yml lands)
  • Delete their tests

Out of scope

  • Slash commands (/hold, /ready, etc.) — separate ticket
  • PR-closed cleanup chain (cleanupBranch + dep drop + pr.merged publication) — separate ticket
  • legacy* executor hooks in main.ts — separate ticket
  • Janitor stale_fix_ci_redispatch rule — remains as 6 h safety net; verify dedup map shared with new webhook path

References

  • Root-cause analysis in this conversation
  • apps/server/src/domain/workflow/post-ci.ts (decision tree + fallback timer impl)
  • apps/server/src/domain/workflow/event-handlers.ts:1000 (orphaned handleCheckSuiteCompleted)
  • PR #1090 — symptom (no reviewer requested despite CI green)
  • Janitor rule stale_fix_ci_redispatch (background/janitor.ts:774) — the only currently-active fix-ci path
As an operator, I want CI completion events to drive flow-engine dispatch (fix-ci on red, reviewer request on green), so that PRs don't sit forever without a reviewer (currently PR #1090 is silently blocked because `ci_run` events are dropped by the YAML adapter). ## Context `apps/server/src/domain/flows-yaml/webhook-adapter.ts:183` explicitly drops `ci_run` events with comment "CI events live outside the YAML trigger surface in v1 (the legacy NF-1 union still owns `check_suite.completed`)". But the legacy handler `handleCheckSuiteCompleted` (`apps/server/src/domain/workflow/event-handlers.ts:1000`) is dead code — no non-test caller. Net effect: no `fix-ci` dispatch on red CI, no reviewer request on green CI. Only path remaining is the janitor `stale_fix_ci_redispatch` safety net (6 h cadence). Also: `flows/defaults/pr-opened.yml` short-circuits any PR without `area:agents` label (PR #1090 had `area:dashboard`), and dispatches reviewer immediately without waiting for CI. Both behaviours need rewiring. ## Acceptance criteria ### Trigger surface - [ ] Add `CiRunTrigger` to `apps/server/src/domain/flows-yaml/trigger-events.ts` (kind `ci_run`, fields: `state` (`success`|`failure`), `sha`, `context`, `runId`, `workflowId`, `branches`) - [ ] Append `"ci_run"` to `FORGE_TRIGGER_KINDS` - [ ] `apps/server/src/domain/flows-yaml/webhook-adapter.ts:183` converts `ci_run` `ForgeEvent` to `CiRunTrigger` (currently returns `null`) - [ ] `apps/server/src/domain/flows-yaml/schema.ts` parses `on: { ci_run: [success, failure] }` syntax ### New ops (thin wrappers around existing implementations in `post-ci.ts` + `event-handlers.ts`) - [ ] `repo_has_workflows` op — wraps `ForgePort.repoHasWorkflows` - [ ] `arm_review_fallback` op — wraps `armReviewFallback` - [ ] `cancel_review_fallback` op — wraps `cancelReviewFallback` - [ ] `find_open_pr_for_head` op — wraps `findOpenPRForHead`; outputs `{found, pr_number, title, author, head_ref}` - [ ] `fetch_aggregate_ci_state` op — wraps `fetchAggregateState`; outputs `{state}` - [ ] `dispatch_fix_ci` op — wraps `dispatchFixCi` - [ ] `decide_post_ci` op — wraps `decidePostCiAction` (composite — keeps approve/merge/bounce decision tree atomic) ### Flows - [ ] Rewrite `flows/defaults/pr-opened.yml` — drop `area:agents` gate, arm CI fallback when workflows present, otherwise call `decide_post_ci` directly - [ ] New `flows/defaults/pr-ci.yml` on `ci_run: [success, failure]`: cancel fallback timer → find PR for head SHA → dispatch fix-ci (red) or `decide_post_ci` after aggregate-confirm (green) ### Tests - [ ] Unit tests for each new op (under `apps/server/src/domain/flows-yaml/ops/*.test.ts`) - [ ] Adapter test for `ci_run` event conversion (`webhook-adapter.test.ts`) - [ ] Schema test for the new trigger surface - [ ] Flow integration test for `pr-ci.yml` (success + failure paths) - [ ] Update `pr-opened.yml` tests to drop the `area:agents` gate assertion ### Cleanup - [ ] Delete `handlePullRequestOpened` from `event-handlers.ts` (dead after rewrite) - [ ] Delete `handleCheckSuiteCompleted` from `event-handlers.ts` (dead after `pr-ci.yml` lands) - [ ] Delete their tests ## Out of scope - Slash commands (`/hold`, `/ready`, etc.) — separate ticket - PR-closed cleanup chain (`cleanupBranch` + dep drop + `pr.merged` publication) — separate ticket - `legacy*` executor hooks in `main.ts` — separate ticket - Janitor `stale_fix_ci_redispatch` rule — remains as 6 h safety net; verify dedup map shared with new webhook path ## References - Root-cause analysis in this conversation - `apps/server/src/domain/workflow/post-ci.ts` (decision tree + fallback timer impl) - `apps/server/src/domain/workflow/event-handlers.ts:1000` (orphaned `handleCheckSuiteCompleted`) - PR #1090 — symptom (no reviewer requested despite CI green) - Janitor rule `stale_fix_ci_redispatch` (`background/janitor.ts:774`) — the only currently-active fix-ci path
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
charles/claude-hooks#1092
No description provided.