Flows YAML — defaults, JSON Schema pipeline, REST CRUD + SSE #1076
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#1076
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
User story
As an operator, I want the 9 default flows shipped on disk, a JSON Schema generated from Zod that drives both server validation and editor autocomplete, and a complete REST surface (with SSE change broadcast) so that the dashboard can manage flows without ssh-ing into the host.
Acceptance criteria
Default YAML files (
flows/defaults/)pr-opened.yml,review-requested.yml,issue-opened.yml,issue-labeled.yml,issue-assigned.yml,issue-unassigned.yml,issue-closed.yml,pr-merged.yml,breakdown-comment.yml.flows.schema.jsonvalidation; references only registered ops.flows/defaults/__tests__/.Zod → JSON Schema pipeline
apps/server/src/domain/flows-yaml/schema.tscoverFlowFile,TriggerKind, every op'sargsSchema(discriminated onuses), expression types..describe()(becomes JSON Schemadescriptionand Monaco hover text).apps/server/scripts/generate-flow-schema.tsrunszod-to-json-schema; output committed toapps/web/public/schemas/flows.schema.json.format: "flow-expression".just qaregenerates and fails if committed file stale; web build runs generator before bundling.POST/PUTre-parse with Zod (authoritative); JSON Schema mismatch → 422.*.ymlvalidates.REST CRUD endpoints (
apps/server/src/http/flows-yaml-routes.ts)GET /flows— list{name, source, trigger_summary, enabled, mtime}.GET /flows/:name— raw YAML + parsed metadata + last-run summary.POST /flows— create underflows/custom/; 409 if name exists.PUT /flows/:name— body includesexpected_mtime; mismatch → 409 with current contents.DELETE /flows/:name— 409 ifsource: "default"; otherwise remove file.POST /flows/:name/disable— toggleenabled: falseoverlay (flows/custom/.disabled.json).POST /flows/:name/dry-run— synthetic event input, per-step trace, no side effects, noflow_runsrow.POST /flows/reload— full re-scan.GET /flows/runs(paginated) andGET /flows/runs/:id.GET /schemas/flows.schema.jsonserved fromapps/web/public/schemas/.Default-fork behavior
PUTon asource: "default"flow writes a copy toflows/custom/<name>.yml; custom version then served.DELETEon a custom-shadowed default removes the custom file (resets to factory).SSE
flow.changed { name, source, mtime }broadcast on every fs-watcher event and successful CRUD write.Auth + audit
Tests
flow_runs.Out of scope
References
docs/specs/flows-yaml.md§3, §10.3.2, §10.3.5, §10.3.7, §11 Phase 1 step 3.apps/server/src/http/flows-routes.ts:43–76seed list.Inherited acceptance criteria from #1075
The engine PR (#1079) ships every #1075 acceptance criterion except the disk-loader's reload endpoint. That work belongs here:
POST /flows/reload— returns{ loaded: number, errors: { file, error }[] }. WrapsFlowRegistry.reload()(already implemented inapps/server/src/domain/flows-yaml/loader.ts). Gated by existing dashboard auth. Ties #1075's loader/file-watcher AC closed once shipped here.This is consistent with the original §10.3.5 surface listed in this ticket — flagged so the cross-issue link is explicit and #1075 can close as a single coherent shipment.