Files
lda-wf/docs/superpowers/plans/2026-08-14-workflow-console-contract-graph.md
T

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, and AuthoringContractInventoryPayload.

  • 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 existing frame_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 available or conditional availability.

  • 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_item and loop_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 conditional with 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 by WorkflowApi.

  • Produces: JSON-RPC operation workflow.draft_workspaces.inspect_authoring_contract with 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, and set_workflow_output_bindings.

  • Produces: console OperationName literals 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, and AuthoringContractInventory camelCase 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: AuthoringContractInventory and AuthoringPathPicker from Task 5.

  • Preserves: canonical StepInputBinding and OutputBinding submissions.

  • 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_source for source and state_target for 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: WorkbenchSelection with { kind: "contract"; contract: "input" | "state" | "output" | "outcomes" }.

  • Extends: WorkflowGraphNodeKind with contract-specific visual kinds or one contract kind plus explicit contract metadata.

  • Produces: stable ids contract:input, contract:state, contract:output, and contract: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: DraftAuthoringClient with setContract, setStart, and setWorkflowOutputBindings.

  • Extends: DraftAuthoringController with 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 AuthoringPathPicker for workflow input/state sources and inventory output targets. Exclude runtime context because final projection has no execution frame; preserve an existing context.* 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.md to docs/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:

  1. opening a draft reads canonical workspace and inventory;
  2. selecting Input opens its schema/start form;
  3. selecting a capability node refreshes inventory with selected_step_id;
  4. foreach context appears only for a proven loop-body node;
  5. selecting and saving one input binding sends the catalog path unchanged;
  6. editing final output sends ordered canonical bindings;
  7. an inventory failure retains confirmed draft values and exposes Advanced repair;
  8. a stale write preserves the selected contract and local form; and
  9. 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.