feat(flows-yaml): port CI events to YAML (ci_run trigger + pr-ci.yml + pr-opened.yml rewrite) #1097
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!1097
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/flows-yaml-ci-events"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Closes #1092. Restores the silent fix-ci + reviewer-request paths that PR #1090 surfaced as broken.
Surface
ci_run(Forgejostatus+action_run_*, GitHubworkflow_run, GitLab pipeline) as a YAML trigger and replace the orphaned imperative handlers with flows.Trigger surface
CiRunTriggerinflows-yaml/trigger-events.ts;"ci_run"added toFORGE_TRIGGER_KINDSwebhook-adapter.ts:185converts the wire-shapedForgeEventintoCiRunTrigger(was dropping it on the floor)schema.tsparseson: { ci_run: [success, failure] }Capability + ops
PostCiCapabilityinflows-yaml/types.ts; live + shadow bags wire it;LiveCapabilitiesDepsextended withpostCidomain/workflow/post-ci.ts:repo_has_workflows,arm_review_fallback,cancel_review_fallback,find_open_pr_for_head,fetch_aggregate_ci_state,dispatch_fix_ci,decide_post_ci(composite — keeps decision tree atomic)main.tswires thepostCicapability slot viafindOpenPRForHead+renderPrompt(fix-ci skill) +decidePostCiActionFlows
pr-opened.ymlrewritten: drop thearea:agentsgate that silently skipped PR #1090; arm the 180 s CI fallback timer instead of dispatching the reviewer immediatelypr-ci.yml: cancel fallback → find PR → dispatch fix-ci (red) or aggregate-confirmeddecide_post_ci(green)Cleanup
handlePullRequestOpened+handleCheckSuiteCompletedfromevent-handlers.ts(no non-test callers)flows.schema.json(22 ops now)Test plan
pr-ci.ymlflow test covers success + failure + no-PR + pending-aggregate pathspr-opened.ymltest rewritten for new flow shape; covers no-workflows shortcut + workflows-present arming + thearea:agentsgate removal (#1090 regression test)bun run qaclean (typecheck + biome + tests)Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Review — PR #1097: feat(flows-yaml): port CI events to YAML
CI: ✅ green
Verdict: APPROVE
What I read
Full diff reviewed end-to-end: trigger surface, webhook adapter, 7 new ops + tests, both flow YAML files, capability wiring in
main.ts, shadow/live capability builders, and all test helpers.What's well done
webhook-adapter.ts—ci_runmapping is correct.pendingis dropped,erroris collapsed ontofailure, yielding a clean two-state(success | failure)surface for YAML authors. Test coverage for all four wire states is solid.pr-opened.ymlrewrite is the right fix for #1090.The
area:agentsgate was the root cause — any PR without that label was silently skipped forever. The replacement!author_is('reviewer', 'design-reviewer') && event.pr.headSha != nullis minimal and correct. The headSha null guard mirrors the legacyif (!sha) return;bail-out.pr-ci.ymlflow logic is sound.cancel_review_fallbackunconditionally fires first (right — you want to cancel the timer even when no PR is found). The aggregate-state guard on the success path mirrors the legacyhandleCheckSuiteCompletedexactly. The "pending aggregate → no-op" path is tested.All 7 new ops follow the established pattern cleanly — thin adapters delegating to the
postCicapability, proper Zod schemas,deps: ["postCi"] as const. Op-level unit tests are thorough (schema rejection, happy path, branch-hint forwarding, null defaults).Deletions are clean.
handlePullRequestOpened+handleCheckSuiteCompleted+ their tests are gone with no dangling callers. TheisReviewerAgentimport is correctly dropped fromevent-handlers.ts.One thing to note (not a blocker)
Double
findOpenPRForHeadcall per CI event.In
main.ts, bothdispatchFixCianddecidePostCicapability impls silently ignore their_prNumberparameter and do a freshfindOpenPRForHeadlookup:The flow already resolved the PR number in
find_open_pr_for_head; the capability then does a second full API probe becausedomain/workflow/post-ci.ts::dispatchFixCi/decidePostCiActionneed the complete PR object (title, user.login, headRef), not just the number. The design tradeoff is documented by the underscore prefix.This adds two extra Forgejo API calls per completed CI event. It's a stepping-stone cost — a follow-up could expose
getPRByNumberin the capability to turn this into a single cheap lookup. Not blocking, but worth a// TODOcomment in the adapter body so it's visible.Minor
pr_numberfield isrequiredindecide_post_ci's JSON schema and Zod argsSchema, is forwarded through the op → capability →buildPostCiCapability→deps.decidePostCiin main.ts, and then ignored. This is consistent behaviour across the live/shadow impls, but anyone reading just the schema will expect it to be meaningful. The existing underscore prefix on_prNumberin main.ts is the only signal. A one-liner comment indecide_post_ci.ts(// prNumber forwarded for observability / future use; live impl re-resolves the full PR object) would help.Summary
The porting strategy is correct, the test coverage is comprehensive (3240 pass), CI is green, and the #1090 / #1092 regressions are properly fixed. The double-lookup is the only inefficiency and it's a known tradeoff, not a correctness issue. Ship it.