feat(flows): wire executor into webhook pipeline (NF-6) #360
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
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!360
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/327-nf6-executor-wiring"
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?
Replaces the stalled #359 attempt, which flipped the config mode to
"live"but admitted no webhook → executor bridge existed — that flip was a no-op at runtime. This PR ships the actual bridge.Summary
New
apps/server/src/domain/flows/flow-dispatch.ts:dispatchToFlows(event, opts?)— normalizes toTriggerEvent, readsnode_flows.mode, short-circuits on"off", lists enabled flows, filters bytrigger.kind+on.filters, compiles each matching graph, runs the executor with the rightmutationMode("dry-run"→ NF-5 intent recording;"live"→ real side effects), persists oneflow_runsrow + oneflow_node_runsrow per executed node.matchesFilters(filters, trigger)— evaluatesrepo_patterns(glob with*/**),issue_labels_any/all,pr_labels_any/all,author_is,comment_regex. Non-applicable filters fail closed on triggers that don't carry the predicate's field.Wired into all three handlers in
apps/server/src/http/webhook.ts:normalizeForgejoPayloadand fire-and-forgetdispatchToFlows.ForgeEventin hand post-MF-3; dispatch directly after the 204. These routes had no legacy dispatch, so flows are now their only consumer.Design notes
node_flows.modestays"off". Operator flips to"dry-run"for the soak window;"live"after the 7-day observation per spec § Migration.flow_node_runs.inputis intentionally"{}"for v1; the executor doesn't surface resolved inputs. Adding that is an observability follow-up, not a correctness blocker for cutover.FlowNodeRunStatusdoes not model"cancelled"; the bridge folds cancelled → skipped at persistence time (run-level status captures"cancelled"in the outerflow_runsrow).Test plan
bun test apps/server/src/domain/flows/flow-dispatch.test.ts— 20/20, 30 expects.bun test apps/server— 1734 pass, 4 pre-existing fails (sweeper JSONL pruning × 3, foreman session CRUD × 1, all confirmed on main).bun x turbo run typecheck— 4/4 clean.bun x biome check— clean on touched files (--fixapplied once for import-order/format).node_flows.modeto"dry-run"inconfig/agents.json, restart, send a synthetic webhook, confirmGET /flows/runsshows the matching flow fired andflow_node_runs.intentcaptured.GET /flows/divergencesagainsttask_history; flip to"live"if <5% divergence per NF-5 spec.Out of scope
"live"— operator action after soak.flow_node_runs.input— observability follow-up.Closes #327. Supersedes #359.
🤖 Generated with Claude Code