feat(flows-yaml): 9 defaults + JSON Schema pipeline + REST CRUD + main wiring (closes #1076) #1081
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!1081
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "flows-yaml/api-defaults"
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?
Closes #1076. Builds on #1075 (engine + ops + dispatcher merged in #1079).
Lands the operator-facing surface so the YAML engine is ready to ship behind a feature flag.
flows_yaml_settings.modedefaults tooff— the engine boots, the routes mount, no real dispatches fire until #1078 cutover.Commits
feat(flows-yaml): 9 default *.yml flows + Zod→JSON Schema pipeline— defaults, fixture-replay tests, schema generator wired into CI, plus an engine fix for the multi-templateinterpolatefast-path collapse.feat(flows-yaml): REST CRUD + SSE + main.ts dispatcher boot— operator-facing routes, validation pipeline, SSE broadcast, live-capabilities adapter,bootstrapFlowsYamlEngine()in main.ts.What landed
Defaults (
flows/defaults/*.yml)9 files mirroring the legacy graphs:
pr-opened,review-requested,issue-opened,issue-labeled,issue-assigned,issue-unassigned,issue-closed,pr-merged(internal trigger),breakdown-comment. Each has a fixture-replay test underapps/server/src/domain/flows-yaml/defaults/__tests__/.JSON Schema pipeline
.describe()on every Zod field acrossschema.ts,trigger-events.ts, all 15ops/*.ts. Expression-string fields taggedformat: "flow-expression"for Monaco hover (#1077).apps/server/scripts/generate-flow-schema.ts— Zod v4 nativez.toJSONSchema(), no extra dep. Combines per-opargsSchemainto a discriminatedoneOfkeyed onuses:plus the parallel-step branch.apps/server/src/domain/flows-yaml/flows.schema.json+apps/web/public/schemas/flows.schema.json(byte-identical, snapshot-tested).just flow-schema-checkregenerates into a tmp file and diffs; wired intoqa. Webprebuildruns the generator beforevite build.REST CRUD (
/api/flows-yaml/*)/api/flows-yaml{name, source, trigger_summary, enabled, mtime}/api/flows-yaml/runsflow,limit,cursor)/api/flows-yaml/runs/:id/api/flows-yaml/schema/api/flows-yaml/:name/api/flows-yaml/api/flows-yaml/:name/api/flows-yaml/:name/api/flows-yaml/:name/disable.disabled.jsonoverlay/api/flows-yaml/:name/dry-run/api/flows-yaml/reloadValidation pipeline: YAML parse (422 yaml-parse) → Zod
FlowFileSchema(422 schema) → semantic checks (duplicatestep.id, unknownuses:, expression parse — 422 semantic). All routes gated by existing dashboard auth.Path prefix
/api/flows-yaml/*keeps the legacy/api/flows/*JSON node engine intact through the #1078 cutover.Engine fix
interpolate()whole-string fast path now skips when the template contains more than one${{ }}placeholder. The original^\${{\s*([\s\S]*?)\s*}}$-anchored non-greedy regex collapsed multi-template chains like${{ a }}#${{ b }}into one bad capture (a }}#${{ b). Adds a dedicatedexpr/index.test.tscovering each interpolation mode. The three default YAMLs that originally carriedrepo:workaround prefixes are reverted now that the engine is correct.Boot wiring (
main.ts)bootstrapFlowsYamlEngine()runs afterseedDefaultFlowAtBoot():service_settings.flows_yaml_settings.mode(defaultoff)createTriggerBus→setTriggerBus(worker + event-handlers),loadFlowsFromDisk,createFlowWatcher,createPersistentHooks,createDispatcherregisterFlowsYamlRoutesalways runs so operators can author offline even when mode isoffLive capabilities adapter (
live-capabilities.ts)buildLiveCapabilities(deps)maps everyOpDepKeyto a thin adapter over the existing service surface (workers, dedup, agent-resolver, forge, breakdown, review-guard, pr-flow). #1078 wires real deps; until thenbootstrapFlowsYamlEngine()passes empty deps so any actual dispatch faults loudly withMissingCapabilityError.assertCapabilitiesSatisfyOps()does a boot-time conformance check.#1075 ACs closed via this PR
POST /api/flows-yaml/reload— implemented (inherited via comment on #1076)Test plan
bun test apps/server/src/domain/flows-yaml/— 233 / 0 (engine + ops + 9 defaults + dispatcher + hooks + interpolate)cd apps/server && bun test) — 3692 / 0bun x tsc --noEmit— clean (server + shared)just flow-schema-check— passes (committed schema in sync)domain/flows/,webhook.ts, orflows-routes.tsOut of scope (ships in follow-ups)
GET /api/flows-yaml/*+POST /api/flows-yaml/:name/dry-run+ SSEflow.changedbuildLiveCapabilities, flip modeoff→shadow→live, port webhook imperative handlers to YAML ops, drop the legacy node engine, renameflow_id→flow_name/node_id→step_id🤖 Generated with Claude Code
All ACs from #1076 met. CI green. Code is correct.
Nit (not blocking):
dedup_recordinissue-assigned.ymlfires unconditionally even when no agent resolves (no dispatch), leaving a claimed dedup slot with no backing task. Behavior parity with legacy is deferred to #1078, so this is in-scope for that ticket, not here.