seeflow
Original:🇺🇸 English
Translated
Use ONLY when the user explicitly asks to *create* a new SeeFlow flow — "create a flow", "generate a flow", "scaffold a SeeFlow flow", "add a flow to this repo" — or when a previous `/seeflow-lookup` already reported no matching flow exists. **Do NOT invoke for inspection phrasing** ("show me", "how does X work", "diagram our system", "explain the flow") — those route to `/seeflow-lookup` first; it will auto-hand off here only when nothing is registered. Orchestrates five sub-agents and the `seeflow` CLI to turn a natural-language prompt into a registered, validated SeeFlow flow at `<project>/flow.json` (node-attached files live under `<projectPath>/nodes/<id>/`).
9installs
Sourcetuongaz/seeflow
Added on
NPX Install
npx skill4agent add tuongaz/seeflow seeflowTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →seeflow
Turn a natural-language prompt into a registered SeeFlow flow at , with node-attached content (scripts, detail.md, view.html) under . Orchestrate five sub-agents and the CLI; never read the codebase directly, never author by hand ( writes the envelope for you).
<projectPath>/flow.json<projectPath>/nodes/<id>/seeflowflow.jsonprojects:createProject layout convention
A host repo opts into seeflow by creating a directory (the only place this skill introduces a folder — the studio itself is path-agnostic). is shared across every flow in the host and lives at ; each flow lives in its own subdirectory beside it:
<host>/.seeflow/.seeflowLEARN.md<host>/.seeflow/LEARN.md<host>/ ← the user's repo
.seeflow/ ← container, created by this skill
LEARN.md ← shared crib for this skill (project-wide, used by every flow)
<flow-name>/ ← seeflow project root — passed to projects:create --path
flow.json ← envelope + nodes/connectors
style.json ← layout/visuals (managed by `flows:layout`)
nodes/<id>/ ← per-node sidecar files (detail.md, view.html, scripts/)
.tmp/ ← per-flow scratch ($SEEFLOW_TMP)
state/ ← per-flow runtime script stateAlways call . Inside , every CLI / file reference is relative to that project root — never re-prefix with .
seeflow projects:create --path "$repoPath/.seeflow/<flow-name>" --name "..."--path.seeflow/~/.seeflow/Parallelism is the default — one message, N calls. Phase 1's wrong/right block below is canonical; later parallel phases reference it. Narrate each phase boundary with a one-line status (e.g. ) so silent waits don't feel broken.
TaskPhase 3: scaffolding skeleton flow…When NOT to invoke
- Editing nodes on an existing flow → use the canvas, or hit the CLI directly ().
nodes:patch - Deleting a flow → .
flows:delete - Re-laying out an existing flow without semantic changes → .
flows:layout - Empty project (nothing to analyze) → ask the user first.
- Debugging a single broken Play/Status script → edit in-place, re-run Phase 6.
Inputs
- User's prompt; project root ();
$PWD(optional studio host:port).~/.seeflow/config.json - Existing (skip the creation path if already present — fall back to
<project>/flow.json).register --flow flow.json - (
$learnPath) — persistent crib sheet shared across every flow in this host repo, written by prior$PWD/.seeflow/LEARN.mdruns. Read before Phase 1. Format:/seeflow.references/learn-format.md
Conventions
| Variable | Resolution |
|---|---|
| |
| |
| |
| |
| Locally installed |
Scratch files & cleanup
Any intermediate file the orchestrator or a generated Play/Status script needs (curl output, jq scratch, downloaded fixtures, comparison snapshots, etc.) goes under — never , , or . The project-local path requires no extra permission, survives the run for debugging, and is gitignored by convention (the project lives inside the host's container, which is gitignored — add explicitly if not).
$SEEFLOW_TMP/tmp/var/tmp$TMPDIR.seeflow/.tmp/Lifecycle:
- Create on first use — inside any script or wrapper that writes there. Idempotent, costs nothing.
mkdir -p "$SEEFLOW_TMP" - Generated scripts (Phase 5) — Play / Status bodies that need scratch space should reference (or hardcode
"$SEEFLOW_TMP"relative to.tmp/...when running outside a wrapper that exports it).$repoPath - Cleanup at end of run — after Phase 6 prints the final line, the orchestrator removes
Flow "..." registered ...($SEEFLOW_TMP). On a failed/aborted run, leave it in place — the contents are the debugging trail.rm -rf "$SEEFLOW_TMP" - Never check in — if is not yet gitignored, add it before committing.
.tmp/
Every flow mutation goes through the CLI. The studio validates every write server-side — there is no separate validation step. Don't memorise CLI syntax — run to see every subcommand and for synopsis, body shape, output, and error kinds. Treat the help output as the source of truth and follow what it prints. See for the resolver snippet.
$SEEFLOW help$SEEFLOW help <command>references/cli.mdPipeline
P0 /health probe ‖ read $learnPath
P1 code-analyzer ‖ system-analyzer
P2 node-planner (kicks off when code-analyzer returns;
system-analyzer continues in background)
P3 projects:create (path + name → empty flow.json registered)
→ flow:add-bulk (nodes + connectors, atomic) → flows:layout
→ USER REVIEW + dynamic gate (one combined ask)
P4 play-designer ‖ status-designer
P5 write scripts to nodes/<nodeId>/scripts/
→ nodes:patch (per node, with playAction / statusAction)
→ optional newTriggerNodes via flow:add-bulk
→ flows:layout
P6 e2eEach phase gates on the previous (with the Phase 1 → Phase 2 overlap).
Core rules
Full text in :
references/core-rules.md- No mocks. Real services, real state. If something isn't running, stop and ask.
- Bigger picture before INSERTs. Use the natural data-entry path (API, file-drop, producer, seed, webhook).
- Match the project's primary language. Use for every script.
runtimeProfile.primaryLanguage
Common mistakes
- Serial sub-agent dispatch (N messages, one Task call each). One message, N Task calls — see Phase 1's wrong/right.
- One sub-agent fixing multiple failing scripts in Phase 6. Each needs isolated context.
- Authoring directly. Every mutation is a CLI call.
flow.json - Silently overwriting (or silently falling back from) an existing flow at the target path. Phase 3 step 1's existing-flow gate is mandatory — open / rename / overwrite, the user decides. The old "fall back to " behavior was data-loss-adjacent.
register - Touching . The studio owns it via
style.json.flows:layout - Passing as
<slug>/scripts/…. New anchor is the node folder — emit justscriptPath.scripts/play.ts - Mocking services or fake fixtures. Use real triggers; copy fixtures from integration tests.
- Asking "what's your codebase?". Launch the analyzers — that is their job.
- Skipping or simulating Phase 6. Mandatory; the retry budget handles flakiness.
- Bypassing the Phase 0 consent check. Never default to ; always read
enabled: truefirst.~/.seeflow/consent.json - Writing inside a flow folder (
LEARN.md). It is shared across every flow in the host repo — always read/write<host>/.seeflow/<flow-name>/LEARN.md=$learnPath, never anywhere else.$PWD/.seeflow/LEARN.md - Touching after the initial
statuswrite. Thependinghook owns that field — seeSessionEnd.feedback.md - Logging without a redacted summary. If the summary would leak a path, hostname, project name, or prompt text, skip the entry rather than emit a leaky one.
- Writing scratch files to (or
/tmp). Use$TMPDIR($SEEFLOW_TMP) — project-local, no permission prompts, and cleaned up at end of run. Same rule applies to scripts the Phase 4 designers emit.<projectPath>/.tmp/ - Forgetting to clean after a successful run. Leave it in place on failure (debugging trail);
$SEEFLOW_TMPafter Phase 6 prints the finalrm -rf "$SEEFLOW_TMP"line on success.Flow registered
Phase 0 — pre-flight (parallel)
Lookup-first gate — run before anything else
If the user's prompt reads as inspection rather than creation — any of "show me", "show the", "how does", "how do", "what does", "diagram", "explain", "where does", "what handles" — STOP and route through instead. That skill catalogues registered flows and only hands back here if nothing matches. Going straight to creation when a flow already exists wastes the run and surfaces a duplicate. The same gate applies when the user names a flow by slug or title without an explicit verb ("the CRN Enhancement flow", "the checkout flow") — assume inspection unless they prefix it with "create / scaffold / generate / add".
/seeflow-lookupCreation-only triggers (skip the gate): the prompt explicitly says "create / scaffold / generate / add a flow", or has already run in this turn and reported no match.
/seeflow-lookupSilent consent check (see feedback.md
)
feedback.mdRead . If absent, run the first-run prompt and write the file before continuing. The result governs whether qualifying events get logged to for the rest of the run — the skill only writes locally; a hook handles transfer.
~/.seeflow/consent.json~/.seeflow/feedback.jsonlSessionEndCreate a checklist of the six phases ( … ); each as it finishes. Phases skipped at the dynamic gate get marked completed with a one-line note. (If / aren't loaded, run with first.)
TaskCreatePhase 1 — discoverPhase 6 — end-to-end validationTaskUpdateTaskCreateTaskUpdateToolSearchselect:TaskCreate,TaskUpdateCapability probe — run before anything else
Run once and confirm every required subcommand is present: , , , , , , , . (Older versions on lack one or more — was added with the project-local scaffold flow; is the current new-project entry point.) For each missing subcommand, log a feedback entry and surface to the user.
$SEEFLOW helpprojects:createregisterflow:add-bulkflows:layoutnodes:patchschemaidse2e@tuongaz/seeflownpxidsprojects:create- Required missing → log (
env-capability-mismatch,severity: blocker,phase: P0,details: missing <subcommand>[, <subcommand>...]npm i -g @tuongaz/seeflow@latestsummary: $SEEFLOW lacks required subcommands; run). Then stop — do not start Phase 1.and retry - All present → continue.
If itself fails (binary not on PATH, unavailable), log (, , ) and stop.
$SEEFLOW helpnpxenv-tool-missingseverity: blockerphase: P0summary: $SEEFLOW unresolved — neither local binary nor npx fallback availableStudio probe + LEARN.md (parallel)
Then in a single message:
curl --max-time 0.5 -fsS "$STUDIO_URL/health"- Read (
$learnPath) if present →$PWD/.seeflow/LEARN.md(elselearnContext). This file is shared across every flow in this host — do not look inside anynullfolder for it. Format:<flow-name>/.references/learn-format.md
- 200 → Phase 1.
- !200 → tell the user the studio isn't running, warn the first launch can take a minute or two if it has to fall back to , then run the CLI's
npxsubcommand. Re-probestartonce. If still unreachable, log/health(env-service-unreachable,severity: blocker,phase: P0), surface and stop.summary: studio /health unreachable after start retry
Phase 1 — discover (parallel)
Launch both analyzers in parallel — single message, two calls. Serial launch roughly doubles wall-clock for zero benefit.
TaskWrong:
message 1: Task(seeflow-code-analyzer, …) → wait
message 2: Task(seeflow-system-analyzer, …)Right:
message 1: Task(seeflow-code-analyzer, …)
Task(seeflow-system-analyzer, …)Every later parallel phase (Phase 4 designers, Phase 5 retries spanning both overlay families, Phase 6 per-script fix-up) follows this pattern.
- — in:
seeflow-code-analyzer,userPrompt,projectRoot,existingDemo. Out:learnContext,userIntent,audienceFraming,scope,codePointers,knownEndpoints,techStack.existingDemo - — in:
seeflow-system-analyzer,projectRoot. Out:learnContext+ aruntimeProfilepayload (learnUpdates,localDevSetup,integrationTests,fixtures,factories,seedCommands,dataEntryPaths,gotchas). Every fact it learns about how to start / set up the local environment MUST land intechAdaptations.learnUpdates
Tools: (read-only). Schemas: , , . Unparseable output: retry that single agent once, then log (, , ), surface, and stop. The same rule applies to every sub-agent in Phases 2 and 4.
Read, Grep, Glob, LS, Bashagents/seeflow-code-analyzer.mdagents/seeflow-system-analyzer.mdreferences/learn-format.mdagent-output-unparseableseverity: failureagent: <slug>summary: <agent> returned unparseable JSON after retryagent-output-unparseableEmpty-project / design-only mode
If the project root has no source tree (no , no Go module, no Python project, no language source files), the "When NOT to invoke" rule kicks in: ask the user first. If they say "design anyway" (mockups, demo skeletons, architectural sketches), skip both analyzers and build a synthetic brief by hand from the user's prompt:
package.jsonjson
{
"userIntent": "<extracted verbatim from the user's prompt>",
"audienceFraming":"design-only sketch — no running system to observe",
"scope": { "rootEntities": [<inferred from prompt>], "outOfScope": [] },
"codePointers": [],
"knownEndpoints": [],
"techStack": [<user-stated, or empty>],
"existingDemo": null,
"runtimeProfile": null
}Forward that brief to (Phase 2) as-is — the planner already tolerates a sparse brief.
seeflow-node-plannerLog (, , , ).
mode-fallbackseverity: degradedphase: P1details: design-onlysummary: empty project — analyzers skipped, synthetic brief built from promptDownstream consequences:
- Phase 3 dynamic gate: default to static without re-asking. Without , Phase 4 designers cannot pick a real interpreter or fixture; tell the user to populate code first if they later want dynamic.
runtimeProfile - Phase 6 (e2e): N/A — skip with a one-line note when summarising the run.
- : still write the flow row, but mark it
$learnPathin the purpose column so the next run knows the canvas is not wired to a real system.(design-only)
Phase 1 → Phase 2 overlap
Start as soon as the code-analyzer returns — it only needs the code-analyzer's brief plus . The system-analyzer continues in the background.
seeflow-node-plannertechStackWhen the system-analyzer returns:
- Size-check the payload first. Measure the JSON byte length. If > 16 KB (twice the agent's budget — see § "Output budget"), the analyzer drifted. Apply the per-field caps from that section before merging: truncate
agents/seeflow-system-analyzer.mdto 10,gotchas[]/fixtures[]to 8, prose fields to 400 chars, etc. Drop any inherited fact that already appears verbatim infactories[](the merger would keep it anyway). Log$learnPathonce withagent-output-corrected. The trimmed payload is what feeds steps 1–3.agent: seeflow-system-analyzer, details: oversize-learnupdates-truncated (Nb → Mb) - Merge into
learnUpdates($learnPath— create$PWD/.seeflow/LEARN.mdif missing; the file is shared across every flow in this host). Anything about boot, ports, env vars, fixtures, gotchas, or tech adaptations MUST land in the file. Re-cap$PWD/.seeflow/to ~6 KB after the merge per$learnPath§ "Merging rules" (push oldest gotchas into a collapsedreferences/learn-format.mdblock).<details> - Splice +
runtimeProfilefacts into the in-memory context brief used by Phase 4. Forward the trimmed payload — never the raw analyzer output — and only the fields each designer actually consumes ($learnPath, the matchingruntimeProfilefor techs in this flow, the relevanttechAdaptations.<techId>, top 5dataEntryPaths). The rest stays ingotchasfor the next run; it doesn't need to ride along in every designer prompt.$learnPath - Merge /
knownEndpointsfrom the code-analyzer into the same write.techStack
Resolve tech refs. Map each in the merged to . Forward those paths and the matching into Phase 2 / 4 prompts (~3–5 refs per flow). If the system-analyzer hasn't returned yet, forward whatever already had; the planner produces a first draft and the user reviews in Phase 3 anyway.
techId## Tech stackreferences/tech/<techId>.md## Tech stack adaptationstechAdaptations$learnPathPhase 2 — plan nodes
Look up the current node + connector contract from the CLI first. Before drafting anything, run and (parallel; one message, two Bash calls) and capture both outputs. Pass them to the planner alongside the brief — the planner has no shell, so what you don't forward, it doesn't know. Skipping this step lets the planner invent fields the CLI rejects on , burning a retry.
$SEEFLOW schema node$SEEFLOW schema connectorflow:add-bulkLaunch with: the brief, the resolved tech-ref paths, the matching , and the two CLI outputs above. No tools — pure reasoning. The planner reads each ref's Node modelling section and treats as the project-specific override.
seeflow-node-plannertechAdaptationstechAdaptationsConnectors conform to and nothing more. If the planner emits any field the contract rejects, strip it before and log with . Do not enumerate the legal fields here — re-run the schema command whenever in doubt.
$SEEFLOW schema connectorflow:add-bulkagent-output-correcteddetails: connector-extras-stripped (×N)- Resource nodes first — every DB, queue, event bus, cache, file store, external SaaS gets its own node, typed with a matching Lucide
rectangle(icon,database,list-ordered,radio-tower,cloud) and aservercapability when state is worth probing.statusAction - Abstraction — one node per service / workflow / worker / queue / DB. Exceptions: independently-meaningful pipeline stages, fan-out consumers, branches, and services hosting multiple independent state machines.
- Duplicate shared resources for clarity. When a DB / queue / bus is referenced by many nodes and the lines tangle the canvas, split it into role-specific copies (,
orders-db-read) sharing the sameorders-db-write+type+data.iconbut distinctdata.names.id
Output: a single envelope carrying , , , , and (planner-only sibling map). The and arrays must conform to and — they are forwarded verbatim in a single body to the subcommand in Phase 3. Any key the CLI rejects here is rejected at too. One retry on unparseable output, then surface and stop. Full contract: .
nameslugnodesconnectorsrationalesnodesconnectors$SEEFLOW schema node$SEEFLOW schema connectorflow:add-bulkflow:add-bulkagents/seeflow-node-planner.mdValidate the envelope before continuing. A parseable JSON blob is not the same as a complete envelope. After , assert every required key is present and non-empty:
JSON.parsetypeof name === 'string' && name.length > 0typeof slug === 'string' && slug.length > 0Array.isArray(nodes) && nodes.length > 0- (may be empty for single-node flows)
Array.isArray(connectors) - (one entry per node id)
rationales && typeof rationales === 'object' && Object.keys(rationales).length === nodes.length
If any assertion fails, re-dispatch the planner once with the specific gap echoed back in the prompt (). On second failure, log (, , , , ), surface, and stop. Never silently synthesise the missing fields — losing the planner's own justifications at the Phase 3 review gate is a real loss of signal, and a fabricated / ships under the planner's authority without its review.
Your previous output was missing: name, rationales[3 of 5 nodes]. Re-emit the full envelope.agent-output-incompleteseverity: failurephase: P2agent: seeflow-node-plannerdetails: <missing-keys>summary: planner returned partial envelope after retrynameslugPhase 3 — scaffold, populate, layout, review
The skeleton flow lands via four steps, in order. No authoring by hand — writes the empty envelope for you. Run for each subcommand's body shape and flags.
flow.jsonprojects:create$SEEFLOW help <command>-
Scaffold + register inside the project via. This is the entry point for a new project: the CLI writes the empty
projects:createatflow.json(project root) and registers it in one shot.<repoPath>/flow.jsonExisting-flow gate — check before the CLI write. Test. If the file exists (ortest -f "$repoPath/flow.json"later returnsprojects:createexit code 4 because the pre-check raced), STOP and ask viaalreadyExists— never silently overwrite, never silently fall back:AskUserQuestionA SeeFlow flow is already registered at this path. What do you want to do?- Open the existing flow (Recommended) — skip creation; run to re-attach the existing envelope, surface
$SEEFLOW register --path "$repoPath", then stop. If the user wanted to inspect rather than edit, hand off to$STUDIO_URL/d/<slug>./seeflow-lookup - Create a new flow with a different name — ask the user for a new flow name, recompute , then retry this step (Phase 1/2 only rerun if the user's intent also changed).
$repoPath = $PWD/.seeflow/<new-slug> - Overwrite the existing flow — destructive. Confirm once more, then (and
$SEEFLOW flows:delete --path "$repoPath"for any sidecar leftovers), then retry this step.rm -rf "$repoPath"
Log oneper session (plan-revision,severity: friction,phase: P3). Debounce — even if the user toggles between options, the entry is written once.summary: existing flow at target path; user picked <open|rename|overwrite>Gate clear → forward the planner-supplied(andnameif the planner provided one):descriptionbash$SEEFLOW projects:create --path "$repoPath" --name "$plannerName" [--description "$plannerDescription"]The studio writes the envelope, adds a registry entry under, and returns~/.seeflow/registry.json(slug is derived from{ id, slug }). Capturenamefrom the response and use it (notid) for every follow-up CLI call below — several commands document slug support inslugbut the server only resolves by id today. Registration is a precondition for opening the canvas: thehelproute only works after this step succeeds, so never surface the canvas URL to the user before this step.$STUDIO_URL/d/<slug>Ifreturnsprojects:create(code 4) after the pre-check passed (filesystem race), loop back to the gate above and let the user decide — do not auto-fall-back. Do not hardcode the envelope shape from memory; if you need to inspect whatalreadyExistswrites, runprojects:create.$SEEFLOW schema flow - Open the existing flow (Recommended) — skip creation; run
-
Normalize the planner output: strip(keep them in memory for the review prompt below), then for the planner's designated trigger node (the one whose
rationalesis set even as a placeholder), inject the minimumdata.playActionpayload the contract requires so the server accepts the batch. Look up the exact required fields by runningplayActionand$SEEFLOW schema action(the$SEEFLOW schema nodeshape's required keys) — do not hardcode the shape from memory. Pick the interpreter fromPlayAction(falling back toruntimeProfile.primaryLanguage) and pointbunatscriptPath. The Phase 4 play-designer overwrites the placeholder with the real action viascripts/play.ts. The script file does not need to exist yet — Phase 5 writes it, Phase 6 runs it. 2a. Mint canonical ids. Planner ids are descriptive (nodes:patch,checkout-api); the studio's id producers (canvas, server auto-assign, the upload endpoint regex) usec-order-server-event-bus/node-<10 base62>. Rewrite at the boundary so flow.json matches. Use the CLI — it shares the exact alphabet and rejection-sampling logic with every other id producer in the studio:conn-<10 base62>bashmapfile -t nodeIds < <($SEEFLOW ids node "${#nodes[@]}") mapfile -t connIds < <($SEEFLOW ids connector "${#connectors[@]}")For eachthat already matchesnodes[i].id(edit-case reuse from^node-[A-Za-z0-9]{10}$), keep it; only mint new canonical ids for net-new nodes. Build aeditTargetmap and rewrite:descriptiveId → canonicalIdnodes[].id- ,
connectors[].id,connectors[].sourceconnectors[].target - keys (kept in memory for the review prompt) 2b. Log any silent corrections from steps 2 and 2a (see
rationales). For each correction kind that fired (placeholder-feedback.mdinjection, descriptive→canonical id rewrite, unknown-type rename, unknown-field rename, bidir-connector strip, user-to-system-rectangle retype, …), emit oneplayActionentry withagent-output-corrected,severity: corrected,phase: P3, andagent: seeflow-node-plannerwhere N is the count. Aggregate across nodes — never one entry per node. If no corrections were needed, log nothing. This is the signal that the planner drifted from the contract; without it, the orchestrator's silent patching is invisible.details: <correction-kind> (×N)
retype rule. If the planner shippeduser-to-system-rectanglefor a node whosetype:'user'is clearly a software system (data.name,Web UI,Mobile App,Browser,SPA,Desktop Client,CLI, anything ending inSDK/Client/App/UI/Frontend), silently retype toServicewithrectangleinferred from the name (data.iconfor web/UI/frontend,monitorfor mobile,smartphonefor CLI,terminalfor SDK/Client). Keepplugonly whentype:'user'is a human role (data.name,Customer,Support Agent,Reviewer,Approver). Log one aggregateOperatorentry withagent-output-corrected. The Phase 3 canvas review surfaces the result to the user — they see the correction.details: user-to-system-rectangle (×N) -
— atomic seed of nodes + connectors in one transactional write. Forward the normalized + id-rewritten
flow:add-bulkandnodesarrays asconnectors. Connectors may reference nodes from the same call — the server validates the merged graph as a whole, so a dangling source/target or a malformed node rolls back both arrays together. No two-phase commit to reason about; no orphan nodes if connectors fail.{ nodes, connectors } -
— run ELK and write
flows:layout.style.json
Each call validates server-side. A exit means feed the issues back to the planner and retry — no separate validation step.
badSchemaOpen the canvas, surface the planner's per node — prefix each with so the human sees a readable anchor despite the opaque id () — and ask one combined question (layout review + dynamic gate in a single round-trip — two consecutive waits is interrogation):
rationales<data.name> (<canonical id>):POST /orders (node-Ab12cd34Ef): Single HTTP service — internal routes are implementation detail.bash
URL="$STUDIO_URL/d/$slug"
(open "$URL" 2>/dev/null || xdg-open "$URL" 2>/dev/null || start "$URL" 2>/dev/null) &Opened the canvas at. Two quick questions:<url>
- Layout — any additions, removals, or renames?
- Dynamic or static — continue with Play scripts + Status probes so the canvas reacts to your running system, or stop with the static layout?
Wait once. Parse both answers from the reply.
- Layout changes requested → log (
plan-revision,severity: friction,phase: P3), re-run node-planner with the feedback, repeat the combined ask. The dynamic answer (if given) is remembered but not acted on until the layout is approved. Debounce — log once per session even if the user revises multiple times.summary: user requested layout changes at canvas review gate - Layout approved + dynamic → Phase 4. If the system-analyzer is still running, await it now; Phase 4 designers need its , fixtures, data-entry paths, and tech adaptations. Re-merge any new
runtimeProfilefirst.learnUpdates - Layout approved + static → print and stop. Still merge any pending
Flow "<name>" registered as <slug> (static). Open: $STUDIO_URL/d/<slug>.learnUpdates - Dynamic answer unclear or absent → default to static (dynamic writes executable scripts; opt-in). Log (
mode-fallback,severity: degraded,phase: P3,details: dynamic-to-static).summary: dynamic gate unclear; auto-downgraded to static
(Design-only mode from Phase 1's empty-project branch defaults to static here without re-asking.)
Phase 4 — design Play + Status (parallel)
Launch + in parallel (Phase 1 rule). Both receive: context brief, node draft, edit target, tech-ref paths, matching . They read each ref's Play / Status section and treat as the project override. Tools: .
seeflow-play-designerseeflow-status-designertechAdaptationstechAdaptationsRead, Grep, Glob, LSLook up the action + node contract from the CLI first. Run and (parallel; one message, two Bash calls) and capture both outputs. Pass them to each designer alongside the brief — designers have no shell, so what you don't forward, they don't know. The same outputs serve both designers; reuse them. Skipping this step lets the designer invent fields the CLI rejects on , burning a retry.
$SEEFLOW schema action$SEEFLOW schema nodenodes:patchOutput shape (both): triples. is the exact body for . is project-root-relative (); inside is node-folder-relative (). Full contracts: , .
{ nodeId, patch, scriptFile: {path, body, chmod}, validationSafe?, rationale }patchseeflow nodes:patchscriptFile.pathnodes/<nodeId>/scripts/<name>playAction.scriptPathpatchscripts/play.tsagents/seeflow-play-designer.mdagents/seeflow-status-designer.mdSample data priority: integration/e2e fixtures (, copy verbatim) → seed / migration / ORM factories → README / OpenAPI / Postman examples → invent last, note in .
runtimeProfile.integrationTestDirrationalenewTriggerNodes{nodes, connectors}Phase 5 — patch overlays + layout
For each overlay returned by Phase 4 (parallelise the writes when the script bodies don't depend on each other):
- Write to
scriptFile.body(Write tool).scriptFile.path - per
chmod(default 755).scriptFile.chmod - Call with the overlay's
nodes:patchbody. (Body shape:patch.)$SEEFLOW help nodes:patch
If the play-designer emitted , batch them via (one call, both arrays atomic), then re-run . (Body shape: .)
newTriggerNodesflow:add-bulkflows:layout$SEEFLOW help flow:add-bulkEdit-case retype routing. When the Phase 2 diff against flags a node whose already exists but whose changed (e.g. a former trigger reshaped to a decorative ), route it through — not + . The patch path preserves the per-node folder under ; the delete cascade destroys it. The server validates required fields for the new type after the merge (e.g. needs , needs , accepts an optional string); a exit means feed the issues to the play-designer and retry.
editTargetidtyperectangledatabasenodes:patch { type, ...required fields }nodes:deleteflow:add-bulknodes/<id>/* → imagepath* → iconicon* → htmlhtmlbadSchemaRetry budget: per-node failure → re-dispatch that one designer with the CLI's reported issues, retry, max 3 per node. Parallelise re-dispatches when more than one node failed (Phase 1 rule). When the budget is exhausted for a node, log (, , (or the actual code), ). Aggregate across nodes — one entry per (kind, code) pair, not one per node.
nodes:patchretry-exhaustedseverity: failurephase: P5code: badSchemasummary: nodes:patch retries exhausted on <kind> (N nodes)Phase 6 — end-to-end validation
Must run. Do not skip or simulate.
Run the subcommand for the flow. Pass with the s of any Phase 4 overlays whose (third-party or paid actions); skipped nodes appear in , not as failures. Body / flag details: .
e2e--skip-nodesnodeIdvalidationSafe === falseskipped[]$SEEFLOW help e2eok: trueFlow "<name>" registered as <slug>. Open: $STUDIO_URL/d/<slug>rm -rf "$SEEFLOW_TMP"ok: false- Identify failing nodes from /
plays[*].error.statuses[*].outcome - Parallel fix-up (Phase 1 rule): one sub-agent per failing script, single message. A single agent fixing N scripts cross-contaminates.
- Each agent gets the script path (under ), the specific error payload, and a concrete fix hypothesis (
nodes/<nodeId>/scripts/).play.ts: ECONNREFUSED on :3001 — start the app first - Edit in-place, re-run the subcommand. Max 2 retries, then log
e2e(seeflow:e2e-fail,severity: failure,phase: P6,details: <N> failing scripts after fix-up) and ask retry / stop.summary: e2e ok:false after retry budget exhausted
If the run is design-only (Phase 1 fallback), skip Phase 6 entirely and log (, , , ).
phase-skippedseverity: degradedphase: P6details: design-onlysummary: e2e skipped — no runtime to validate againstPolish LEARN.md with anything learned
If Phases 5-6 surfaced something the next run would want — port mismatch, fixture path, missed env var, working seed command, useful data-entry path — append to ( bullet or the relevant section). Also append the flow to the "Flows already created" table with today's date and a one-line purpose. Skip if nothing new — empty updates are noise. The file is shared across every flow in this host, so the table accumulates every flow the skill has ever scaffolded here.
$learnPathGotchasTech-specific learnings (a helper, a required attribute, an emulator quirk, a fixture path) go in → , not just . If the code-analyzer missed a tech entirely, also append the to . This is what makes the next run reuse the work.
## Tech stack adaptations### <techId>## GotchastechId## Tech stack/seeflowOperations
| Topic | File |
|---|---|
CLI resolver + discovery via | |
| Error handling, retry caps, sub-agent table | |
| Per-node file convention, action runtime budgets, when-to-use guidance | |
| Core rules | |
| |
| Tech-specific best practices | |
| Sub-agent prompts | |
| Feedback collection — consent, kinds, format, redaction, hook handoff | |
| Canonical id generator | |