From d0b5dcbfbe07fec02a17ea9c167e3cb172cab6ca Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Thu, 4 Jun 2026 11:20:50 -0700 Subject: [PATCH] docs(plan): pluggable step parsers (plugin runtime) + code node for arbitrary TypeScript (KTD-15, U14) Co-Authored-By: Claude Opus 4.8 (1M context) --- ...inversion-workflow-modelable-steps-plan.md | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/docs/plans/2026-06-04-001-feat-step-inversion-workflow-modelable-steps-plan.md b/docs/plans/2026-06-04-001-feat-step-inversion-workflow-modelable-steps-plan.md index 20373e8a71..7b41265ecd 100644 --- a/docs/plans/2026-06-04-001-feat-step-inversion-workflow-modelable-steps-plan.md +++ b/docs/plans/2026-06-04-001-feat-step-inversion-workflow-modelable-steps-plan.md @@ -15,7 +15,7 @@ Extend the engine→workflow inversion to **task steps**. Today the engine owns Steps additionally gain **parallel execution and worktree isolation as explicit foreach axes** (`mode: sequential|parallel`, `isolation: shared|worktree`): PROMPT.md steps may carry `depends:` metadata, and a parallel foreach runs dependency-satisfied instances concurrently — each in its **own worktree/branch off a common base** — with an **ordered integration stage** that lands step branches in step order and routes rebase conflicts to an `integration-conflict` rework outcome (KTD-11). -Third, the **task shape itself becomes workflow-defined** (KTD-12/13/14): the existence of PROMPT.md and the `### Step N:` parsing convention stop being engine law — workflows declare their **artifacts** (named task documents), and **step parsing is itself a graph node**: `parse-steps(artifact, parser)` reads an artifact, runs a registry parser, and writes the step list, with routable `no-steps`/`parse-error` outcomes. Workflows also declare **custom task fields** (typed, with enum options and rendering instructions); the task model reduces to core fields (title, description) plus standard metadata, with everything else as workflow-defined fields, and the task UI renders the field schema dynamically (detail form + card badges). +Third, the **task shape itself becomes workflow-defined** (KTD-12/13/14): the existence of PROMPT.md and the `### Step N:` parsing convention stop being engine law — workflows declare their **artifacts** (named task documents), and **step parsing is itself a graph node**: `parse-steps(artifact, parser)` reads an artifact, runs a registry parser, and writes the step list, with routable `no-steps`/`parse-error` outcomes — and parsers are **plugin-pluggable**. A **`code` node** (KTD-15) runs arbitrary sandboxed TypeScript for logic no built-in covers. Workflows also declare **custom task fields** (typed, with enum options and rendering instructions); the task model reduces to core fields (title, description) plus standard metadata, with everything else as workflow-defined fields, and the task UI renders the field schema dynamically (detail form + card badges). The default workflow is untouched (monolithic `execute` seam, declares PROMPT.md + the step-headings parser + zero custom fields — byte-identical; it is the parity oracle, same posture as the columns track). Inversion is opt-in via custom workflows; a new built-in **stepwise coding workflow** demonstrates the full modeling and is the parity-comparison subject. @@ -47,6 +47,8 @@ The FN-4359 reliability-freeze waiver carried by plans 002/003 continues to appl - **Parallel step execution** (KTD-11): `TaskStep.dependsOn` metadata parsed from PROMPT.md `### Step N (depends: 1,2):` annotations; foreach `concurrency` config; per-instance worktrees/branches off a common base via the existing worktree pool; ordered integration (rebase/cherry-pick in step order) with `outcome:integration-conflict` routed as rework on the updated base; branch-scoped RETHINK reset in parallel mode. - **Workflow-defined task artifacts & a `parse-steps` node** (KTD-12): workflows declare named task documents (riding the existing task-documents machinery), and step parsing is a first-class graph node — `parse-steps(artifact, parser)` reads the artifact and writes the step list, with `no-steps`/`parse-error` outcome edges; `parseStepsFromPrompt` becomes the built-in `step-headings` parser in a parser registry; the default workflow keeps its legacy in-execute step init for byte-identical parity. - **Workflow-defined custom task fields** (KTD-13): typed field definitions (string/text/number/boolean/enum/multi-enum/date/url) with enum options and rendering instructions in the IR; values stored per task and validated against the schema through a single store authority; agent-tool parity. +- **Pluggable step parsers**: plugins register parsers (namespaced ids) through the plugin runtime, mirroring the plugin-trait adapter; fail-closed to `parse-error`. +- **`code` node** (KTD-15): inline TypeScript compiled with esbuild, executed in a timeout-bounded child process against a narrow harness contract (`ctx` in, `{outcome, contextPatch, customFields}` out); save-time syntax validation. - **Dynamic task UI** (KTD-14): TaskDetailModal renders the field schema as a form section; TaskCard renders card-front-placed fields as badges/chips; workflow editor gains a field-definitions panel. - Built-in **stepwise coding workflow** (new, opt-in) modeling today's per-step review policy explicitly; parity assertions against legacy in-session behavior. - Workflow node editor support for authoring foreach/step-review/rework constructs; agent-tool and plugin-SDK type parity; docs. @@ -58,7 +60,7 @@ The FN-4359 reliability-freeze waiver carried by plans 002/003 continues to appl - Step-template authoring from the dashboard *board* (lanes/cards unchanged); authoring lives in the existing workflow node editor. - Plugin-defined node kinds (plugins already reach gates via traits; new node kinds stay built-in this round). - **Recasting existing built-in task fields** (priority, labels, etc.) as workflow custom fields — this plan ships the field *system*; migrating built-ins onto it is a follow-up with its own compatibility track (every built-in field has hardcoded consumers across ~150 files). The field system is designed so that migration is additive when it comes. -- Plugin-contributed field types or parsers (the registries are built-in-only this round, same posture as node kinds). +- Plugin-contributed field *types* (the field-type whitelist is built-in-only this round; step parsers ARE pluggable, KTD-12). - Cross-workflow field identity (two workflows defining a field with the same id are distinct schemas; no shared/global field namespace yet). - Graduating any flag default. @@ -177,7 +179,7 @@ Today `PROMPT.md` is hardcoded engine law: created by the planning phase, parsed - **IR gains an `artifacts` declaration**: `artifacts: [{ key, title?, producedBy?: "planning" | "manual", role?: "step-source" | "context" }]`. Artifacts ride the **existing task-documents machinery** (`TaskDocument`, `fn_task_document_write/read`) — no new storage; `PROMPT.md` becomes the default workflow's declared `step-source` artifact backed by its current file location (the document layer already fronts it). - **New node kind `parse-steps`**, config `{ artifact: , parser: "step-headings" | "json-steps" }`. Its handler reads the artifact at walk time, runs the named parser, and writes the canonical step list through the projection sink (`Task.steps[]` via the store, graph source — same single authority as everything else). Outcomes: `success`, `outcome:no-steps` (parsed cleanly, zero steps — routable, defaults to success), `outcome:parse-error` (malformed artifact — routable, defaults to failure). The node is the *only* graph-side writer of the step list; running a `parse-steps` after a foreach has already expanded trips the KTD-3 pin protection (audited failure), so re-plan loops cannot silently desynchronize an expanded foreach. -- **Parser registry**: a **built-in parser registry** (same registry posture as traits — built-in-only this round) holds `step-headings` (the extracted `parseStepsFromPrompt` logic, including the `(depends: …)` annotation from U1 — extraction, not rewrite) and `json-steps` (a structured `[{name, depends?}]` document for workflows that plan in JSON). The node invokes parsers from the registry; the registry is the extension point when parsers go pluggable later. +- **Parser registry is pluggable**: the registry (same posture as the trait registry) holds built-ins `step-headings` (the extracted `parseStepsFromPrompt` logic, including the `(depends: …)` annotation from U1 — extraction, not rewrite) and `json-steps` (a structured `[{name, depends?}]` document for workflows that plan in JSON), and **plugins register parsers** under namespaced ids (`plugin::`) through the plugin runtime — mirroring `plugin-trait-adapter.ts`. Contract: `(artifactContent, ctx) → {steps: [{name, dependsOn?}]}`, executed through the plugin runner with a timeout; an unavailable or throwing plugin parser maps to `outcome:parse-error` (fail-closed, audited) — never a crash. IR referencing a parser absent from the live registry is rejected at save with the id named. - **foreach consumes, never parses**: foreach `source: "task-steps"` just reads `Task.steps[]`; the KTD-3 dominance validation is now concrete — a `parse-steps` node must dominate any `foreach(source:"task-steps")` on all paths (the monolithic default workflow has no foreach and keeps its legacy in-`execute()` step init untouched). - **The planning seam contract**: the workflow's `producedBy: "planning"` artifacts are what the planning seam is told to produce (surfaced in the planning prompt); the engine no longer assumes PROMPT.md by name outside the default workflow's declaration. Reconcile (`reconcileStepsFromGitHistory`) and resume read through the workflow's parse-steps declaration to know which artifact/parser governs the task. - **Parity**: the legacy path (`parseStepsFromPrompt` callers in store/executor) delegates to the same extracted `step-headings` parser function — byte-identical, proven by the existing parse tests running against both the direct call and the registry resolution. The stepwise builtin's chain becomes: plan seam → `parse-steps(PROMPT.md, step-headings)` → foreach. @@ -200,6 +202,17 @@ The task model is recast as: **core fields** (title, description) + **standard m - **Workflow editor**: a **Fields panel** in the workflow editor (sibling to the column panel from the columns track) — add/edit/remove field definitions, enum option editor with color picker, render-placement controls, live badge preview. Reuses the editor's existing validation-at-save surfacing. - **TUI**: read-only rendering of card-placed fields in the task detail view (chips → bracketed labels); no TUI editing this round. +### KTD-15 — `code` node: arbitrary TypeScript in the graph + +A general computation escape hatch so workflows can express logic no built-in node covers (derive a field from artifacts, call an internal API, compute routing data): + +- **New node kind `code`**, config `{ source: string, timeoutMs?: number }` — inline TypeScript. Compiled in-memory with esbuild at execution (and syntax-checked at save: the validator runs the same transform and rejects IR whose source fails to compile), then run in a **child process** with cwd = the task's worktree. +- **Harness contract**: the script default-exports `async (ctx) => result`, where `ctx = { task, steps, customFields, context, artifacts: {read(key)}, instance? }` (instance present inside a foreach template — the `foreach:active` data). The returned `{ outcome?, value?, contextPatch?, customFields? }` maps to graph behavior: `outcome` routes `outcome:` edges (absent → success), `contextPatch` merges into walk context, `customFields` writes through the U11 validation authority. Throw/timeout/non-zero exit → `failure` outcome with the error audited. +- **Boundaries**: the code node does NOT get a store handle, engine internals, or the step-list write path — steps are written only by `parse-steps` (KTD-12), task fields only through the validated patch it returns. It can read artifacts and the worktree; it runs with the same trust as existing workflow script steps (workflows are project-local, user-authored config — `WorkflowStepMode "script"` already executes arbitrary project scripts today, so this adds expressiveness, not a new trust tier). Timeout clamped (default 30s, cap 300s); stdout/stderr captured and size-capped into the node result; source size capped (64KB). +- **Placement rules**: legal anywhere a script node is legal, including inside foreach templates and split branches (it is review-side/read-only with respect to git unless the author's code itself writes files in the worktree — same as script nodes today; the file-scope guard still applies to anything it commits via the session, and the code node itself never commits). +- **Editor**: palette entry + inspector with a code textarea (monospace, esbuild syntax errors surfaced at save), timeout input. + + --- ## Requirements @@ -219,9 +232,10 @@ The task model is recast as: **core fields** (title, description) + **standard m - R13: Agent tools and plugin SDK expose the new IR types (type-only in SDK); `fn_workflow_create/update` accept the new constructs with the same validation. - R14: Five lifecycle invariants + lost-work guard trio remain non-configurable and covered by tests on the stepwise path. - R15: Parallel step execution per KTD-11 — foreach exposes explicit `mode` (sequential|parallel) and `isolation` (shared|worktree) axes (parallel+shared validator-rejected); `dependsOn` parsed from PROMPT.md (unannotated steps depend on the previous step, preserving sequential behavior by default); per-instance worktrees off the integration base; ordered integration flips the projection (done iff integrated); rebase conflicts route `outcome:integration-conflict` to rework on the updated base within the rework budget; concurrency clamped by semaphore availability without deadlock; dependency cycles rejected at expansion. -- R16: Step parsing is a graph node per KTD-12 — `parse-steps(artifact, parser)` is the only graph-side writer of the step list, with routable `no-steps`/`parse-error` outcomes; workflows declare task artifacts; the parser registry's `step-headings` is the extracted `parseStepsFromPrompt`, byte-identical for legacy callers; a parse-steps node must dominate any foreach; parse-steps after foreach expansion trips the pin protection. +- R16: Step parsing is a graph node per KTD-12 — `parse-steps(artifact, parser)` is the only graph-side writer of the step list, with routable `no-steps`/`parse-error` outcomes; workflows declare task artifacts; the parser registry's `step-headings` is the extracted `parseStepsFromPrompt`, byte-identical for legacy callers; a parse-steps node must dominate any foreach; parse-steps after foreach expansion trips the pin protection; plugin-registered parsers resolve through the plugin runtime and fail closed to `parse-error`. - R17: Workflows define custom task fields (typed, enum options, render instructions) per KTD-13; values validated through a single store authority with typed rejections; field removal orphans (never destroys) values; agent tools have full read/write parity. - R18: Task UI renders the field schema dynamically per KTD-14 — detail form widgets by type, card badges by placement, workflow-editor Fields panel; zero custom fields renders exactly today's UI. +- R19: `code` node per KTD-15 — save-time compile validation, child-process execution with clamped timeout and capped output, harness contract honored (no store handle, fields only via the validated patch, steps never), throw/timeout → audited failure. --- @@ -293,7 +307,7 @@ The task model is recast as: **core fields** (title, description) + **standard m - **Files**: Modify `packages/dashboard/app/components/WorkflowNodeEditor.tsx`, `packages/dashboard/app/components/workflow-flow-mapping.ts`, `packages/dashboard/app/components/WorkflowNodeEditor.css` (or sibling component CSS per the extraction convention); tests extend `packages/dashboard/app/components/__tests__/WorkflowNodeEditor.test.tsx`, `workflow-flow-mapping.test.ts`; register any new component test in `packages/dashboard/vitest.config.ts` `qualityAppComponentTests`. - **Approach** (design decisions committed here, not deferred): - **Template authoring is inline**: template nodes are always-visible React Flow children of the foreach group node (`parentId` set to the group), no drill-in canvas mode. `flowToIr` partitions nodes by `parentId` — children of a foreach group reassemble into that node's `config.template`; everything else stays top-level. Empty foreach groups render an empty-state hint ("drag a step-execute node here"). - - **Palette**: add `foreach` (preset `source:"task-steps"`, auto-populating one `step-execute` child so the group is never confusingly empty), `step-review`, and `parse-steps` (preset artifact `PROMPT.md`, parser `step-headings`; inspector renders an artifact select over the workflow's declared artifacts and a parser select) entries to the PALETTE array (`WorkflowNodeEditor.tsx:87`). + - **Palette**: add `foreach` (preset `source:"task-steps"`, auto-populating one `step-execute` child so the group is never confusingly empty), `step-review`, `parse-steps` (preset artifact `PROMPT.md`, parser `step-headings`; inspector renders an artifact select over the workflow's declared artifacts and a parser select sourced from the live registry incl. plugin parsers), and `code` (inspector: monospace source textarea with save-time esbuild error surfacing + timeout input) entries to the PALETTE array (`WorkflowNodeEditor.tsx:87`). - **Inspector fields**: `foreach` branch renders a `mode` select (sequential|parallel), an `isolation` select (shared|worktree — shared disabled when mode is parallel, matching the validator), a numeric `concurrency` input shown only in parallel mode (min 1, max 8, placeholder 2), and a numeric `maxReworkCycles` input (min 1, max 10, placeholder 3 — mirroring the `maxRetries` input pattern at `WorkflowNodeEditor.tsx:711`); `step-review` branch renders a `type` select (plan|code) and the existing `CustomModelDropdown` (optional, like the gate node's model field). - **Edge authoring**: selecting an edge whose source is a `step-review` node shows an edge inspector with a condition dropdown (approve/revise/rethink/unavailable — stored as `outcome:` conditions, displayed as short labels) and a "rework" toggle (sets `kind:"rework"`). Rework edges render dashed in the accent color with a loop indicator. - Validation errors from `parseWorkflowIr` surface inline at save (existing pattern); all strings `t("key","Default")`; i18n keys added across the 6 locales via the deep-merge convention (do not let the sync prune dynamic keys — prior incident). @@ -322,10 +336,10 @@ The task model is recast as: **core fields** (title, description) + **standard m ### U12 — `parse-steps` node, workflow artifacts & parser registry - **Goal**: KTD-12 (R16) — the `parse-steps` node handler, artifact declarations, and the parser registry. **Execution note: characterization-first** — pin `parseStepsFromPrompt` behavior before extraction. -- **Files**: Modify `packages/core/src/workflow-ir-types.ts` / `workflow-ir.ts` (`artifacts` declaration — parse-steps node-kind validation itself lands in U1), new `packages/core/src/step-parsers.ts` (registry + `step-headings` extraction + `json-steps`), `packages/core/src/store.ts` (`parseStepsFromPrompt` delegates to the registry), `packages/engine/src/workflow-node-handlers.ts` (parse-steps handler: read artifact → run parser → write step list via the graph-source projection path → outcome mapping), `packages/engine/src/executor.ts` (reconcile + planning-prompt artifact contract reads the workflow declaration; pin-protection check for parse-steps after expansion); tests new `packages/core/src/__tests__/step-parsers.test.ts`, new `packages/engine/src/__tests__/workflow-parse-steps.test.ts`, executor reconcile tests extended. +- **Files**: Modify `packages/core/src/workflow-ir-types.ts` / `workflow-ir.ts` (`artifacts` declaration — parse-steps node-kind validation itself lands in U1), new `packages/core/src/step-parsers.ts` (registry + `step-headings` extraction + `json-steps`), `packages/core/src/store.ts` (`parseStepsFromPrompt` delegates to the registry), `packages/engine/src/workflow-node-handlers.ts` (parse-steps handler: read artifact → run parser → write step list via the graph-source projection path → outcome mapping), `packages/engine/src/plugin-parser-adapter.ts` (new — plugin parser registration through the plugin runtime, mirroring `plugin-trait-adapter.ts`; timeout + fail-closed mapping to parse-error), `packages/engine/src/executor.ts` (reconcile + planning-prompt artifact contract reads the workflow declaration; pin-protection check for parse-steps after expansion); tests new `packages/core/src/__tests__/step-parsers.test.ts`, new `packages/engine/src/__tests__/workflow-parse-steps.test.ts`, executor reconcile tests extended. - **Approach**: per KTD-12 — the node is the only graph-side step-list writer; registry is built-in-only; foreach `source: "task-steps"` reads the projection; the default workflow keeps its legacy in-execute step init (untouched code path) while legacy `parseStepsFromPrompt` callers delegate to the same extracted parser (parity by construction); planning seam surfaces `producedBy: "planning"` artifact keys in its prompt. - **Patterns to follow**: trait registry (`trait-registry.ts`) for the registry shape; `createGateHandler` for handler wiring; task-documents machinery for artifact backing. -- **Test scenarios**: step-headings extraction byte-identical on existing fixtures (incl. depends annotations); json-steps parses `[{name, depends}]`; parse-steps writes steps through the graph-source projection path; `outcome:no-steps` on clean-empty parse routes (defaults success); `outcome:parse-error` on malformed artifact routes (defaults failure), not crash; missing artifact → parse-error; parse-steps after foreach expansion → pin-protection audited failure; default workflow parity (direct call vs registry resolution identical); reconcile reads through the workflow's parse-steps declaration. +- **Test scenarios**: step-headings extraction byte-identical on existing fixtures (incl. depends annotations); json-steps parses `[{name, depends}]`; parse-steps writes steps through the graph-source projection path; `outcome:no-steps` on clean-empty parse routes (defaults success); `outcome:parse-error` on malformed artifact routes (defaults failure), not crash; missing artifact → parse-error; parse-steps after foreach expansion → pin-protection audited failure; default workflow parity (direct call vs registry resolution identical); reconcile reads through the workflow's parse-steps declaration; plugin parser happy path through the runner; plugin parser timeout/throw → parse-error (fail-closed, audited); IR referencing an unregistered plugin parser rejected at save. - **Verification**: characterization tests green pre/post extraction; core + engine suites green. ### U13 — Dynamic task UI: field rendering + Fields panel @@ -336,6 +350,15 @@ The task model is recast as: **core fields** (title, description) + **standard m - **Test scenarios**: each widget type renders + edits + validation error inline; card badge placement honors max-3 overflow; enum color applied; orphaned values shown in disclosure, absent from card; Fields panel CRUD + option color editor + save-time IR validation surfaced; zero-fields snapshot identical; SSE workflow:updated refreshes field defs. - **Verification**: dashboard component shards green; `qualityAppComponentTests` registration verified; TUI render test. +### U14 — `code` node: compile, sandbox runner, harness contract + +- **Goal**: KTD-15 (R19). **Execution note: test-first** for the harness contract and failure modes. +- **Files**: Modify `packages/core/src/workflow-ir-types.ts` / `workflow-ir.ts` (`code` node kind + save-time esbuild syntax validation + source/timeout clamps), new `packages/engine/src/code-node-runner.ts` (esbuild in-memory compile + child-process execution + harness I/O + output caps), `packages/engine/src/workflow-node-handlers.ts` (handler wiring: ctx assembly incl. `foreach:active`, result mapping to outcome/contextPatch/customFields-via-U11-authority); tests new `packages/engine/src/__tests__/code-node.test.ts`, IR validation tests extended. +- **Approach**: per KTD-15 — esbuild is already in the toolchain (vitest); child process gets cwd = worktree, a minimal env, and the serialized ctx; no store handle crosses the boundary; returned customFields patch goes through `updateTaskCustomFields`; stdout/stderr captured (capped) into the node result for the audit log. +- **Patterns to follow**: script-step execution for process spawning + capture posture; `createPromptLikeHandler` for handler shape; U11 authority for field writes. +- **Test scenarios**: happy path returns value + routes success; `outcome: "foo"` routes `outcome:foo` edge; contextPatch merges; customFields patch validated (invalid → node failure with typed rejection surfaced); syntax error rejected at IR save; runtime throw → failure audited; timeout kills the child and fails; output cap enforced; source size cap; inside a foreach template receives `instance`; no steps-write path exists (attempting one is not expressible via the contract). +- **Verification**: code-node suite green; validator round-trip green. + ### U9 — Agent tools, plugin SDK, docs, changeset - **Goal**: Agent-native parity and documentation (R13, R16/R17 agent halves). @@ -347,7 +370,7 @@ The task model is recast as: **core fields** (title, description) + **standard m U1 → (U2 ∥ U4-core-half) → U3 → U5 → U6 → U10 → U7; U9 last. U2, U3, U5, U6, U10 are **strictly serial** (all touch `executor.ts`/graph executor). U4's core-only half (db.ts migration + store CRUD) parallelizes with U2; U4's executor wiring (`buildStepInstancePersistence`) waits for U3. U10 (parallel/isolated execution) builds on sequential foreach (U3) + projection discipline (U6) and lands before U7 so the parity/invariant suite covers all modes. U8 may start IR-type authoring, palette, inspector, and CSS work in parallel with U3–U6 (the `foreach:active` context decision is now committed in U3's approach, so no mid-stream rewrite risk); its round-trip tests land after U1 is merged. -The task-shape track is largely independent of the step-execution track: **U11 (fields core) parallelizes with U2–U6** (core-only, no executor surface beyond the U4 migration it shares); **U12 (artifacts/parsers) follows U1** (shares IR validation) and its executor touches (reconcile/planning contract) slot between U6 and U10 in the serial executor chain; **U13 (dynamic UI) follows U11** and parallelizes with U8 (different dashboard surfaces; both register component tests). U7's parity scope includes U12's default-workflow declaration. +The task-shape track is largely independent of the step-execution track: **U11 (fields core) parallelizes with U2–U6** (core-only, no executor surface beyond the U4 migration it shares); **U12 (artifacts/parsers) follows U1** (shares IR validation) and its executor touches (reconcile/planning contract) slot between U6 and U10 in the serial executor chain; **U13 (dynamic UI) follows U11** and parallelizes with U8 (different dashboard surfaces; both register component tests). U7's parity scope includes U12's default-workflow declaration. **U14 (code node) follows U1** (node kind) and **U11** (field-write authority); its engine half is independent of the U2–U10 executor chain except the handler-registration touch, which slots anywhere after U3. --- @@ -363,6 +386,8 @@ The task-shape track is largely independent of the step-execution track: **U11 ( - **Worktree pool pressure**: per-instance worktrees multiply pool usage. Mitigation: concurrency cap 8, semaphore clamp, and release-on-integration hygiene in U10; pool reuse machinery already exists for fan-out branches. - **Parser extraction parity (KTD-12)**: `parseStepsFromPrompt` has subtle behaviors (auto-reinit interplay, regex edge cases) that a registry indirection could perturb. Mitigation: characterization-first in U12; default workflow resolves to the same extracted function, asserted identical on existing fixtures. - **Dynamic UI regression surface (KTD-14)**: schema-driven rendering touches TaskCard/TaskDetailModal, the two highest-traffic components. Mitigation: zero-fields snapshot guard (today's UI byte-identical when no fields are defined); new rendering isolated in `TaskFieldsSection`; card overflow bounded at 3. +- **Code-node misuse surface (KTD-15)**: arbitrary TS in workflows can do anything the user can. Mitigation: explicit trust framing (same tier as existing script steps), no store handle across the boundary, field writes only through the validated patch, steps never writable, clamped timeout + output caps, save-time compile rejection, full stdout/stderr audit capture. +- **Plugin parser availability drift (KTD-12)**: a workflow saved against a plugin parser can outlive the plugin. Mitigation: save-time registry check; at runtime fail-closed to `parse-error` (routable), never a crash; audit names the missing parser id. - **Field-schema drift vs stored values**: workflow edits can strand values. Mitigation: orphan-not-delete posture (KTD-13), `coerce` confirmation on incompatible type changes, orphaned-fields disclosure in detail UI. ## System-Wide Impact