feat(flows): NF-6 default flow parity (redo) — token + skill render + guard #377
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!377
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/flows-default-parity-redo"
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?
Phase 1 redo of NF-6 default flow parity. Closes the three blockers + one major from the review of the original Phase 1 attempt (reverted in #375).
What this PR fixes vs. the reverted attempt
Real
forgejo_token/model/system_promptmake it into the dispatchedTaskRequest. The previous attempt'sagent.resolve_by_loginreturned only{ type, instance }, soagent.dispatchbuilt aTaskRequestwithforgejo_token: "pending". That works in unit tests but breaks every real dispatch. This PR returns a fullResolvedAgentEnvelope(type,instance,forgejo_user,git_name,git_email,branch_prefix,forgejo_token,model,system_prompt) on individual output ports plus anagentenvelope port for downstream nodes that prefer to thread the whole object via path shorthand.agent.dispatchnow consumes the envelope and the literal"pending"fallback only fires for tests that don't wire either.Skill template rendered before dispatch. Previously
agent.dispatchshipped the literal"implement"string as the SDK prompt. LegacydispatchIssueForAgent(webhook-handlers.ts:567-655) loads the skill viaskillForEvent, runsinterpolate, then layersapplyAppendix+maybeApplyCavemanAppendix+applyArtifactStyleAppendix. Newagent.render_skillasync node does exactly that — outputtaskis the rendered prompt body;agent.dispatchconsumes it viainputs.task.Silent-drop guard at config load. Previously
node_flows: { mode: "off", suppress_legacy: ["issue.assigned"] }skipped the legacy switch AND no-op'ddispatchToFlows, dropping every event on the floor.loadWebhookConfignow rejects this combo with a clear error message. 4 new tests cover the guard + the three legal combinations.Restored the
domain/dispatch/assign-dedup.tslift (re-exported fromwebhook-handlers.tsfor back-compat) and theargDefaultsplumbing onexecutor.tsthat was reverted alongside the previous attempt — both required soagent.dispatchcan reach the real worker pool, dedup state, andresolveAgentByUserinjection in production.What this PR does NOT fix yet (deliberately scoped follow-ups)
webhook.ts:184iteratesfor (const assignee of payload.issue.assignees)and dispatches per-assignee. The trigger normaliser surfaces only the last. Multi-assign tickets still silently lose every dispatch except the trailing login. Cutover forissue.assignedshould still wait for this — Phase 1B will either change the normaliser or add arouter.fan_outnode.changes_requested→address-reviewpivot. LegacydispatchIssueForAgent:583-602probes for an open PR + recentREQUEST_CHANGESreview against the current head SHA and pivotsskill: "implement"→skill: "address-review". Without this, dep-closure cascades wedge the review loop. Cutover forissue.assignedshould still wait for this too — Phase 1C.New default flow pipeline (v3)
The previous draft's
type_switchlabel-routing branches (designer / design-reviewer with skillreview) are gone — legacyhandleIssueAssignedalways usesimplementregardless of who the assignee is. Thereviewskill comes from PRreview_requested, which is a separate trigger and a separate flow.Tests
forgejo_token === "tok-dev"(not"pending"), the rendered task is >100 chars + contains the issue number + title (not the literal"implement"string), the unknown-login soft-skip, the dedup-hit dropped path, and the cancel-stale legacy parity.agent.resolve_by_loginenvelope shape +agent.render_skillregistration covered.Cutover readiness for
issue.assignedAfter this PR + #376 (forge-mutation divergence tooling) + Phase 1B (fan-out) + Phase 1C (address-review pivot), the cutover protocol becomes:
node_flows.mode: "dry-run"(nosuppress_legacy).GET /flows/divergence/summary?since=...forflow_onlyandlegacy_onlyto drop to zero across the soak window."issue.assigned"tosuppress_legacy. Watchtask_historyfor 24 h.handleIssueAssignedbody. Keep the function —propagateDependencyClosurestill calls it viaonAssignedReady.🤖 Generated with Claude Code