docs: design workflow console foundation
This commit is contained in:
@@ -42,13 +42,15 @@ Implementation order:
|
||||
2. Completed: deterministic `examples/lda_report_workflow/` case study with
|
||||
local document, report, issue-board sources, and typed issue-review
|
||||
interrupt.
|
||||
3. Add a top-level `web/` Astro/Effect app with loopback JSON-RPC connection and
|
||||
method registry.
|
||||
3. Add a top-level `web/` pnpm workspace with a React/Vite console, Hono local
|
||||
server, Effect JSON-RPC boundary, loopback connection flow, and method
|
||||
registry. Design:
|
||||
[`workflow console foundation`](superpowers/specs/2026-07-01-workflow-console-foundation-design.md).
|
||||
4. Add console read/inspect views for sources, drafts, artifacts, deployments,
|
||||
runs, traces, and raw RPC drawers.
|
||||
5. Add lifecycle autoplay, typed approval, issue-board output, and replay.
|
||||
6. Add a constrained demo agent that invokes one prepared recipe macro.
|
||||
7. Add presentation and appendix routes for the 15-minute defense.
|
||||
7. Add an Astro presentation app and appendix routes for the 15-minute defense.
|
||||
|
||||
Boundaries: this is not a production admin panel, generic visual workflow
|
||||
editor, scheduler, external Google Drive/mail integration, or benchmark evidence
|
||||
|
||||
@@ -10,11 +10,12 @@ Related:
|
||||
- [Workflow API architecture](../../wf_api_architecture.md)
|
||||
- [Persisted run/resume contract](2026-06-03-persisted-run-resume-contract.md)
|
||||
- [Self-describing interrupt contracts](2026-07-01-self-describing-interrupt-contracts.md)
|
||||
- [Workflow console foundation design](2026-07-01-workflow-console-foundation-design.md)
|
||||
- [Thesis system design](../../thesis/system-design-implementation.md)
|
||||
|
||||
## Purpose
|
||||
|
||||
Build one local-first web application that serves three related needs:
|
||||
Build one local-first web workspace that serves three related needs:
|
||||
|
||||
1. a reusable Workflow Console for inspecting a running `wf` JSON-RPC server;
|
||||
2. a reliable defense demonstration of the complete workflow lifecycle;
|
||||
@@ -38,22 +39,14 @@ Create a top-level `web/` package rather than putting product code under
|
||||
```text
|
||||
web/
|
||||
package.json
|
||||
astro.config.mjs
|
||||
src/
|
||||
pages/
|
||||
connect.astro
|
||||
console/
|
||||
demo/
|
||||
replay/
|
||||
presentation/
|
||||
appendix/
|
||||
components/
|
||||
agent/
|
||||
lifecycle/
|
||||
rpc/
|
||||
trace/
|
||||
workflow-graph/
|
||||
presentation/
|
||||
pnpm-workspace.yaml
|
||||
apps/
|
||||
console/ # React + Vite
|
||||
server/ # Hono + Effect boundary
|
||||
presentation/ # Astro, added in the presentation slice
|
||||
packages/
|
||||
rpc/
|
||||
ui/ # added only after shared components exist
|
||||
```
|
||||
|
||||
The first release is a local-development Workflow Console. It is not a
|
||||
@@ -64,8 +57,8 @@ multi-user administration, or safe access to arbitrary remote servers.
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
Browser[Astro and React UI]
|
||||
Routes[Astro server routes]
|
||||
Browser[React + Vite UI]
|
||||
Routes[Hono server routes]
|
||||
Agent[DemoAgent service]
|
||||
Recipe[Prepared ReportRecipe]
|
||||
Job[LifecycleJob service]
|
||||
@@ -108,7 +101,7 @@ Use Effect for server-side orchestration and protocol handling:
|
||||
- timeouts and retries apply only where semantically safe.
|
||||
|
||||
Do not spread Effect through every React component. React components consume
|
||||
ordinary view models and event streams. Astro route handlers are the
|
||||
ordinary view models and event streams. Hono route handlers are the
|
||||
`Effect.runPromise` boundary.
|
||||
|
||||
Mutation calls such as artifact creation, deployment saving, run start, and
|
||||
@@ -120,11 +113,11 @@ small bounded retry policy.
|
||||
The connection page accepts a JSON-RPC URL such as
|
||||
`http://127.0.0.1:8765/rpc`.
|
||||
|
||||
1. The Astro server validates the URL.
|
||||
1. The Hono server validates the URL.
|
||||
2. The first slice accepts only loopback hosts.
|
||||
3. `workflow.health` verifies the endpoint.
|
||||
4. The connection is retained for the browser session.
|
||||
5. Astro proxies JSON-RPC calls server-side to avoid browser CORS coupling.
|
||||
5. Hono proxies JSON-RPC calls server-side to avoid browser CORS coupling.
|
||||
6. Every call records its raw request, raw response, interpreted result,
|
||||
duration, and equivalent CLI command.
|
||||
|
||||
@@ -188,8 +181,8 @@ The UI uses focus modes instead of showing every panel simultaneously:
|
||||
- **Output:** rendered readiness report and created issues.
|
||||
- **Raw:** collapsible JSON-RPC request and response drawer.
|
||||
|
||||
Use an interactive graph component, expected to be `@xyflow/react`, inside an
|
||||
Astro React island. Static presentation diagrams remain Mermaid.
|
||||
Use an interactive graph component, expected to be `@xyflow/react`, inside the
|
||||
React console. Static presentation diagrams remain Mermaid.
|
||||
|
||||
A small graph embedded in a lifecycle card can expand into the primary canvas.
|
||||
Selecting a node opens a side drawer with capability, source, bindings,
|
||||
@@ -383,19 +376,21 @@ Routes:
|
||||
- `/replay`: recorded fallback;
|
||||
- `/appendix`: backup architecture, evaluation, and implementation slides.
|
||||
|
||||
The presentation transitions directly into the demo and back. The exact slide
|
||||
library is selected in the presentation slice; the route and shared component
|
||||
boundaries are fixed by this design.
|
||||
The presentation transitions directly into the demo and back. Astro may be
|
||||
added as a separate static presentation app and served by the Hono process. The
|
||||
exact slide library is selected in the presentation slice; the route and shared
|
||||
component boundaries are fixed by this design.
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. Self-describing interrupt request/resume contracts.
|
||||
2. Deterministic `lda.chat` report workflow and Python sources.
|
||||
3. `web/` Astro and Effect foundation, connection flow, and RPC registry.
|
||||
3. `web/` React/Vite, Hono, and Effect foundation, connection flow, and RPC
|
||||
registry.
|
||||
4. Workflow Console read/inspect views, graph, trace, and raw drawers.
|
||||
5. Lifecycle job, autoplay, typed approval, issue board, and replay.
|
||||
6. Constrained demo agent and replaceable model gateway.
|
||||
7. Defense presentation and appendix routes.
|
||||
7. Astro defense presentation and appendix routes.
|
||||
|
||||
Each slice gets its own executable implementation plan. Do not combine the
|
||||
Python contract change, web foundation, agent integration, and presentation
|
||||
|
||||
@@ -0,0 +1,429 @@
|
||||
# Workflow Console Foundation Design
|
||||
|
||||
Date: 2026-07-01
|
||||
|
||||
Status: Approved design for the first web implementation slice.
|
||||
|
||||
Related:
|
||||
|
||||
- [Workflow console, agent demo, and defense presentation](2026-07-01-workflow-console-agent-demo.md)
|
||||
- [Self-describing interrupt contracts](2026-07-01-self-describing-interrupt-contracts.md)
|
||||
- [Workflow API architecture](../../wf_api_architecture.md)
|
||||
- [Current roadmap](../../current_roadmap.md)
|
||||
|
||||
## Goal
|
||||
|
||||
Create the first executable `web/` slice for lda.chat: a local React console
|
||||
that connects to a loopback `wf-rpc-server` through a small Node proxy, proves
|
||||
typed JSON-RPC decoding with Effect, and presents both interpreted connection
|
||||
state and raw protocol evidence.
|
||||
|
||||
This slice establishes durable package and protocol boundaries. It does not
|
||||
build lifecycle browsing, workflow graphs, autoplay, replay, the demo agent, or
|
||||
the defense presentation.
|
||||
|
||||
## Architecture Decision
|
||||
|
||||
Use an app-first React architecture rather than Astro as the primary console
|
||||
shell:
|
||||
|
||||
- Vite builds and serves the React development application.
|
||||
- Hono provides the local Node HTTP server and production static-file host.
|
||||
- Effect owns URL validation, JSON-RPC schemas, typed errors, and upstream RPC
|
||||
execution.
|
||||
- React consumes plain view models and never runs Effect programs directly.
|
||||
- Astro remains available for a later static presentation application and is
|
||||
not a dependency of the console.
|
||||
|
||||
The Hono server is not a second workflow API. It is a narrow browser-facing
|
||||
adapter over the existing public JSON-RPC endpoint.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Browser[React + Vite console]
|
||||
Hono[Hono local server]
|
||||
Policy[Loopback target policy]
|
||||
Effect[Effect WorkflowRpc service]
|
||||
Rpc[wf-rpc-server]
|
||||
|
||||
Browser -->|POST /api/connect| Hono
|
||||
Browser -->|POST /api/rpc| Hono
|
||||
Hono --> Policy
|
||||
Hono --> Effect
|
||||
Effect -->|JSON-RPC HTTP| Rpc
|
||||
```
|
||||
|
||||
## Workspace Layout
|
||||
|
||||
Create a pnpm workspace under the existing repository root:
|
||||
|
||||
```text
|
||||
web/
|
||||
package.json
|
||||
pnpm-lock.yaml
|
||||
pnpm-workspace.yaml
|
||||
tsconfig.base.json
|
||||
apps/
|
||||
console/
|
||||
package.json
|
||||
index.html
|
||||
vite.config.ts
|
||||
src/
|
||||
app/
|
||||
components/
|
||||
connection/
|
||||
rpc/
|
||||
styles/
|
||||
main.tsx
|
||||
server/
|
||||
package.json
|
||||
src/
|
||||
app.ts
|
||||
index.ts
|
||||
static.ts
|
||||
packages/
|
||||
rpc/
|
||||
package.json
|
||||
src/
|
||||
errors.ts
|
||||
method-registry.ts
|
||||
protocol.ts
|
||||
service.ts
|
||||
target-policy.ts
|
||||
index.ts
|
||||
```
|
||||
|
||||
The first slice creates only `console`, `server`, and `rpc`. Later slices may
|
||||
add:
|
||||
|
||||
```text
|
||||
web/apps/presentation/ # Astro presentation and appendix routes
|
||||
web/packages/ui/ # shared components after real reuse exists
|
||||
```
|
||||
|
||||
Do not create empty placeholder packages for future work.
|
||||
|
||||
The root package pins `[email protected]`. The supported runtime is Node.js 22 or
|
||||
newer; the current development environment uses Node.js 26.
|
||||
|
||||
## Process Model
|
||||
|
||||
Development uses two processes started through one root command:
|
||||
|
||||
1. Vite serves the React application with hot reload.
|
||||
2. Hono serves `/api/*` and proxies validated JSON-RPC calls.
|
||||
|
||||
Vite forwards `/api/*` to Hono during development. The browser therefore uses
|
||||
one apparent origin and does not depend on Python CORS configuration.
|
||||
|
||||
Production uses one Hono process:
|
||||
|
||||
1. Vite builds static assets.
|
||||
2. Hono serves the Vite output.
|
||||
3. Hono retains the same `/api/*` routes.
|
||||
4. Unknown non-API paths fall back to `index.html` for client routing.
|
||||
|
||||
The first slice does not start or supervise `wf-rpc-server`. Operators run it
|
||||
separately and paste its loopback JSON-RPC URL into the console.
|
||||
|
||||
## Connection Contract
|
||||
|
||||
The connection form accepts a URL such as:
|
||||
|
||||
```text
|
||||
http://127.0.0.1:8765/rpc
|
||||
```
|
||||
|
||||
Accepted targets must satisfy all rules:
|
||||
|
||||
- scheme is `http`;
|
||||
- hostname is exactly `127.0.0.1`, `localhost`, or `[::1]`/`::1` after URL
|
||||
parsing;
|
||||
- username and password are absent;
|
||||
- port is explicit and in the valid TCP port range;
|
||||
- URL has no query string or fragment;
|
||||
- redirects are not followed by the upstream fetch;
|
||||
- the normalized URL is validated again for every proxy request.
|
||||
|
||||
`POST /api/connect` accepts:
|
||||
|
||||
```json
|
||||
{
|
||||
"target": "http://127.0.0.1:8765/rpc"
|
||||
}
|
||||
```
|
||||
|
||||
It sends `workflow.health` with an empty params object and returns a view model:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"connection": {
|
||||
"status": "connected",
|
||||
"target": "http://127.0.0.1:8765/rpc",
|
||||
"serverStatus": "ok",
|
||||
"durationMs": 12
|
||||
},
|
||||
"exchange": {
|
||||
"request": {},
|
||||
"response": {}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The exact raw JSON-RPC request and response are retained inside `exchange`.
|
||||
They are omitted from the compact connection card but available in the raw
|
||||
drawer.
|
||||
|
||||
The browser stores only the normalized target URL in `sessionStorage`. The
|
||||
server remains stateless: every `/api/rpc` request carries the target and is
|
||||
validated again. No cookies, server sessions, authentication tokens, or target
|
||||
registry are introduced in this slice.
|
||||
|
||||
## JSON-RPC Contract
|
||||
|
||||
The RPC package defines Effect schemas for:
|
||||
|
||||
- JSON-RPC request envelopes;
|
||||
- success response envelopes;
|
||||
- error response envelopes;
|
||||
- `workflow.health` results;
|
||||
- `workflow.sources.list` parameters and the selected result fields required by
|
||||
the first console screen.
|
||||
|
||||
The request id is generated by the server adapter and represented as a string.
|
||||
Responses must have `jsonrpc: "2.0"`, the matching id, and exactly one of
|
||||
`result` or `error`.
|
||||
|
||||
The first method registry contains two entries:
|
||||
|
||||
```text
|
||||
workflow.health
|
||||
workflow.sources.list
|
||||
```
|
||||
|
||||
Each registry entry includes:
|
||||
|
||||
- method name;
|
||||
- parameter schema;
|
||||
- result schema;
|
||||
- human label and explanation;
|
||||
- idempotency classification;
|
||||
- equivalent CLI formatter;
|
||||
- result-to-view-model interpreter.
|
||||
|
||||
Unknown methods are rejected by the Hono adapter. This is a mapped console
|
||||
surface, not an unrestricted arbitrary JSON-RPC relay.
|
||||
|
||||
## Server API
|
||||
|
||||
The first Hono application exposes:
|
||||
|
||||
- `GET /api/health`: confirms the web server itself is running;
|
||||
- `POST /api/connect`: validates a target and calls `workflow.health`;
|
||||
- `POST /api/rpc`: invokes one registered read-only JSON-RPC operation;
|
||||
- static Vite assets and SPA fallback in production.
|
||||
|
||||
`POST /api/rpc` accepts:
|
||||
|
||||
```json
|
||||
{
|
||||
"target": "http://127.0.0.1:8765/rpc",
|
||||
"operation": "workflow.sources.list",
|
||||
"params": {}
|
||||
}
|
||||
```
|
||||
|
||||
It returns:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": true,
|
||||
"operation": "workflow.sources.list",
|
||||
"label": "List sources",
|
||||
"interpreted": {},
|
||||
"exchange": {
|
||||
"request": {},
|
||||
"response": {}
|
||||
},
|
||||
"equivalentCli": "uv run wf source list",
|
||||
"durationMs": 8
|
||||
}
|
||||
```
|
||||
|
||||
The server accepts only registry operations. It does not accept a raw method
|
||||
string supplied directly by the browser.
|
||||
|
||||
Failures use one browser-facing shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"ok": false,
|
||||
"error": {
|
||||
"code": "upstream_unreachable",
|
||||
"message": "Could not connect to the workflow RPC server."
|
||||
},
|
||||
"exchange": {
|
||||
"request": {},
|
||||
"response": null
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
HTTP status mapping is fixed:
|
||||
|
||||
- `400`: invalid target, unknown operation, or invalid browser request;
|
||||
- `502`: upstream connection, JSON-RPC remote, protocol, or decode failure;
|
||||
- `504`: upstream timeout.
|
||||
|
||||
An upstream JSON-RPC error remains visible in the raw exchange but is mapped to
|
||||
a safe browser-facing code and message.
|
||||
|
||||
## Effect Boundary
|
||||
|
||||
Effect is confined to `web/packages/rpc` and the Hono handler boundary.
|
||||
|
||||
Services:
|
||||
|
||||
- `TargetPolicy`: parse and validate loopback RPC targets;
|
||||
- `WorkflowRpc`: execute one registered JSON-RPC call;
|
||||
- `MethodRegistry`: resolve operation metadata and schemas;
|
||||
- `Clock` through Effect for measured duration and deterministic tests.
|
||||
|
||||
Tagged errors:
|
||||
|
||||
- `InvalidTargetError`;
|
||||
- `UnknownOperationError`;
|
||||
- `UpstreamConnectionError`;
|
||||
- `UpstreamTimeoutError`;
|
||||
- `RpcProtocolError`;
|
||||
- `RpcRemoteError`;
|
||||
- `RpcDecodeError`.
|
||||
|
||||
Hono handlers call `Effect.runPromise` once per request and map tagged errors to
|
||||
stable HTTP responses. React receives JSON DTOs and ordinary discriminated
|
||||
unions, not Effect values, causes, layers, or services.
|
||||
|
||||
No retries are applied in this slice. Even health and list operations should
|
||||
report the first real failure so connection diagnostics remain honest.
|
||||
|
||||
## Console UI
|
||||
|
||||
The first page has three regions:
|
||||
|
||||
1. **Connection header**: target URL, connect/reconnect action, connection
|
||||
status, server status, and measured duration.
|
||||
2. **Source inventory**: a compact read-only list produced by
|
||||
`workflow.sources.list` after connection succeeds.
|
||||
3. **Protocol evidence**: a collapsible drawer showing the selected operation,
|
||||
equivalent CLI, raw request, raw response, and any typed error.
|
||||
|
||||
The UI must distinguish:
|
||||
|
||||
- not configured;
|
||||
- connecting;
|
||||
- connected;
|
||||
- invalid target;
|
||||
- unreachable server;
|
||||
- JSON-RPC remote error;
|
||||
- malformed response.
|
||||
|
||||
Connection failures keep the entered URL editable. Reconnect replaces the
|
||||
session target only after a successful health call. Reload restores the last
|
||||
successful target from `sessionStorage` but does not silently call it until the
|
||||
user reconnects.
|
||||
|
||||
The first slice uses React local state and reducers. Do not add Redux, Zustand,
|
||||
Pinia, XState, TanStack Query, or a client Effect runtime before lifecycle data
|
||||
creates a demonstrated need.
|
||||
|
||||
## Visual Direction
|
||||
|
||||
The console should look like an operational instrument, not a generic admin
|
||||
template:
|
||||
|
||||
- warm off-white workspace background with a subtle technical grid;
|
||||
- ink, slate, signal green, amber, and red tokens;
|
||||
- expressive condensed heading face paired with a readable technical body
|
||||
face;
|
||||
- dense but calm source rows;
|
||||
- raw evidence presented as an intentional inspection surface, not a debug
|
||||
afterthought;
|
||||
- desktop-first layout that remains usable on a narrow laptop or tablet.
|
||||
|
||||
Avoid dark-mode-first styling, purple gradients, interchangeable dashboard
|
||||
cards, and motion without semantic purpose. The first meaningful animation is
|
||||
the connection transition and staggered source reveal.
|
||||
|
||||
## Error And Security Boundaries
|
||||
|
||||
- The proxy never forwards non-loopback URLs.
|
||||
- Upstream redirects are rejected rather than followed.
|
||||
- Browser request bodies are limited to 256 KiB.
|
||||
- Upstream response bodies are limited to 4 MiB.
|
||||
- Upstream calls have a five-second timeout and abort signal.
|
||||
- Raw evidence is JSON-serialized and rendered as text, never inserted as HTML.
|
||||
- Hono error responses contain a stable error code and safe message; stack
|
||||
traces remain server-side.
|
||||
- The app binds to loopback by default.
|
||||
- This slice does not claim authentication or protection from other local
|
||||
processes running as the same user.
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
Use Vitest for all TypeScript tests and React Testing Library for component
|
||||
behavior.
|
||||
|
||||
Required tests:
|
||||
|
||||
- target policy accepts the three loopback host forms and rejects remote,
|
||||
credentialed, query, fragment, implicit-port, and redirect targets;
|
||||
- JSON-RPC schemas accept valid success/error envelopes and reject mismatched
|
||||
ids or malformed envelopes;
|
||||
- method registry exposes only health and source-list operations;
|
||||
- Hono connect route returns interpreted and raw health data through an injected
|
||||
fake upstream fetch;
|
||||
- Hono RPC route rejects unknown operations before any fetch;
|
||||
- Hono maps timeout, connection, remote RPC, and decode failures to stable error
|
||||
DTOs;
|
||||
- React connection form preserves failed input and persists only successful
|
||||
normalized targets;
|
||||
- source inventory renders interpreted source rows;
|
||||
- raw evidence drawer exposes request, response, and equivalent CLI;
|
||||
- production build completes and the Hono static fallback serves the console.
|
||||
|
||||
Do not require a live Python server in the default frontend test suite. Add a
|
||||
documented optional smoke command against:
|
||||
|
||||
```powershell
|
||||
uv run wf-rpc-server --config wf.config.json --host 127.0.0.1 --port 8765
|
||||
```
|
||||
|
||||
## First-Slice Success Criteria
|
||||
|
||||
The foundation slice is complete when:
|
||||
|
||||
1. `pnpm --dir web install`, test, typecheck, and build commands are documented
|
||||
and pass;
|
||||
2. one root development command starts Vite and Hono;
|
||||
3. a user can enter a loopback RPC URL and receive interpreted
|
||||
`workflow.health` state;
|
||||
4. a connected user can list workflow sources;
|
||||
5. every displayed operation has raw request/response evidence and an
|
||||
equivalent CLI string;
|
||||
6. unknown methods and non-loopback targets fail before upstream fetch;
|
||||
7. the production Hono process serves the built React application;
|
||||
8. no Python CORS change, direct store access, workflow mutation, graph UI,
|
||||
autoplay, replay, or agent integration is included.
|
||||
|
||||
## Deferred Slices
|
||||
|
||||
After this foundation:
|
||||
|
||||
1. read-only lifecycle inspectors for drafts, artifacts, deployments, runs,
|
||||
traces, and graph rendering;
|
||||
2. prepared lifecycle job, autoplay, typed interrupt form, issue-board output,
|
||||
and replay;
|
||||
3. constrained demo agent and recipe macro;
|
||||
4. Astro presentation and appendix app consuming shared React components only
|
||||
where interactivity is useful.
|
||||
Reference in New Issue
Block a user