42 KiB
Workflow Console Contract Graph 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: Make workflow Input, State, Output, and Outcomes selectable and editable in the console while one backend-owned inventory supplies discoverable source and target choices, including node-scoped runtime context.
Architecture: Add a deep, read-only authoring-contract inspection interface above canonical draft schemas, capability contracts, and core execution-scope analysis. Carry its generated contract into the existing Effect RPC and console domain layers, then use one reusable path picker and four graph projections to drive focused canonical mutations. Existing focused write operations remain authoritative; the browser does not invent a second workflow model.
Tech Stack: Python 3.14, Pydantic 2, JSON Schema Draft 2020-12, pytest, JSON-RPC/OpenRPC, generated workflow contract manifest, React 19, TypeScript, Effect Schema, Valibot, React Flow, Vitest, Testing Library, Vite.
Global Constraints
- Input, State, Output, and Outcomes are derived graph projections, never persisted executable steps.
- Runtime context appears only when the backend reports applicable entries for the selected step; do not add a permanent Context graph projection.
- No high-level TypeScript module may hardcode runtime context key names.
- The current runtime exposes only the current foreach iteration alias; do not claim automatic inheritance of enclosing aliases.
- The normal UI is a searchable, grouped picker with labels, paths, types, and descriptions. Manual paths live under Advanced.
- The backend inventory is advisory for discoverability and obvious compatibility. Existing complete workflow validation remains authoritative.
- Preserve canonical ordered bindings, revision checks, conflict reapply, invalid-draft visibility, and mounted mobile-inspector state.
- Do not add step-id rename or a persisted per-step title. Existing step id remains the selected-step heading.
- Retry and timeout remain optional and presence-aware.
- Reuse existing focused operations for start, contract, and final output bindings. Do not introduce generic JSON Patch from the browser.
- Use checked generated transport schemas; do not hand-write duplicate wire contracts.
- Add comments/docstrings around graph-scope traversal, conservative availability, and unsupported-schema preservation.
- Do not modify Serena configuration.
File Structure
Core And Workflow API
src/wf_core/context_contracts.py: canonical runtime-context keys and schema descriptions shared by execution and inspection.src/wf_core/analysis/context_scopes.py: conservative abstract graph traversal that projects context availability by node.src/wf_core/runtime/ops/frames.py: runtime value projection using shared context key constants.src/wf_api/models/authoring_contracts.py: transport-neutral inventory payload types.src/wf_api/authoring_contracts.py: schema flattening, capability contract projection, and complete inventory composition.src/wf_api/surface.py,service.py: protocol-neutral inspection interface.src/wf_transport_rpc_http/models.py,methods/drafts.py,client/drafts.py: JSON-RPC request, dispatch, and remote client support.
Generated And Authored TypeScript Contract
contracts/workflow-api.manifest.json: regenerated canonical operation contract.web/packages/rpc/src/generated/workflow-contract.ts: regenerated TypeScript wire inventory.web/packages/rpc/src/rpcs.ts,method-registry.ts,service.ts: authored Effect operation.web/apps/console/src/connection/contracts.ts: explicit browser operation allowlist.
Console Domain And Authoring
web/apps/console/src/workspace/domain/authoring-contract-models.ts: decoded browser inventory types.web/apps/console/src/workspace/domain/authoring-contract-client.ts: read adapter for inventory inspection.web/apps/console/src/workspace/authoring/useAuthoringContract.ts: selection/revision-aware inventory controller.web/apps/console/src/workspace/authoring/AuthoringPathPicker.tsx: grouped searchable source/target picker.web/apps/console/src/workspace/authoring/workflow-contract-editor.ts: lossless contract form projection.web/apps/console/src/workspace/authoring/WorkflowContractInspector.tsx: Input, State, Output, and Outcomes forms.web/apps/console/src/workspace/authoring/authoring-graph.ts: contract projection nodes and binding-derived connectors.web/apps/console/src/workspace/authoring/AuthoringGraph.tsx,ContextInspector.tsx,DraftWorkbench.tsx: selection and composition.web/apps/console/src/workspace/authoring/useDraftAuthoring.ts: focused contract/start/output mutations and conflict reapply.web/apps/console/src/styles/global.css: contract projection, picker, and responsive inspector styles.
Task 1: Model Schema-Derived Authoring Choices
Files:
- Create:
src/wf_api/models/authoring_contracts.py - Create:
src/wf_api/authoring_contracts.py - Create:
tests/wf_api/test_authoring_contracts.py - Modify:
src/wf_api/models/__init__.py
Interfaces:
-
Produces:
AuthoringPathOptionPayload,AuthoringStepContractPayload, andAuthoringContractInventoryPayload. -
Produces:
schema_path_options(schema, *, root, uses) -> list[AuthoringPathOptionPayload]. -
Produces:
project_authoring_contract_inventory(...) -> AuthoringContractInventoryPayload; Task 2 supplies its context entries and Task 3 supplies persisted workspace/capability data. -
Step 1: Write failing schema inventory tests
Create fixtures with nested objects, required fields, arrays, $defs, descriptions, and unconstrained additionalProperties. Assert exact entries such as:
assert options[0] == {
"path": "input.request",
"label": "Request",
"origin": "workflow_input",
"schema": {"type": "object", "properties": {"id": {"type": "string"}}},
"required": True,
"availability": "available",
"uses": ["step_input", "workflow_output"],
}
Assert deterministic parent-before-child order, $ref resolution through the existing bounded schema helpers, whole-array selection without synthetic wildcard paths, and omission of invented names for unconstrained additional properties.
- Step 2: Run the tests and verify RED
uv run pytest tests/wf_api/test_authoring_contracts.py -q
Expected: import failures because the inventory models and projector do not exist.
- Step 3: Implement explicit payload types
Define these literals and payloads without Any-shaped public fields beyond JSON Schema objects:
type AuthoringPathOrigin = Literal[
"workflow_input", "workflow_state", "runtime_context",
"step_input", "step_output", "workflow_output",
]
type AuthoringPathAvailability = Literal["available", "conditional"]
type AuthoringPathUse = Literal[
"step_input", "step_output_source", "state_target", "workflow_output",
]
class AuthoringPathOptionPayload(TypedDict):
path: str
label: str
origin: AuthoringPathOrigin
schema: JsonObject
required: bool
availability: AuthoringPathAvailability
uses: list[AuthoringPathUse]
description: NotRequired[str]
reason: NotRequired[str]
AuthoringContractInventoryPayload contains workspace_id, revision, selected_step_id, readable_sources, step_input_targets, step_output_sources, state_targets, workflow_output_targets, entry_steps, workflow_outcomes, and warnings.
- Step 4: Implement bounded schema flattening and inventory composition
Reuse schema_fragment_at_location and local $ref limits from wf_api.schema_projection. Derive labels from schema title, otherwise humanize the final path segment. Copy descriptions only when they are strings. Return {} for a known path whose schema is intentionally unconstrained; never infer a type from a field name or runtime value.
Keep project_authoring_contract_inventory pure: pass all schemas, selected-step details, context entries, entry candidates, and warnings as arguments. Do not make it load stores or capability sources itself.
- Step 5: Verify and commit
uv run pytest tests/wf_api/test_authoring_contracts.py tests/wf_api/test_schema_projection.py -q
uv run basedpyright --level error src/wf_api/authoring_contracts.py src/wf_api/models/authoring_contracts.py
Commit:
git add src/wf_api/authoring_contracts.py src/wf_api/models/authoring_contracts.py src/wf_api/models/__init__.py tests/wf_api/test_authoring_contracts.py
git commit -m "feat: project authoring contract choices"
Task 2: Analyze Node-Scoped Runtime Context
Files:
- Create:
src/wf_core/context_contracts.py - Create:
src/wf_core/analysis/context_scopes.py - Create:
src/wf_core/analysis/__init__.py - Modify:
src/wf_core/runtime/ops/frames.py - Create:
tests/core/test_context_scopes.py - Modify:
tests/core/test_scheduler.py - Modify:
tests/wf_api/test_authoring_contracts.py
Interfaces:
-
Produces:
STANDARD_CONTEXT_FIELDS,foreach_context_fields(alias, item_schema), and the existingframe_context_values(frame)behavior using shared key constants. -
Produces:
context_fields_by_node(workflow: Workflow) -> dict[str, tuple[ContextFieldAvailability, ...]]. -
Consumes: Task 1 inventory composition and returns runtime-context options with
availableorconditionalavailability. -
Step 1: Write failing runtime-contract and graph-analysis tests
Cover:
- standard frame keys on ordinary nodes;
- serial and concurrent foreach loop targets;
- configured alias plus
loop_itemandloop_index; - a node reached only inside one foreach as
available; - a node reached both from root and a foreach child as
conditional; - nested foreach where the inner child exposes only the inner alias;
- return to the outer frame after the nested foreach preserves the outer alias;
- malformed/missing loop routes produce warnings and no guaranteed alias; and
- traversal terminates on cyclic graphs by memoizing
(node_id, frame_scope).
Use field-level assertions rather than whole-object equality for warning text.
- Step 2: Run the tests and verify RED
uv run pytest tests/core/test_context_scopes.py tests/core/test_scheduler.py tests/wf_api/test_authoring_contracts.py -q
Expected: imports fail for the new context contract and analysis modules.
- Step 3: Centralize runtime-context names and schemas
Move the semantic key registry, not runtime values, into context_contracts.py:
STANDARD_CONTEXT_FIELDS = (
ContextFieldContract("prior_outcome", {"type": ["string", "null"]}, "Prior route outcome"),
ContextFieldContract("activated_incoming_edge", {"type": ["string", "null"]}, "Incoming step id"),
ContextFieldContract("scope_id", {"type": "string"}, "Execution scope id"),
ContextFieldContract("lineage_id", {"type": "string"}, "Execution lineage id"),
ContextFieldContract("parent_lineage_id", {"type": ["string", "null"]}, "Parent lineage id"),
)
frame_context_values continues to read actual frame values but uses these constants. foreach_context_fields returns loop_item, loop_index, and the configured alias, deduplicating an alias equal to a standard loop key.
- Step 4: Implement conservative abstract traversal
Traverse states shaped as (node_id, active_foreach_id | None). Normal routes retain the active frame scope. A foreach loop route enters a new child frame owned by that foreach; its other routes stay in the current frame. A nested foreach child replaces, rather than inherits, the outer iteration metadata because that matches frame_context_values today.
Aggregate every frame scope that can reach each node:
- a field present in every reachable scope is
available; - a field present in only some scopes is
conditionalwith a concise reason; - malformed route targets and absent loop routes add bounded warnings; and
- no traversal result may grant a scoped alias that runtime cannot provide.
Add a docstring explaining why this is an abstract execution-frame analysis rather than ordinary graph reachability.
- Step 5: Feed context options into Task 1 inventory
Project canonical paths as context.<key>. Derive foreach item schema from the over source when the source schema is declared and array-valued; otherwise use {}. Runtime context options are offered only for step_input. Final workflow projection currently executes without frame context, so the inventory must not advertise context.* for workflow_output.
- Step 6: Verify and commit
uv run pytest tests/core/test_context_scopes.py tests/core/test_scheduler.py tests/wf_api/test_authoring_contracts.py -q
uv run basedpyright --level error src/wf_core/context_contracts.py src/wf_core/analysis src/wf_core/runtime/ops/frames.py
Commit:
git add src/wf_core/context_contracts.py src/wf_core/analysis src/wf_core/runtime/ops/frames.py src/wf_api/authoring_contracts.py tests/core/test_context_scopes.py tests/core/test_scheduler.py tests/wf_api/test_authoring_contracts.py
git commit -m "feat: inspect node runtime context"
Task 3: Expose Authoring Contract Inspection Through Workflow API And JSON-RPC
Files:
- Modify:
src/wf_api/surface.py - Modify:
src/wf_api/service.py - Modify:
src/wf_api/authoring_contracts.py - Modify:
src/wf_transport_rpc_http/models.py - Modify:
src/wf_transport_rpc_http/methods/drafts.py - Modify:
src/wf_transport_rpc_http/client/drafts.py - Modify:
tests/wf_api/test_drafts_service.py - Modify:
tests/wf_transport_rpc_http/test_app.py - Modify:
tests/wf_transport_rpc_http/test_client.py - Modify:
tests/wf_transport_rpc_http/test_openrpc_contract.py
Interfaces:
-
Produces:
WorkflowDraftSurface.inspect_draft_authoring_contract(*, workspace_id: str, revision: int, selected_step_id: str | None = None) -> AuthoringContractInventoryPayload, implemented byWorkflowApi. -
Produces: JSON-RPC operation
workflow.draft_workspaces.inspect_authoring_contractwith the same fields. -
Consumes: Tasks 1-2 pure inventory modules.
-
Step 1: Write failing service tests
Create a persisted draft with input/state/output schemas and a selected capability step. Assert that inspection:
-
returns the requested workspace id and exact revision;
-
resolves the selected capability input/output schemas and outcomes;
-
lists executable entry candidates but not projection ids or
__end__; -
returns context options from Task 2;
-
rejects an unknown selected step;
-
returns the standard workflow choices plus warnings for a persisted invalid draft whose selected step cannot be interpreted; and
-
returns a normal conflict result/error for a stale revision without mutating the workspace.
-
Step 2: Run service tests and verify RED
uv run pytest tests/wf_api/test_drafts_service.py -q -k authoring_contract
Expected: WorkflowDraftSurface has no inspection method.
- Step 3: Implement the service method
Load the persisted workspace with its canonical draft, verify the supplied revision, and project tolerant workflow-level schemas before selected-step details. Resolve a capability contract only when the selected keyed step is a valid capability use. Compile the workflow for context analysis when possible; on compile failure return no scoped context and add a warning rather than making the entire inventory unusable.
Do not mutate, validate-and-save, or increment the revision during inspection.
- Step 4: Add JSON-RPC params, dispatch, and client
Define:
class InspectDraftAuthoringContractParams(RpcParamsModel):
workspace_id: str = Field(min_length=1)
revision: int = Field(ge=1)
selected_step_id: str | None = Field(default=None, min_length=1)
Register workflow.draft_workspaces.inspect_authoring_contract in methods/drafts.py and add the matching remote client method. Keep errors in the existing JSON-RPC mapping: malformed request parameters map to -32602; an unknown selected step follows the existing workflow-domain error mapping; missing workspace uses the established not-found mapping; stale revision uses the existing conflict semantics.
- Step 5: Add RPC and OpenRPC tests
Assert exact snake_case payload/result fields, nullable selected_step_id, conditional reason, schema fragments, and no mutation after success or failure. Verify the OpenRPC document references the named payload types rather than an unconstrained object.
- Step 6: Verify and commit
uv run pytest tests/wf_api/test_drafts_service.py tests/wf_transport_rpc_http/test_app.py tests/wf_transport_rpc_http/test_client.py tests/wf_transport_rpc_http/test_openrpc_contract.py -q
uv run basedpyright --level error src/wf_api src/wf_transport_rpc_http
Commit:
git add src/wf_api src/wf_transport_rpc_http tests/wf_api/test_drafts_service.py tests/wf_transport_rpc_http
git commit -m "feat: expose authoring contract inventory"
Task 4: Generate And Author The Browser RPC Contract
Files:
- Modify:
tests/wf_contract_manifest/test_generate.py - Regenerate:
contracts/workflow-api.manifest.json - Modify:
web/packages/rpc/scripts/workflow-contract-generator.ts - Modify:
web/packages/rpc/scripts/workflow-contract-generator.test.ts - Regenerate:
web/packages/rpc/src/generated/workflow-contract.ts - Modify:
web/packages/rpc/src/rpcs.ts - Modify:
web/packages/rpc/src/method-registry.ts - Modify:
web/packages/rpc/src/service.ts - Modify:
web/packages/rpc/src/index.ts - Modify:
web/packages/rpc/src/json-schema/authored-rpc-fixtures.ts - Modify:
web/packages/rpc/src/json-schema/rpc-parity.test.ts - Modify:
web/packages/rpc/src/generated/workflow-contract.test.ts - Modify:
web/packages/rpc/src/method-registry.test.ts - Modify:
web/packages/rpc/src/service.test.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: authored Effect RPCs for
inspect_authoring_contract,set_contract,set_start, andset_workflow_output_bindings. -
Produces: console
OperationNameliterals for those four operations. -
Preserves: Hono/browser authorization as an explicit allowlist independent of generated operation coverage.
-
Step 1: Write failing generated-contract and allowlist tests
Assert all four methods are in the authored RPC cohort and generated inventory. Add positive browser authorization tests for those methods and a negative test proving adjacent generic patch and replace_document operations remain blocked.
- Step 2: Regenerate checked contracts
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 hand-edit generated artifacts.
- Step 3: Add authored Effect schemas and registry entries
Confirm .repos/effect is present, consult the local Effect Schema guide, and
follow the package's existing authored-RPC patterns. Use
runtimeSchemasFor(...) for payload and success schemas. Register inspection
as idempotency "read"; register the three focused mutations as "write". Add
exhaustive service switch cases and export the RPCs through the package index.
Author representative parity fixtures containing one input source, one state target, one conditional context source with reason, selected-step input/output choices, and entry/outcome lists.
- Step 4: Extend the console and proxy allowlists
Add exactly the four operation literals. Do not authorize source-admin, artifact deletion, generic patch, complete document replacement, or run mutations as a side effect.
- Step 5: Verify and commit
uv run pytest tests/wf_contract_manifest/test_generate.py tests/wf_contract_manifest/test_committed_manifest.py -q
pnpm --dir web --filter @lda/workflow-rpc test
pnpm --dir web --filter @lda/workflow-rpc typecheck
pnpm --dir web --filter @lda/console test -- src/connection
pnpm --dir web --filter @lda/server test -- src/browser-operation-policy.test.ts src/app.test.ts
Commit:
git add tests/wf_contract_manifest contracts/workflow-api.manifest.json web/packages/rpc web/apps/console/src/connection web/apps/server/src/browser-operation-policy.ts web/apps/server/src/browser-operation-policy.test.ts web/apps/server/src/app.test.ts
git commit -m "feat: authorize workflow contract authoring"
Task 5: Decode Inventory And Build The Reusable Path Picker
Files:
- Create:
web/apps/console/src/workspace/domain/authoring-contract-models.ts - Create:
web/apps/console/src/workspace/domain/authoring-contract-models.test.ts - Create:
web/apps/console/src/workspace/domain/authoring-contract-client.ts - Create:
web/apps/console/src/workspace/domain/authoring-contract-client.test.ts - Create:
web/apps/console/src/workspace/authoring/useAuthoringContract.ts - Create:
web/apps/console/src/workspace/authoring/useAuthoringContract.test.tsx - Create:
web/apps/console/src/workspace/authoring/AuthoringPathPicker.tsx - Create:
web/apps/console/src/workspace/authoring/AuthoringPathPicker.test.tsx - Modify:
web/apps/console/src/styles/global.css
Interfaces:
-
Produces:
AuthoringPathOption,AuthoringStepContract, andAuthoringContractInventorycamelCase browser types. -
Produces:
createAuthoringContractClient(executor).inspect(input) -> Promise<AuthoringContractInventory>. -
Produces:
useAuthoringContract({ workspaceId, revision, selectedStepId })returning{ phase, inventory, message, refresh }. -
Produces:
<AuthoringPathPicker options uses value onChange label allowCustom />. -
Step 1: Write failing decoder and client tests
Decode a complete snake_case wire result into camelCase fields. Reject unknown origins, availability values, uses, malformed schemas, and a response whose workspace/revision does not match the request. Assert the client sends the exact inspection params through the read executor.
- Step 2: Implement boundary decoding and the read adapter
Use Valibot at the external boundary. Preserve JSON Schema fragments as decoded JSON objects; do not use casts. Filter picker entries by their declared uses instead of their origin names.
- Step 3: Write failing hook lifecycle tests
Cover disconnected, loading, ready, stale response after selection change, failed inspection retaining the last matching inventory, revision change, and manual refresh. Use stable executor mocks and abort/ignore stale requests following existing workspace hooks.
- Step 4: Implement the hook
Key requests by connected target, workspace id, revision, and selected step id. Selecting a contract projection passes null; selecting an executable node passes its id. Never copy context names into the hook.
- Step 5: Write failing picker interaction tests
Assert:
-
options group as Workflow input, State, Step output, and Runtime context;
-
searching matches label, path, and description;
-
canonical path is secondary text;
-
conditional entries show their reason;
-
Runtime context is absent when no context options exist;
-
disabled incompatible entries cannot be selected;
-
nested choices remain keyboard reachable; and
-
Advanced reveals a custom path text control without replacing normal options.
-
Step 6: Implement the picker
Use native labelled controls and the existing console visual language. Keep the options panel internally scrollable and mobile-safe. The picker emits only a canonical string; its caller owns row state and validation.
- Step 7: Verify and commit
pnpm --dir web --filter @lda/console test -- src/workspace/domain/authoring-contract-models.test.ts src/workspace/domain/authoring-contract-client.test.ts src/workspace/authoring/useAuthoringContract.test.tsx src/workspace/authoring/AuthoringPathPicker.test.tsx
pnpm --dir web --filter @lda/console typecheck
Commit:
git add web/apps/console/src/workspace/domain/authoring-contract-* web/apps/console/src/workspace/authoring/useAuthoringContract* web/apps/console/src/workspace/authoring/AuthoringPathPicker* web/apps/console/src/styles/global.css
git commit -m "feat: browse authoring contract paths"
Task 6: Replace Local Suggestion Lists In Existing Binding Editors
Files:
- Modify:
web/apps/console/src/workspace/authoring/StepInputBindingsForm.tsx - Modify:
web/apps/console/src/workspace/authoring/StepInputBindingsForm.test.tsx - Modify:
web/apps/console/src/workspace/authoring/InputExpressionControl.tsx - Modify:
web/apps/console/src/workspace/authoring/InputExpressionControl.test.tsx - Modify:
web/apps/console/src/workspace/authoring/StepOutputBindingsForm.tsx - Modify:
web/apps/console/src/workspace/authoring/StepOutputBindingsForm.test.tsx - Modify:
web/apps/console/src/workspace/authoring/SelectedCapabilityInspector.tsx - Modify:
web/apps/console/src/workspace/authoring/SelectedCapabilityInspector.test.tsx - Modify:
web/apps/console/src/workspace/authoring/selected-step-dataflow.ts - Modify:
web/apps/console/src/workspace/authoring/selected-step-dataflow.test.ts
Interfaces:
-
Consumes:
AuthoringContractInventoryandAuthoringPathPickerfrom Task 5. -
Preserves: canonical
StepInputBindingandOutputBindingsubmissions. -
Removes as source of truth:
workflowSourceSuggestions; schema-local output paths may remain a pure fallback only for unsupported/repair state. -
Step 1: Write failing integration tests for inventory-backed choices
Render a selected step with inventory options. Assert that input path rows and every recursive path expression use the same picker, context appears only when supplied, and output rows use inventory step-output sources plus state targets. Confirm a selected canonical path serializes unchanged.
- Step 2: Verify RED
pnpm --dir web --filter @lda/console test -- src/workspace/authoring/StepInputBindingsForm.test.tsx src/workspace/authoring/InputExpressionControl.test.tsx src/workspace/authoring/StepOutputBindingsForm.test.tsx src/workspace/authoring/SelectedCapabilityInspector.test.tsx
Expected: forms still render datalists or local schema suggestions.
- Step 3: Thread inventory options through the selected-step inspector
Pass filtered options rather than schemas into normal picker paths:
- step inputs consume options with
step_input; - expression path leaves consume the same options;
- step outputs consume
step_output_sourcefor source andstate_targetfor destination.
Existing malformed or custom persisted paths initialize Advanced mode and remain repairable. Do not silently replace them with the first catalog value.
- Step 4: Retire duplicate frontend path discovery
Remove workflowSourceSuggestions once all production callers use inventory. Keep schema navigation helpers still needed for local capability targets, output preview, expression schema validation, and unsupported-record repair. Add a test proving no production authoring file contains hardcoded context.loop_item or similar runtime keys.
- Step 5: Verify and commit
pnpm --dir web --filter @lda/console test -- src/workspace/authoring
pnpm --dir web --filter @lda/console typecheck
Commit:
git add web/apps/console/src/workspace/authoring
git commit -m "refactor: use canonical authoring choices"
Task 7: Project Workflow Contracts Into The Graph
Files:
- Modify:
web/apps/console/src/graph/graph-model.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/authoring-graph.ts - Modify:
web/apps/console/src/workspace/authoring/authoring-graph.test.ts - Modify:
web/apps/console/src/workspace/authoring/AuthoringGraph.tsx - Modify:
web/apps/console/src/workspace/authoring/AuthoringGraph.test.tsx - Modify:
web/apps/console/src/workspace/authoring/DraftWorkbench.tsx - Modify:
web/apps/console/src/workspace/authoring/DraftWorkbench.test.tsx - Modify:
web/apps/console/src/styles/global.css
Interfaces:
-
Extends:
WorkbenchSelectionwith{ kind: "contract"; contract: "input" | "state" | "output" | "outcomes" }. -
Extends:
WorkflowGraphNodeKindwith contract-specific visual kinds or onecontractkind plus explicit contract metadata. -
Produces: stable ids
contract:input,contract:state,contract:output, andcontract:outcomes. -
Step 1: Write failing pure graph projection tests
Assert every canonical draft produces exactly four stable contract nodes; their summaries reflect schema/outcome counts; none appear in persisted draft steps; insertion order does not alter ids or positions; entry and binding connectors are derived from canonical start/input/output data; and malformed bindings omit only the affected connector.
- Step 2: Implement graph projection records
Build contract records before calling the Dagre-backed graph model. Use a left-to-right authoring layout with Input near entry steps, State adjacent to executable dataflow, Output near terminal flow, and Outcomes separated from executable End nodes. Derived connectors use distinct non-route styling and cannot be selected as route outcomes.
- Step 3: Add contract node rendering and selection
Render readable contract labels and concise field counts. Do not reuse execution status colors or imply the projections run. Contract nodes are selectable by mouse, Enter, and Space; React Flow panning and zoom remain intact.
- Step 4: Preserve responsive selection state
Selecting a contract opens the existing inspector sheet on mobile and remains selected after close/reopen. Canvas selection clears it. Contract selection must not create insertion context or expose capability-only deferred actions.
- Step 5: Verify and commit
pnpm --dir web --filter @lda/console test -- src/graph src/workspace/authoring/authoring-graph.test.ts src/workspace/authoring/AuthoringGraph.test.tsx src/workspace/authoring/DraftWorkbench.test.tsx
pnpm --dir web --filter @lda/console typecheck
Commit:
git add web/apps/console/src/graph web/apps/console/src/workspace/authoring/authoring-graph* web/apps/console/src/workspace/authoring/AuthoringGraph* web/apps/console/src/workspace/authoring/DraftWorkbench* web/apps/console/src/styles/global.css
git commit -m "feat: project workflow contracts in graph"
Task 8: Add Focused Workflow Contract Editors
Files:
- Create:
web/apps/console/src/workspace/authoring/workflow-contract-editor.ts - Create:
web/apps/console/src/workspace/authoring/workflow-contract-editor.test.ts - Create:
web/apps/console/src/workspace/authoring/WorkflowSchemaFieldsForm.tsx - Create:
web/apps/console/src/workspace/authoring/WorkflowSchemaFieldsForm.test.tsx - Create:
web/apps/console/src/workspace/authoring/WorkflowContractInspector.tsx - Create:
web/apps/console/src/workspace/authoring/WorkflowContractInspector.test.tsx - Modify:
web/apps/console/src/workspace/domain/draft-workspace-models.ts - Modify:
web/apps/console/src/workspace/domain/draft-authoring-client.ts - Modify:
web/apps/console/src/workspace/domain/draft-authoring-client.test.ts - Modify:
web/apps/console/src/workspace/authoring/useDraftAuthoring.ts - Modify:
web/apps/console/src/workspace/authoring/useDraftAuthoring.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/styles/global.css
Interfaces:
-
Produces:
WorkflowContractPatch,WorkflowOutputBindingRow, and lossless schema field projection helpers. -
Extends:
DraftAuthoringClientwithsetContract,setStart, andsetWorkflowOutputBindings. -
Extends:
DraftAuthoringControllerwith the same focused mutations and conflict-reapply submissions. -
Consumes: Task 5 inventory and Task 7 contract selection.
-
Step 1: Write failing pure schema-editor tests
Project ordinary object schemas into ordered rows with name, type, required, description, nested object fields, array item schema, and state-only default/reducer metadata. Assert round-trip preservation of untouched $defs, root metadata, and unsupported field fragments.
Unsupported composition such as oneOf remains visible as a bounded read-only fragment. Saving is blocked until the author leaves it untouched, explicitly removes it, or replaces it with a supported field model; never discard it during another field edit.
- Step 2: Build the recursive workflow schema form
Support string, integer, number, boolean, object, array, and unconstrained value fields. Object fields recursively edit children; array fields edit one item schema. Keep recursion bounded by the existing schema normalization limit and show a concise unsupported message past that boundary.
State rows expose default and reducer controls only where the canonical state schema accepts them. Input and Output rows do not render state-only fields.
- Step 3: Add client methods and exact payload tests
Define domain inputs:
type SetWorkflowContractInput = {
readonly workspaceId: string;
readonly revision: number;
readonly inputSchema?: JsonObject;
readonly stateSchema?: JsonObject;
readonly outputSchema?: JsonObject;
readonly outcomes?: ReadonlyArray<string>;
};
type SetWorkflowStartInput = {
readonly workspaceId: string;
readonly revision: number;
readonly stepId: string;
};
type SetWorkflowOutputBindingsInput = {
readonly workspaceId: string;
readonly revision: number;
readonly bindings: ReadonlyArray<InputBinding>;
};
Assert exact operation names and snake_case payloads. Deep-copy schemas and bindings without JSON stringify round trips.
- Step 4: Add controller mutations and conflict reapply
Extend LastSubmission with contract, start, and workflow_outputs. Reapply the exact prior payload against the refreshed revision. Deduplicate identical pending submissions and preserve contract selection/dirty state on conflicts using the existing mutation runner.
If one user gesture needs both output schema and output bindings, submit them as two explicit operations in order. Show confirmed partial success if the second fails; do not present the pair as atomic.
-
Step 5: Build focused contract inspectors
-
Input: schema fields plus entry-step picker from
inventory.entrySteps. -
State: state fields with defaults/reducers and affected-binding summaries before removal.
-
Output: schema fields plus ordered final output bindings using
AuthoringPathPickerfor workflow input/state sources and inventory output targets. Exclude runtime context because final projection has no execution frame; preserve an existingcontext.*source only as an unsupported repair value. -
Outcomes: ordered non-blank unique workflow outcomes with add/remove/reorder controls.
All forms show unsaved, saving, invalid-confirmed, conflict, and failed states truthfully. Raw schema appears only in an Advanced details region for unsupported repair.
- Step 6: Route contract selection through ContextInspector
Render WorkflowContractInspector only for selection.kind === "contract". Hide capability-only deferred actions. Keep shared diagnostics and bounded raw draft available below the focused form.
- Step 7: Verify and commit
pnpm --dir web --filter @lda/console test -- src/workspace/domain/draft-authoring-client.test.ts src/workspace/authoring/workflow-contract-editor.test.ts src/workspace/authoring/WorkflowSchemaFieldsForm.test.tsx src/workspace/authoring/WorkflowContractInspector.test.tsx src/workspace/authoring/useDraftAuthoring.test.tsx src/workspace/authoring/ContextInspector.test.tsx
pnpm --dir web --filter @lda/console typecheck
Commit:
git add web/apps/console/src/workspace/domain web/apps/console/src/workspace/authoring web/apps/console/src/styles/global.css
git commit -m "feat: edit workflow contracts in console"
Task 9: Verify The Complete Slice And Update Live Documentation
Files:
- Modify:
web/apps/console/src/workspace/routes/DraftDetailRoute.authoring-sync.test.tsx - Modify:
web/apps/console/src/workspace/authoring/DraftWorkbench.test.tsx - Modify:
web/README.md - Modify:
docs/project_map.md - Modify:
docs/current_roadmap.md - Move after completion:
docs/superpowers/plans/2026-08-14-workflow-console-contract-graph.mdtodocs/historical/superpowers/plans/2026-08-14-workflow-console-contract-graph.md
Interfaces:
-
Consumes: all prior tasks.
-
Produces: route-level proof that a real inventory read and focused contract writes update one canonical draft without browser-only state divergence.
-
Step 1: Add route-level integration tests
Mock the actual browser operation sequence and assert:
- opening a draft reads canonical workspace and inventory;
- selecting Input opens its schema/start form;
- selecting a capability node refreshes inventory with
selected_step_id; - foreach context appears only for a proven loop-body node;
- selecting and saving one input binding sends the catalog path unchanged;
- editing final output sends ordered canonical bindings;
- an inventory failure retains confirmed draft values and exposes Advanced repair;
- a stale write preserves the selected contract and local form; and
- closing/reopening the mobile inspector preserves unsaved rows.
- Step 2: Run focused Python and web suites
uv run pytest tests/core/test_context_scopes.py tests/core/test_scheduler.py tests/wf_api/test_authoring_contracts.py tests/wf_api/test_drafts_service.py tests/wf_transport_rpc_http/test_app.py tests/wf_transport_rpc_http/test_client.py tests/wf_transport_rpc_http/test_openrpc_contract.py tests/wf_contract_manifest/test_generate.py tests/wf_contract_manifest/test_committed_manifest.py -q
pnpm --dir web --filter @lda/workflow-rpc test
pnpm --dir web --filter @lda/console test -- src/workspace
pnpm --dir web typecheck
pnpm --dir web build
- Step 3: Run a real-server smoke test
Against the existing example config, start the JSON-RPC server if one is not already available:
uv run wf-rpc-server --config examples/lda_report_workflow/wf.config.json --host 127.0.0.1 --port 8765
Use the console to create or open a draft, inspect the four contract projections, select a normal source without reading raw JSON, save one focused contract edit, reload, and confirm the returned revision and graph projection match. Also inspect a foreach-body node if the fixture contains one; otherwise exercise context-scope behavior through the focused automated tests rather than inventing demo data.
- Step 4: Run React and interface diagnostics
Run the React diagnostic against changed console files:
pnpm dlx react-doctor@latest --verbose --scope changed
Then run:
pnpm --dir web --filter @lda/console typecheck
pnpm --dir web --filter @lda/console build
git diff --check
Fix actionable accessibility, stale-effect, or component-interface findings in touched files. Do not broaden into unrelated presentation UI.
- Step 5: Update live docs and archive the plan
Document the authoring inventory, four contract projections, normal picker workflow, Advanced custom-path fallback, and current typed-step exclusions in web/README.md and docs/project_map.md. Mark Slice 6 completed in the roadmap and link the historical plan path. Move the plan with PowerShell Move-Item, then stage the active and historical plan directories with git add -A so both tracked and untracked source states are handled.
- Step 6: Run final verification and review
uv run ruff check
uv run basedpyright --level error
pnpm --dir web test
pnpm --dir web typecheck
pnpm --dir web build
git diff --check
Run an independent two-axis review against the approved design. Fix Critical and Important findings, and record any deliberate Minor deferrals with concrete rationale.
- Step 7: Commit the completed slice
git add -A -- web/README.md docs/project_map.md docs/current_roadmap.md docs/superpowers/plans docs/historical/superpowers/plans web/apps/console/src/workspace/routes/DraftDetailRoute.authoring-sync.test.tsx web/apps/console/src/workspace/authoring/DraftWorkbench.test.tsx
git commit -m "docs: complete workflow contract graph"
Final Acceptance Checklist
- The API returns one revision-scoped inventory for workflow and selected-step authoring choices.
- Input/state fields and step-applicable context fields are selectable without raw JSON inspection.
- Final output choices exclude runtime context until final projection has defined context semantics.
- Runtime context disappears entirely when no applicable options exist.
- Conditional foreach paths are labelled honestly and never presented as guaranteed.
- Existing input, expression, and output editors use the same inventory-backed picker.
- Input, State, Output, and Outcomes are selectable graph projections but absent from stored steps.
- Start, contract, outcomes, and final output writes use focused canonical operations.
- Unsupported persisted schemas and custom paths remain visible and repairable without data loss.
- Desktop and mobile preserve selection and unsaved edits through inventory refreshes and conflicts.
- Generated contracts, Effect RPCs, browser authorization, Python tests, web tests, typechecks, and builds pass.