feat(flows): NF-6 per-trigger legacy bypass gate #375
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
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!375
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/flows-suppress-legacy-gate"
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?
First piece of the NF-6 cutover machinery: a per-trigger gate that lets the operator skip the hard-coded webhook switch on a kind-by-kind basis once a matching flow is ready.
What this PR does
New
agents.jsonfield:node_flows.suppress_legacy: TriggerKind[]. When a kind is listed, the Forgejo webhook entry point skips the hard-coded handler switch for that kind.dispatchToFlowsstill fires below the gate so amode: "live"flow takes over the surface.Per-trigger granularity = each NF-6 surface flips with a one-line config edit, no deploy.
apps/server/src/shared/config/webhook-config.ts— parses + validates againstTRIGGER_KINDS, dedups, frozenapps/server/src/http/webhook.ts— hoistsnormalizeForgejoPayload+toTriggerEventahead of the legacy switch; the switch is skipped whentriggerForGate.kindis suppressed; reuses the already-normalised event fordispatchToFlowsapps/server/src/shared/config/flow-mode.test.ts— 7 new test cases for parsingapps/server/src/http/webhook-suppress-legacy.test.ts— 4 integration tests covering legacy default-on, gate skips legacy, non-suppressed kinds still run, unmodelled events fall through safelyOperator usage
→ legacy handler skipped for
issues.assigned, only the matching flow fires. Other events keep the legacy path until their own flow lands.What was reverted (commit
9c53eae)The earlier Phase 1 commit (
748c35b) attempted to upgrade the baked-indefaultflow so it could stand in forhandleIssueAssigned. Code review caught three blockers that make the upgraded flow non-cuttable as authored — reverted to keep this PR scoped to the gate alone:agent-nodes.ts toTaskRequestbuilds aTaskRequestwithforgejo_token: "pending", rawtask: "implement"literal (not the rendered skill template), and no model / system_prompt / appendix. LegacydispatchIssueForAgent(webhook-handlers.ts:567-655) loads the skill viaskillForEvent, runsinterpolate, layersapplyAppendix+maybeApplyCavemanAppendix+applyArtifactStyleAppendix, then threads agent identity throughbuildAgentRequest. The flow path skips all of that — every dispatch is broken end-to-end.agent.resolve_by_loginreturns only{ type, instance }— no token, model, system_prompt, branch_prefix, or caveman config.dispatch_resolvedcannot authenticate against Forgejo.webhook.ts:184iteratesfor (const assignee of payload.issue.assignees)and dispatches per-assignee. The trigger normaliser (webhook-normalize.ts:188-196) surfaces only the last assignee. Multi-assign tickets silently lose every dispatch except the trailing login.Two majors also flagged:
changes_requestedpivot (issue #270,webhook-handlers.ts:583-602) is load-bearing for the dependency-closure cascade. Without it, dev runsimplementagainstdev/Ninstead ofaddress-reviewagainstpr.headRefand the review loop wedges.mode === "off"+ non-emptysuppress_legacysilently drops events. Either reject this combo at config load OR only suppress when at least one enabled flow on the row matches the trigger kind.A follow-up PR will add the missing pieces (
agent.resolve_by_loginreturning the fullResolvedAgent, anagent.dispatch.render_skillhop or args extension, arouter.fan_outover assignees, the address-review pivot, and the empty-flow guard) before any operator should setsuppress_legacy: ["issue.assigned"].Tests
Next steps after merge
flows-divergence.ts) to coverforge.*mutations (currently dispatch-only). Needed to validate every flow except theissue.assignedone before any cutover.issue.assigned.🤖 Generated with Claude Code
feat(flows): NF-6 legacy-bypass gate + default flow parityto feat(flows): NF-6 per-trigger legacy bypass gate