# Workflow Console Selected-Step Dataflow Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Let console operators inspect and atomically replace capability-step input bindings, output-to-state bindings, and optional execution metadata without editing raw JSON. **Architecture:** Extend the existing authored Effect/browser operation seam with the two focused binding methods already present in the checked contract manifest. Parse canonical draft data into pure selected-step projections, submit complete ordered binding lists through the shared draft-authoring client/controller, and render three inspector tabs that always rehydrate from the returned canonical draft. **Tech Stack:** React 19, TypeScript, Effect RPC, generated workflow contract schemas, Valibot domain decoders, Vite/Vitest, Testing Library, Hono browser proxy, Playwright/Chrome smoke testing. ## Global Constraints - The returned canonical draft is the only editable workflow document; do not add browser-owned graph persistence. - Use `runtimeSchemasFor(...)` and the checked generated workflow contract; do not hand-write duplicate transport schemas. - Input and output binding replacement preserves list order, nested paths, literals, explicit null, whole-payload bindings, and repeated-source fan-out. - New output targets are projected into workflow state by the existing backend operation; the browser does not synthesize state schema patches. - Blank retry and timeout values are optional. Retry accepts integer `0` or greater; timeout accepts values greater than `0` or remains absent. - Keep the browser operation allowlist explicit and fail closed for unrelated mutations. - Direct drag-to-bind, workflow-level contracts, non-capability steps, state deletion, and dataflow graph edges are out of scope. - Preserve revision checks, target provenance, operation evidence, conflict reload/reapply, mobile sheets, and accessible keyboard alternatives. - Use TDD for every task. Do not modify Serena configuration. --- ## File Structure ### Transport Boundary - `src/wf_core/models/steps.py`: canonical recursive JSON literal type for input value bindings. - `tests/core/test_canonical_node_bindings.py`: JSON literal model/schema coverage. - `tests/wf_contract_manifest/test_generate.py`: generated manifest coverage for literal JSON values. - `contracts/workflow-api.manifest.json`: regenerated neutral workflow contract. - `web/packages/rpc/scripts/workflow-contract-generator.ts`: authored runtime operation cohort. - `web/packages/rpc/scripts/workflow-contract-generator.test.ts`: runtime cohort generation coverage. - `web/packages/rpc/src/generated/workflow-contract.ts`: regenerated TypeScript contract. - `web/packages/rpc/src/rpcs.ts`: authored Effect RPC definitions generated from checked schemas. - `web/packages/rpc/src/service.ts`: operation dispatch to the JSON-RPC client. - `web/packages/rpc/src/method-registry.ts`: labels, interpretations, and equivalent CLI evidence. - `web/packages/rpc/src/index.ts`: public exports. - `web/apps/console/src/connection/contracts.ts`: browser response operation decoder. - `web/apps/server/src/browser-operation-policy.ts`: explicit browser allowlist. ### Workspace Domain - `web/apps/console/src/workspace/domain/draft-workspace-models.ts`: canonical binding and focused mutation input types. - `web/apps/console/src/workspace/domain/draft-authoring-client.ts`: transport-neutral selected-step mutation interface. - `web/apps/console/src/workspace/authoring/selected-step-dataflow.ts`: pure draft/schema projection and form serialization. - `web/apps/console/src/workspace/authoring/useDraftAuthoring.ts`: mutation lifecycle, conflict preservation, and reapply. ### React Surfaces - `web/apps/console/src/workspace/authoring/CapabilitySetupForm.tsx`: presence-aware metadata patch form. - `web/apps/console/src/workspace/authoring/StepInputBindingsForm.tsx`: ordered path/literal input editor. - `web/apps/console/src/workspace/authoring/StepOutputBindingsForm.tsx`: ordered output-to-state editor and inferred-schema preview. - `web/apps/console/src/workspace/authoring/SelectedCapabilityInspector.tsx`: Setup/Inputs/Outputs tab composition. - `web/apps/console/src/workspace/authoring/ContextInspector.tsx`: selection routing only. - `web/apps/console/src/workspace/authoring/authoring-graph.ts`: compact binding-count summary on capability nodes. - `web/apps/console/src/graph/graph-model.ts`: optional node summary carried through the shared graph model. - `web/apps/console/src/graph/WorkflowGraph.tsx`: render the optional node summary. - `web/apps/console/src/styles/global.css`: inspector tabs, ordered rows, responsive controls, and node summary styling. --- ### Task 1: Author The Two Binding RPC Operations **Files:** - Modify: `web/packages/rpc/src/rpcs.ts` - Modify: `web/packages/rpc/src/service.ts` - Modify: `web/packages/rpc/src/method-registry.ts` - Modify: `web/packages/rpc/src/index.ts` - Modify: `web/packages/rpc/src/service.test.ts` - Modify: `web/packages/rpc/src/method-registry.test.ts` - Modify: `web/packages/rpc/src/json-schema/runtime-schema.test.ts` - Modify: `web/packages/rpc/src/json-schema/rpc-parity.test.ts` - Modify: `web/packages/rpc/src/json-schema/authored-rpc-fixtures.ts` - Modify: `web/packages/rpc/scripts/workflow-contract-generator.ts` - Modify: `web/packages/rpc/scripts/workflow-contract-generator.test.ts` - Modify: `src/wf_core/models/steps.py` - Modify: `tests/core/test_canonical_node_bindings.py` - Modify: `tests/wf_contract_manifest/test_generate.py` - Regenerate: `contracts/workflow-api.manifest.json` - Regenerate: `web/packages/rpc/src/generated/workflow-contract.ts` - Modify: `web/apps/console/src/connection/contracts.ts` - Modify: `web/apps/server/src/browser-operation-policy.ts` - Modify: `web/apps/server/src/browser-operation-policy.test.ts` - Modify: `web/apps/server/src/app.test.ts` **Interfaces:** - Produces: callable operations `workflow.draft_workspaces.set_step_input_bindings` and `workflow.draft_workspaces.set_step_output_bindings`. - Produces: payload/result schema exports named `WorkflowDraftWorkspacesSetStepInputBindingsPayloadSchema`, `WorkflowDraftWorkspacesSetStepInputBindingsResultSchema`, `WorkflowDraftWorkspacesSetStepOutputBindingsPayloadSchema`, and `WorkflowDraftWorkspacesSetStepOutputBindingsResultSchema`. - Preserves: the explicit browser policy independent from generated operation inventory. - [ ] **Step 1: Write failing canonical JSON-literal and generator tests** Add model and manifest tests proving `InputValueBinding.value` accepts and describes every JSON value category: string, number, boolean, null, array, and object. The generated TypeScript `InputValueBinding["value"]` must be a recursive JSON value, not `{ [key: string]: unknown }`. Replace the object-only `InputValueBindingSchema.value` in `authored-rpc-fixtures.ts` with the same recursive JSON-value semantics. Add fixture decode assertions for scalar, null, array, and object literals so parity cannot pass with a narrower authored reference. Extend `workflow-contract-generator.test.ts` so removing either focused binding operation from the complete fixture fails with `missing runtime operation`, and so both operation names and their reachable binding schemas appear in `workflowRuntimeContract`. - [ ] **Step 2: Run the contract tests and verify RED** Run: ```powershell uv run pytest tests/core/test_canonical_node_bindings.py tests/wf_contract_manifest/test_generate.py -q pnpm --dir web --filter @lda/workflow-rpc test -- scripts/workflow-contract-generator.test.ts ``` Expected: failures because unconstrained Python `object` generates an object-only TypeScript value and the two methods are absent from the runtime operation cohort. - [ ] **Step 3: Repair the canonical contract and regenerate checked outputs** Define one recursive JSON-value alias at the canonical binding model seam and use it for `InputValueBinding.value`. Keep booleans distinct from integers in tests and reject non-JSON Python objects. Add both focused methods to `runtimeOperationNameList`. Regenerate in dependency order: ```powershell uv run python -m wf_contract_manifest write pnpm --dir web --filter @lda/workflow-rpc contract:write pnpm --dir web --filter @lda/workflow-rpc contract:check ``` Do not edit either generated file manually. - [ ] **Step 4: Write failing RPC service and registry tests** Add representative cases to `service.test.ts`: ```ts { operation: "workflow.draft_workspaces.set_step_input_bindings" as const, params: { workspace_id: "console.demo", revision: 3, step_id: "render", bindings: [ { path: "input.title", target: "report.title" }, { target: "format", value: "markdown" }, ], }, result: draftWorkspaceResult, }, { operation: "workflow.draft_workspaces.set_step_output_bindings" as const, params: { workspace_id: "console.demo", revision: 4, step_id: "render", bindings: [{ source: "report", target: "state.report" }], }, result: draftWorkspaceResult, }, ``` Add registry assertions that input bindings render `wf draft set-input` and output bindings render `wf draft set-output`. Add dedicated evidence renderers for these replacement commands: the existing add-capability renderer's `--input` syntax must remain unchanged. Use `--clear` for an empty list; use repeated `--map`/`--value` only when exactly representable, otherwise mark the CLI evidence non-equivalent and point to `--bindings-file`. - [ ] **Step 5: Write failing policy and contract tests** Extend `browser-operation-policy.test.ts` to require both focused methods and continue rejecting `workflow.draft_workspaces.replace_document`, `workflow.draft_workspaces.remove_step`, and `workflow.admin.auth.list`. Extend `app.test.ts` with one accepted request per operation. Extend runtime schema and RPC parity tests so both payload and success sides translate from the checked manifest and enforce exact canonical binding unions. Add matching entries to `authored-rpc-fixtures.ts`; parity coverage must compare both generated schemas against those authored reference schemas. - [ ] **Step 6: Run the focused tests and verify RED** Run: ```powershell pnpm --dir web --filter @lda/workflow-rpc test -- src/service.test.ts src/method-registry.test.ts src/json-schema/runtime-schema.test.ts src/json-schema/rpc-parity.test.ts pnpm --dir web --filter @lda/server test -- src/browser-operation-policy.test.ts src/app.test.ts ``` Expected: failures because the two operations are not in `WorkflowRpcs`, service dispatch, registry metadata, browser contracts, or browser policy. - [ ] **Step 7: Implement the authored RPC definitions and dispatch** Follow the existing update-capability pattern in `rpcs.ts`: ```ts const setStepInputBindingsSchemas = runtimeSchemasFor( "workflow.draft_workspaces.set_step_input_bindings", ); export const WorkflowDraftWorkspacesSetStepInputBindingsPayloadSchema = setStepInputBindingsSchemas.payload; export const WorkflowDraftWorkspacesSetStepInputBindingsResultSchema = setStepInputBindingsSchemas.success; export const WorkflowDraftWorkspacesSetStepInputBindings = Rpc.make( "workflow.draft_workspaces.set_step_input_bindings", { payload: WorkflowDraftWorkspacesSetStepInputBindingsPayloadSchema, success: WorkflowDraftWorkspacesSetStepInputBindingsResultSchema, error: Schema.Never, }, ); ``` Create the matching output definition. Add both to `WorkflowRpcs`, service imports/switch cases, registry metadata, and public exports. Decode parameters before dispatch exactly like existing draft operations. - [ ] **Step 8: Open only the narrow browser seam** Add both names to `OperationNameSchema` and `browserAllowedOperationNames`. Update tests in the same authored order. Do not derive the browser list from generated `WorkflowOperationName`. - [ ] **Step 9: Run focused verification and commit** Run the focused commands from Step 6 plus: ```powershell pnpm --dir web --filter @lda/workflow-rpc typecheck pnpm --dir web --filter @lda/server typecheck ``` Expected: all pass. Commit: ```powershell git add web/packages/rpc web/apps/console/src/connection/contracts.ts web/apps/server/src git add src/wf_core/models/steps.py tests/core/test_canonical_node_bindings.py tests/wf_contract_manifest/test_generate.py contracts/workflow-api.manifest.json git commit -m "feat: expose canonical step binding RPCs" ``` --- ### Task 2: Project Canonical Selected-Step Dataflow **Files:** - Modify: `web/apps/console/src/workspace/domain/draft-workspace-models.ts` - Create: `web/apps/console/src/workspace/authoring/selected-step-dataflow.ts` - Create: `web/apps/console/src/workspace/authoring/selected-step-dataflow.test.ts` - Modify: `web/apps/console/src/workspace/authoring/canonical-capability-form.ts` - Create: `web/apps/console/src/workspace/authoring/canonical-capability-form.test.ts` **Interfaces:** - Produces: `StatePath`, `OutputBinding`, `SetStepInputBindingsInput`, and `SetStepOutputBindingsInput` domain types. - Produces: `SelectedStepDataflow`, `projectSelectedStepDataflow(draft, stepId)`, `inputBindingRows(...)`, `outputBindingRows(...)`, and canonical row serializers. - Produces: `bindingDiagnosticsForStep(...)` for row-owned and unmatched diagnostics. - Produces: `CapabilitySetupPatch`, shared by the controller and Setup form. - Produces: metadata presence that distinguishes absent, explicit null, and numeric values. - [ ] **Step 1: Write failing pure projection tests** Use both a keyed draft and the equivalent compiled `nodes[]` draft containing: ```ts steps: { render: { use: "wf.std.concat", input: [ { path: "input.items", target: "items" }, { target: "separator", value: null }, { path: "state.fallback", target: "fallback" }, ], output: [ { source: "text", target: "state.report" }, { source: "text", target: "state.audit.latest" }, ], retry: 0, }, }, ``` Assert exact list order, explicit null, repeated output source, absent timeout, and `retry: 0`. Add cases for structural path objects, whole payload `.`, empty lists, malformed records with explicit unsupported reasons, and a missing step. The keyed and compiled shapes must project to the same `SelectedStepDataflow`. - [ ] **Step 2: Run the pure test and verify RED** Run: ```powershell pnpm --dir web --filter @lda/console test -- src/workspace/authoring/selected-step-dataflow.test.ts ``` Expected: failure because the module and output binding types do not exist. - [ ] **Step 3: Add canonical domain types** Add: ```ts export type StatePath = | string | { readonly root: "state"; readonly parts: string[] }; export type OutputBinding = { readonly source: LocalInputPath; readonly target: StatePath; }; export type SetStepInputBindingsInput = { readonly workspaceId: string; readonly revision: number; readonly stepId: string; readonly bindings: ReadonlyArray; }; export type SetStepOutputBindingsInput = { readonly workspaceId: string; readonly revision: number; readonly stepId: string; readonly bindings: ReadonlyArray; }; ``` - [ ] **Step 4: Implement parser-first projection** Define a compound return type with a docstring: ```ts export type SelectedStepDataflow = { readonly stepId: string; readonly capabilityName: string; readonly description: string | null | undefined; readonly retry: number | null | undefined; readonly timeoutSeconds: number | null | undefined; readonly inputs: ReadonlyArray; readonly outputs: ReadonlyArray; readonly unsupported: ReadonlyArray; }; export type UnsupportedBindingRow = { readonly field: "input" | "output"; readonly index: number; readonly raw: unknown; readonly reason: string; }; export type CapabilitySetupPatch = { readonly description?: string | null; readonly retry?: number | null; readonly timeoutSeconds?: number | null; }; ``` Use type guards for records and structural path variants. Preserve valid rows in order. Do not coerce malformed bindings into maps or discard them silently; append an indexed unsupported reason so the inspector can block destructive replacement until the operator acknowledges or repairs the raw shape. Unsupported rows remain in their original positions as read-only raw previews with an explicit `Remove unsupported row` action. A save stays blocked while any unsupported row remains. Removing one is an explicit repair decision and serializes the remaining canonical rows in their original relative order. Although `StatePath` remains wire-compatible with generated types, output-row serialization must reject bare `state`, `state.`, empty structural `parts`, and empty path segments. Test canonical nested targets and each rejected form. Update `canonical-capability-form.ts` so absent metadata remains `undefined` instead of being collapsed to `null` by `?? null`. - [ ] **Step 5: Add schema-informed suggestions and preview helpers** Add pure helpers that return canonical strings: ```ts workflowSourceSuggestions(inputSchema, stateSchema): ReadonlyArray capabilityLocalPathSuggestions(capabilitySchema): ReadonlyArray stateTargetSuggestions(stateSchema): ReadonlyArray inferredStateSchemaPreview(outputSchema, source, target): unknown | null bindingDiagnosticsForStep(diagnostics, stepId, field): BindingDiagnostics ``` Reuse `normalizeSchema`, `parseTOMLPath`, and `formatTOMLPath`. Do not implement JSON Schema compatibility in the browser; the preview only shows the selected source schema the backend will project. `BindingDiagnostics` contains row issues keyed by binding index plus unmatched issues. Accept only these structural path forms: - `nodes[N].input[M]...` / `nodes[N].output[M]...`, where `N` is the selected step's index in the compiled `nodes[]` array; - `/steps//input/M/...` / `/steps//output/M/...`, using JSON Pointer escaping; and - `bindings[M]...` only when `diagnostic.step_id` equals the selected step and the caller supplies the field for the focused operation. Field-level paths without `M`, mismatched `step_id`, unknown formats, and prose messages are unmatched. Forms receive row issues explicitly. Unmatched issues remain in the shared ContextInspector diagnostics panel. Add positive and negative tests for every accepted form. - [ ] **Step 6: Verify and commit** Run: ```powershell pnpm --dir web --filter @lda/console test -- src/workspace/authoring/selected-step-dataflow.test.ts src/workspace/authoring/canonical-capability-form.test.ts pnpm --dir web --filter @lda/console typecheck ``` Commit: ```powershell git add web/apps/console/src/workspace/domain/draft-workspace-models.ts web/apps/console/src/workspace/authoring/selected-step-dataflow.* web/apps/console/src/workspace/authoring/canonical-capability-form* git commit -m "feat: project selected-step dataflow" ``` --- ### Task 3: Add Focused Binding Methods To The Console Client **Files:** - Modify: `web/apps/console/src/workspace/domain/draft-authoring-client.ts` - Modify: `web/apps/console/src/workspace/domain/draft-authoring-client.test.ts` **Interfaces:** - Consumes: `SetStepInputBindingsInput` and `SetStepOutputBindingsInput` from Task 2. - Produces: `DraftAuthoringClient.setStepInputBindings(input)` and `DraftAuthoringClient.setStepOutputBindings(input)`. - [ ] **Step 1: Write exact payload tests** Add tests that call both client methods and assert: ```ts expect(run).toHaveBeenCalledWith( "workflow.draft_workspaces.set_step_output_bindings", { workspace_id: "report", revision: 7, step_id: "render", bindings: [ { source: "text", target: "state.report" }, { source: "text", target: "state.audit.latest" }, ], }, decodeDraftWorkspace, ); ``` Cover explicit input null, structural paths, empty arrays, blank identifiers, and caller-array immutability. - [ ] **Step 2: Run the client test and verify RED** Run: ```powershell pnpm --dir web --filter @lda/console test -- src/workspace/domain/draft-authoring-client.test.ts ``` Expected: failures because the methods are absent. - [ ] **Step 3: Implement minimal focused methods** Use `requireIdentifier`, copy each binding record into a fresh array, pass the current revision unchanged, and decode with `decodeDraftWorkspace`. Do not add merge flags because the UI replaces complete canonical lists. - [ ] **Step 4: Verify and commit** Run the focused test and console typecheck. Commit: ```powershell git add web/apps/console/src/workspace/domain/draft-authoring-client.ts web/apps/console/src/workspace/domain/draft-authoring-client.test.ts git commit -m "feat: add selected-step binding client" ``` --- ### Task 4: Extend The Authoring Controller Without Losing Conflict State **Files:** - Modify: `web/apps/console/src/workspace/authoring/useDraftAuthoring.ts` - Modify: `web/apps/console/src/workspace/authoring/useDraftAuthoring.test.tsx` **Interfaces:** - Consumes: focused client methods from Task 3. - Produces: `setStepInputs(bindings)`, `setStepOutputs(bindings)`, `updateSetup(patch)`, and tab-scoped preserved submissions. - Preserves: one pending mutation, revision conflict reload/reapply, target provenance, and selected node identity. - [ ] **Step 1: Write failing controller tests for all three submissions** Test a selected node named `render` and assert: - `setStepInputs` uses the selected node id and current revision; - `setStepOutputs` accepts repeated sources and commits the returned draft; - `updateSetup({ retry: 0 })` sends only retry; - `updateSetup({ timeoutSeconds: null })` clears an existing timeout; - an untouched optional field is absent from the update object; - duplicate clicks share the same pending promise; - a second different mutation is rejected while one is pending; and - stale-target responses do not replace the current draft. - [ ] **Step 2: Write failing conflict/reapply tests** Return a conflict draft, change selection, reload revision 8, and call reapply. Assert the mutation still targets the original immutable step id and preserves the exact ordered rows. Add one test each for input and output submissions. Assert the preserved submission records its tab and exact serializable form payload. Controller state owns the last submitted mutation for conflict reapply; it does not own every unsaved keystroke. - [ ] **Step 3: Run controller tests and verify RED** Run: ```powershell pnpm --dir web --filter @lda/console test -- src/workspace/authoring/useDraftAuthoring.test.tsx ``` - [ ] **Step 4: Generalize last submissions at the controller seam** Extend `LastSubmission` with: ```ts | { readonly kind: "setup"; readonly targetStepId: string; readonly patch: CapabilitySetupPatch } | { readonly kind: "inputs"; readonly targetStepId: string; readonly bindings: ReadonlyArray } | { readonly kind: "outputs"; readonly targetStepId: string; readonly bindings: ReadonlyArray } ``` Keep `runMutation` as the one lifecycle implementation. Add a small helper that captures the selected node id before submission. Reapply dispatches through the same focused controller methods against the refreshed revision. - [ ] **Step 5: Preserve submitted conflict state without stealing form ownership** Keep the discriminated `LastSubmission` as the exact submitted Setup, Inputs, or Outputs payload. `SelectedCapabilityInspector`, keyed by selected `stepId`, owns active tab and unsaved controlled rows. The existing mobile sheet remains mounted while closed, so closing/reopening preserves these values. Reset local forms only when selection changes, the operator explicitly reloads, or a confirmed canonical response is adopted. On conflict, keep local values and use `LastSubmission` for the explicit reapply action. Document this split near the controller state seam. - [ ] **Step 6: Verify and commit** Run the focused tests and console typecheck. Commit: ```powershell git add web/apps/console/src/workspace/authoring/useDraftAuthoring.ts web/apps/console/src/workspace/authoring/useDraftAuthoring.test.tsx git commit -m "feat: control selected-step dataflow mutations" ``` --- ### Task 5: Build Presence-Aware Setup And Ordered Input Forms **Files:** - Create: `web/apps/console/src/workspace/authoring/CapabilitySetupForm.tsx` - Create: `web/apps/console/src/workspace/authoring/CapabilitySetupForm.test.tsx` - Create: `web/apps/console/src/workspace/authoring/StepInputBindingsForm.tsx` - Create: `web/apps/console/src/workspace/authoring/StepInputBindingsForm.test.tsx` - Modify: `web/apps/console/src/workspace/authoring/CapabilityNodeForm.tsx` - Modify: `web/apps/console/src/workspace/authoring/CapabilityNodeForm.test.tsx` - Modify: `web/apps/console/src/workspace/schema-form/schema-field.ts` - Modify: `web/apps/console/src/workspace/schema-form/schema-field.test.ts` **Interfaces:** - Consumes: `CapabilitySetupPatch` from Task 2 with omitted/null/value metadata semantics. - Produces: `StepInputBindingsForm` that submits a complete `ReadonlyArray`. - Consumes: input-row issues from `bindingDiagnosticsForStep(...)`; unmatched diagnostics remain outside the form. - Reuses: normalized schema fields and `SchemaFieldControl` for literal editing. - [ ] **Step 1: Pin the optional metadata regression** Add a `CapabilityNodeForm` test that leaves retry and timeout blank, submits a valid capability input, and expects both keys to be absent from the add value. Add Setup tests for untouched blank, clearing an existing value, retry `0`, rejected retry `-1`, rejected fractional retry, rejected timeout `0`, and valid positive timeout. - [ ] **Step 2: Write failing ordered input editor tests** Render initial path, literal-null, and nested-target rows. Assert accessible controls for target, source mode, source path/value, move up, move down, remove, and add. Reorder rows and assert exact submission order. Select one source for two targets and assert fan-out is preserved. Submit `[]` through an explicit clear action. - [ ] **Step 3: Run tests and verify RED** Run: ```powershell pnpm --dir web --filter @lda/console test -- src/workspace/authoring/CapabilityNodeForm.test.tsx src/workspace/authoring/CapabilitySetupForm.test.tsx src/workspace/authoring/StepInputBindingsForm.test.tsx ``` - [ ] **Step 4: Implement presence-aware setup values** Use touched-field state rather than deriving patch presence from blank text: For add-node `CapabilityNodeForm`, omit blank optional metadata. Preserve the ability to add a step with explicit `retry: 0`. Use `min={0}`/`step={1}` for retry and `min` greater than zero for timeout, plus explicit parser messages so native validity is not the only error surface. - [ ] **Step 5: Expose schema field lookup for literal rows** Add a pure `schemaFieldAtPath(root, path)` helper to `schema-field.ts`. Test nested object, array, `.`, and missing paths. `StepInputBindingsForm` uses the matched field with `SchemaFieldControl`; an unmatched target falls back to a labelled JSON value editor with an explicit explanation. - [ ] **Step 6: Implement controlled ordered rows** Keep stable row ids separate from canonical data. Convert rows to canonical bindings only on change/submit. Validate: - local target is nonblank; - path source starts with `input.`, `state.`, or `context.`; - a row contains exactly one of path or value; and - canonical duplicates/overlaps remain server-authoritative unless locally unambiguous. Buttons provide reorder behavior; do not add drag sorting. - [ ] **Step 7: Verify and commit** Run focused tests, console typecheck, and React Doctor changed scope. Commit: ```powershell git add web/apps/console/src/workspace/authoring/CapabilityNodeForm* web/apps/console/src/workspace/authoring/CapabilitySetupForm* web/apps/console/src/workspace/authoring/StepInputBindingsForm* web/apps/console/src/workspace/schema-form/schema-field* git commit -m "feat: edit capability setup and inputs" ``` --- ### Task 6: Build The Output-To-State Binding Form **Files:** - Create: `web/apps/console/src/workspace/authoring/StepOutputBindingsForm.tsx` - Create: `web/apps/console/src/workspace/authoring/StepOutputBindingsForm.test.tsx` - Modify: `web/apps/console/src/styles/global.css` **Interfaces:** - Consumes: `OutputBinding`, output/state suggestions, and inferred preview helpers from Task 2. - Produces: complete ordered `ReadonlyArray` submissions. - Consumes: output-row issues from `bindingDiagnosticsForStep(...)`; unmatched diagnostics remain outside the form. - [ ] **Step 1: Write failing output form tests** Cover: - source choices from capability output schema; - target choices from existing state schema; - a new nested `state.report.markdown` target; - inferred source schema preview; - repeated source fan-out; - move up/down and remove; - explicit empty-list clearing confirmation; - malformed stored rows that block replacement instead of disappearing; and - row-scoped backend diagnostics. - [ ] **Step 2: Run the focused test and verify RED** Run: ```powershell pnpm --dir web --filter @lda/console test -- src/workspace/authoring/StepOutputBindingsForm.test.tsx ``` - [ ] **Step 3: Implement the controlled output editor** Source uses a select plus an escape-hatch text value for valid nested/whole local paths not represented by top-level schema suggestions. Target accepts a canonical `state.*` path and suggests existing fields. Render the inferred schema as a bounded summary, with raw JSON collapsed. Use explicit copy: ```text Saving a new target asks the workflow API to project this output schema into state. Clearing bindings does not delete existing state fields. ``` - [ ] **Step 4: Add responsive and accessible styling** Rows use a readable grid on desktop and stack inside the mobile inspector sheet. Keep add/save/clear actions reachable without horizontal scrolling. Hide no scrollbar that is needed to discover overflow. - [ ] **Step 5: Verify and commit** Run focused tests, console typecheck, and React Doctor changed scope. Commit: ```powershell git add web/apps/console/src/workspace/authoring/StepOutputBindingsForm* web/apps/console/src/styles/global.css git commit -m "feat: edit capability output bindings" ``` --- ### Task 7: Compose The Selected-Step Inspector And Acceptance Path **Files:** - Create: `web/apps/console/src/workspace/authoring/SelectedCapabilityInspector.tsx` - Create: `web/apps/console/src/workspace/authoring/SelectedCapabilityInspector.test.tsx` - Modify: `web/apps/console/src/workspace/authoring/ContextInspector.tsx` - Modify: `web/apps/console/src/workspace/authoring/ContextInspector.test.tsx` - Modify: `web/apps/console/src/workspace/authoring/authoring-graph.ts` - Modify: `web/apps/console/src/workspace/authoring/authoring-graph.test.ts` - Modify: `web/apps/console/src/graph/graph-model.ts` - Modify: `web/apps/console/src/graph/graph-model.test.ts` - Modify: `web/apps/console/src/graph/WorkflowGraph.tsx` - Modify: `web/apps/console/src/graph/WorkflowGraph.test.tsx` - Modify: `web/apps/console/src/workspace/authoring/DraftWorkbench.test.tsx` - Modify: `web/apps/console/src/workspace/routes/DraftDetailRoute.test.tsx` - Modify: `web/apps/console/src/styles/global.css` - Modify: `web/README.md` - Modify: `docs/current_roadmap.md` - Move after completion: `docs/superpowers/plans/2026-08-09-workflow-console-selected-step-dataflow.md` to `docs/historical/superpowers/plans/2026-08-09-workflow-console-selected-step-dataflow.md` **Interfaces:** - Consumes: Tasks 2–6. - Produces: complete Setup/Inputs/Outputs selected-step workflow. - Preserves: add-capability form, route inspector, canvas inspector, operation evidence, navigation protection, and mobile sheets. - [x] **Step 1: Write failing composition tests** Select a capability node and assert three tabs with Setup active initially. Switch to Inputs and Outputs without clearing local rows. Submit each form and assert the correct controller method, selected step id, and returned revision. Add tests for loading/error capability detail, unsupported stored binding shape, diagnostics, saving state, revision conflict, reload, and reapply. Close and reopen the mounted mobile sheet and assert the active tab and unsaved rows survive. Change the selected step and assert the inspector rehydrates from that step's canonical data instead of leaking the previous form. - [x] **Step 2: Write failing graph summary tests** Project a node with two inputs and one output. Assert its graph detail is `2 inputs · 1 state write`. Use singular forms for one and omit the summary for zero/zero. Assert `buildWorkflowGraph` carries a distinct `summary` field and the generic WorkflowGraph renders it without replacing the existing description/detail, route-edge selection, or node handles. - [x] **Step 3: Run integration tests and verify RED** Run: ```powershell pnpm --dir web --filter @lda/console test -- src/workspace/authoring/SelectedCapabilityInspector.test.tsx src/workspace/authoring/ContextInspector.test.tsx src/workspace/authoring/authoring-graph.test.ts src/graph/graph-model.test.ts src/graph/WorkflowGraph.test.tsx src/workspace/authoring/DraftWorkbench.test.tsx src/workspace/routes/DraftDetailRoute.test.tsx ``` - [x] **Step 4: Extract selected capability composition** Move the selected-node capability branch out of `ContextInspector` into `SelectedCapabilityInspector`. Keep selection routing and generic diagnostics in `ContextInspector`. Use stable tab ids and `role="tablist"`, `role="tab"`, and `role="tabpanel"`. Restore the active tab and preserved form after mobile sheet close/reopen and conflict reapply. - [x] **Step 5: Add truthful node summaries** Set an authoring-only raw-node `summary` from canonical binding counts and carry it into a new optional `WorkflowGraphNodeData.summary`. Render `data.summary` below the node reference while preserving `data.detail` for descriptions. Do not draw binding edges or State nodes in this slice. - [ ] **Step 6: Run full automated verification** Run: ```powershell pnpm --dir web test pnpm --dir web typecheck pnpm --dir web build Push-Location web/apps/console npx react-doctor@latest --verbose --scope changed Pop-Location git diff --check ``` Expected: all tests and typechecks pass; build succeeds with only previously accepted warnings; React Doctor does not regress. - [ ] **Step 7: Run real-server browser smoke** With the console, Hono server, and `wf-rpc-server` running: 1. Connect the console to `http://127.0.0.1:8765/rpc`. 2. Open a disposable draft containing a `wf.std.constant` or `wf.std.concat` capability step. 3. Confirm blank retry and timeout submit without entering zero. 4. Replace ordered input bindings, including one literal. 5. Bind one capability output to two `state.*` targets. 6. Confirm the returned revision increments and the state schema includes new projected targets. 7. Reload the route and confirm canonical rows persist in the same order. 8. Clear output bindings and confirm the state declarations remain. 9. Resize to the mobile breakpoint and confirm all tabs and reorder buttons remain usable. Capture a screenshot and operation evidence for the input and output success states under the gitignored `.visual-smoke/` directory. - [x] **Step 8: Update live documentation and archive the plan** Document selected-step Setup/Inputs/Outputs and canonical replacement behavior in `web/README.md`. Mark Roadmap Slice 3 completed and retain Slices 4–7 as planned. Move this completed plan to the historical path and update its roadmap link. - [x] **Step 9: Review and commit** Run the requesting-code-review skill on the complete slice. Fix all valid Critical and Important findings, rerun affected verification, then commit: ```powershell git add web/apps/console web/README.md docs/current_roadmap.md docs/superpowers/specs/2026-08-09-workflow-console-selected-step-dataflow-design.md docs/historical/superpowers/plans/2026-08-09-workflow-console-selected-step-dataflow.md git commit -m "feat: edit selected-step dataflow" ```