half a decision

half a grill sesh will finish later
This commit is contained in:
lda
2026-06-13 09:59:49 +07:00 Verified
parent 4cab4a7d5d
commit 381d0b1924
7 changed files with 887 additions and 461 deletions
+3
View File
@@ -0,0 +1,3 @@
*.html
*.pdf
*.tex
+215 -454
View File
@@ -1,12 +1,15 @@
---
title: |
lda.chat\
\Large wf: Architecture \& Design
title: "lda.chat wf"
subtitle: "Workflow Platform Architecture and Demo"
author: "draft"
date: "2026-05-30"
date: "2026-06-12"
lang: "en-US"
documentclass: report
papersize: a4
fontsize: 12pt
fontsize: 10pt
toc: true
toc-depth: 2
numbersections: true
geometry:
- top=30mm
- bottom=30mm
@@ -16,527 +19,285 @@ mainfont: "Libertinus Serif"
sansfont: "Libertinus Sans"
monofont: "Libertinus Mono"
mathfont: "Libertinus Math"
colorlinks: true
linkcolor: "MidnightBlue"
urlcolor: "MidnightBlue"
toccolor: "MidnightBlue"
keywords:
- workflow
- agents
- JSON-RPC
- MCP
- Python sources
header-includes:
- \usepackage{graphicx}
- \usepackage{booktabs}
- \usepackage{hyperref}
- \usepackage{hyperxmp}
- \usepackage[dvipsnames]{xcolor}
- \usepackage{fancyhdr}
- \pagestyle{fancy}
- \fancyhead[L]{\small\leftmark}
- \fancyhead[R]{\small lda.chat}
- \fancyhead[L]{\small wf platform}
- \fancyhead[R]{\small\leftmark}
- \fancyfoot[C]{\thepage}
- \setlength{\parskip}{0.6em}
- \setlength{\parindent}{0pt}
- \setkeys{Gin}{width=\linewidth,height=0.55\textheight,keepaspectratio}
- \renewcommand{\arraystretch}{1.3}
- \newcommand{\status}[1]{\texorpdfstring{\hfill{\normalfont\small\textsc{#1}}}{}}
- \hypersetup{pdfauthor={lda.chat}, pdftitle={lda.chat wf Workflow Platform Architecture and Demo}}
diagram:
engine:
mermaid:
theme: neutral
outputFormat: svg
---
# Introduction
# Workflow Platform Presentation
`lda.chat` is an AI agent that turns natural language requests into executable
workspace workflows. The LLM plans a directed graph of typed steps. A
deterministic executor runs it. Registered node handlers do the real
work---whether those handlers call MCP tools, run local Python functions,
execute saved subworkflows, or compose authoring-layer operations.
This is a compact presentation narrative for the current product shape. It is
not a full architecture reference; use the linked docs for implementation
detail.
The core architectural split:
## One-Sentence Thesis
> **The LLM plans, but does not execute. The executor executes, but does not
> think. Nodes do work. Edges are dumb routing.**
LLMs should plan typed workflows, while a deterministic runtime executes those
workflows against explicit, validated capability sources.
**Thesis scope** is narrow: prove the planner/executor/tool split end to end
with a few working MCP tools, LLM-generated workflow JSON, validated execution,
and structured results. The broader product vision---tool registries, scheduling,
multi-user---is out of scope.
## The Problem
# System Architecture \status{implemented}
Agents are good at deciding what should happen next, but bad at being the thing
that directly owns side effects, retries, durable state, and schema contracts.
## Package Structure
The platform separates those jobs:
The system is organized into three main packages with strict dependency
boundaries.
- The LLM or human author chooses and edits workflow structure.
- The workflow runtime executes a typed graph.
- Source providers expose callable capabilities.
- Stores persist artifacts, deployments, and stopped runs.
- Transports let CLI, future UI, and other clients talk to the same server.
```mermaid
graph TD
subgraph Core["wf_core --- Execution Kernel"]
direction TB
Models["models"]
Runtime["runtime"]
Scheduler["scheduler"]
Ops["runtime.ops"]
Validation["validation"]
Paths["paths"]
RunState["run_state"]
end
## Current Product Path
subgraph Authoring["wf_authoring --- DSL"]
direction TB
Builder["WorkflowBuilder"]
NodeDec["@node"]
DSL["dsl/"]
Reducers["reducers/"]
end
subgraph MCP["wf_mcp --- MCP Platform"]
direction TB
Broker["broker/"]
Proxy["proxy/"]
SDK["sdk/"]
WorkflowSurf["workflow_surface/"]
WfConvert["workflow/"]
end
Authoring --> Core
MCP --> Core
MCP --> Authoring
```text
wf CLI
-> JSON-RPC transport
-> WorkflowServer
-> WorkflowApi
-> wf_core runtime + wf_artifacts stores + wf_sources_* providers
```
**Dependency rules:**
The preferred server entrypoint is:
- `wf_core` must not import `wf_authoring` or `wf_mcp`.
- `wf_mcp.sdk` should not import `wf_core` or `wf_authoring`.
- `wf_mcp.proxy` should not import `wf_mcp.workflow`.
- `wf_mcp.workflow` is the only layer that converts MCP tools into node specs.
## Data Flow
```mermaid
flowchart LR
User["User request"] --> LLM["LLM Planner"]
LLM --> WJ["Workflow JSON"]
WJ --> EX["Deterministic Executor"]
EX --> NH["Node Handlers"]
NH --> State["Shared Workflow State"]
```powershell
uv run wf-rpc-server --config wf.config.json
```
# Workflow Model \status{implemented}
The preferred client entrypoint is:
A workflow is a directed graph with explicit schemas and explicit control flow.
## Top-Level Structure
```mermaid
classDiagram
class Workflow {
+str name
+SchemaRef input_schema
+StateSchema state_schema
+SchemaRef output_schema
+list~InputBinding~ output
+list~NodeDef~ node_defs
+list~str~ outcomes
+str start
+list~Step~ nodes
+list~Edge~ edges
}
class NodeDef {
+str name
+SchemaRef input_schema
+SchemaRef output_schema
+list~str~ outcomes
}
class Edge {
+str from
+str outcome
+str to
}
Workflow --> NodeDef
Workflow --> Edge
```powershell
uv run wf --config wf.config.json status
```
## Schema Boundaries
`wf-mcp` still exists for legacy/special-purpose MCP-facing work, but the
durable product path is now `wf-rpc-server` plus `wf`.
Three schemas define distinct contracts:
## Core Model
- **`input_schema`** validates run input once. Input is stored independently and
remains readable throughout execution via `input.*` paths.
- **`state_schema`** defines typed workflow memory with per-field merge
reducers. Nodes read/write state via `state.*` paths.
- **`output_schema`** declares the output contract. Output is projected via
explicit `workflow.output` bindings that can read from `state.*`, `input.*`,
or `context.*` paths. A legacy fallback derives output from same-name top-level
state keys when bindings are omitted.
A workflow is a typed graph:
Input, state, and context are **separate read roots**. The runtime resolves
bindings against all three independently; input is not merged into state.
- `input_schema` validates run input.
- `state_schema` defines workflow memory and reducer behavior.
- `output_schema` defines the final result contract.
- Nodes do real work through `NodeSpec` handlers.
- Edges route by declared outcomes.
- Deployments bind logical source requirements to concrete sources.
## Step Types
The runtime does not know whether a node came from MCP, Python, OpenAPI, or a
built-in package. It resolves a `NodeSpec`, validates payloads, executes, records
trace, and commits reducer-aware state changes.
Steps form a discriminated union on the `type` field:
## Source Model
```mermaid
graph
Step["Step (type)"]
Step --> NU["NodeUse"]
Step --> SG["SubgraphNode"]
Step --> CN["ConditionNode"]
Step --> FE["ForeachNode"]
Step --> JN["JoinNode"]
Step --> EN["EndNode"]
Step --> IN["InterruptNode"]
The common provider output is `CapabilitySource`.
```text
source provider
-> CapabilitySource
-> WorkflowSpecProvider
-> WorkflowApi
```
**NodeUse** binds a reusable `NodeDef` with explicit input/output path
bindings. **ConditionNode** evaluates structured JSON expressions (not code
strings). **ForeachNode** iterates serial or concurrent. **InterruptNode**
pauses for typed external input. **EndNode** sets non-`ok` workflow outcomes.
Current source families:
## Path Bindings
| Source | Kind | Role |
| --- | --- | --- |
| `wf.std` | `system` | built-in standard workflow nodes and reducers |
| `wf.recipes` | `system` | first-party workflow recipes |
| MCP sources | `connection` | upstream MCP tools/resources/prompts via persistent sessions |
| Python sources | `python` | trusted project-local `NodeSpec` registries |
Bindings connect graph state to node-local payloads using structural paths:
The first explicit provider seam is intentionally small:
```python
class WorkflowSourceProvider(Protocol):
def load_sources(self) -> Mapping[str, CapabilitySource]: ...
```
This seam is for static source inventory. MCP also has runtime pools,
auth/catalog stores, admin/apply behavior, and live checks, so it should not be
forced into a tiny static interface too early.
## Demo: Python Source End To End
Write `ops.py`:
```python
from pydantic import BaseModel
from wf_authoring import node
class EchoInput(BaseModel):
text: str
class EchoOutput(BaseModel):
echoed: str
@node(name="echo")
def echo(payload: EchoInput) -> EchoOutput:
return EchoOutput(echoed=payload.text)
registry = [echo]
```
Configure it:
```json
{
"input": [
{ "target": { "root": "local", "parts": ["text"] },
"path": { "root": "input", "parts": ["text"] } }
],
"output": [
{ "source": { "root": "local", "parts": ["echoed"] },
"target": { "root": "state", "parts": ["echoed"] } }
]
"version": 1,
"client": {
"target": {
"kind": "rpc_http",
"url": "http://127.0.0.1:8766/rpc",
"timeout_seconds": 30
}
},
"server": {
"store": {"kind": "filesystem", "root": ".wf_python_store"},
"transports": [
{"kind": "rpc_http", "host": "127.0.0.1", "port": 8766, "path": "/rpc"}
],
"sources": [
{
"kind": "python",
"id": "local.ops",
"path": ".",
"module": "ops",
"registry": "registry"
}
]
}
}
```
Three path kinds: **LocalPath** (node payload), **GraphSourcePath**
(read from `input`/`state`/`context`), **StatePath** (write to state).
Validate before starting:
## State Reducers
State fields declare merge behavior. This matters for parallel and foreach
execution where multiple writers target the same path.
| Reducer | Policy | Use case |
|:--------|:-------|:---------|
| `wf.std.replace` | exclusive | Default scalars |
| `wf.std.append` | mergeable | Foreach accumulation |
| `wf.std.merge_object` | mergeable | Parallel object updates |
| `wf.std.add` | mergeable | Counters |
| `wf.std.set_union` | mergeable | Tag/ID collection |
# Runtime Execution \status{implemented}
## Execution Flow
```mermaid
flowchart
A["execute_workflow()"] --> B["prepare_new_run()"]
B --> C["create_run_state()"]
C --> D["resume_workflow()"]
D --> E{"select_next_frame()"}
E -->|"frame found"| F["step_workflow()"]
F --> G{"interrupted?"}
G -->|Yes| H["return RunState"]
G -->|No| E
E -->|"no frames"| I{"classify terminal"}
I -->|"completed"| J["finalize_run()"]
I -->|"failed"| K["return RunState"]
J --> M["return RunState"]
```powershell
uv run wf config validate wf.python.config.json
```
The executor loops: select a frame from the ready queue, dispatch one step,
re-enqueue or terminate. It never thinks. It only routes.
Start the server:
## Step Dispatch
```mermaid
flowchart LR
SW["step_workflow()"] -->|"NodeUse"| NE["execute node"]
SW -->|"Condition"| CE["evaluate"]
SW -->|"Foreach"| FE["foreach step"]
SW -->|"Join"| JO["done"]
SW -->|"End"| EN["complete"]
SW -->|"Interrupt"| IR["pause run"]
SW -->|"Subgraph"| SG["child scope"]
```powershell
uv run wf-rpc-server --config wf.python.config.json
```
Node handlers are registered callables---anything from a thin MCP tool wrapper
to a full Python function. The executor does not distinguish handler origin; it
resolves by name from the registry, invokes, validates, and commits.
Call the capability:
## Node Execution
```mermaid
sequenceDiagram
participant E as Executor
participant H as Handler
participant S as State
E->>E: resolve input bindings
E->>H: handler(payload, context)
H-->>E: NodeResult{outcome, output}
E->>E: validate outcome + output
E->>S: commit state patch (reducer-aware)
E->>E: record trace, advance frame
```powershell
uv run wf --config wf.python.config.json cap call local.ops.echo --input '{"text":"hello"}'
```
Two channels stay separate: **outcome** controls routing, **output** carries
typed business data.
Turn it into a saved workflow:
## Scheduler
```powershell
uv run wf --config wf.python.config.json draft create-from-capability `
python_echo_ws local.ops.echo --name python_echo
The scheduler uses an explicit FIFO **ready queue** of frame IDs.
uv run wf --config wf.python.config.json draft save python_echo_ws `
--artifact python_echo `
--version 1 `
--title "Python Echo" `
--outcome ok `
--binding local.ops=local.ops `
--binding wf.std=wf.std
```mermaid
stateDiagram-v2
[*] --> PENDING
PENDING --> RUNNING: scheduler selects
RUNNING --> PENDING: re-enqueue
RUNNING --> COMPLETED: reached end
RUNNING --> FAILED: unhandled error
RUNNING --> INTERRUPTED: interrupt node
RUNNING --> BLOCKED: waiting on child
BLOCKED --> PENDING: child completes
INTERRUPTED --> PENDING: resume delivered
uv run wf --config wf.python.config.json deploy save python_echo.default `
--artifact python_echo `
--version 1 `
--binding local.ops=local.ops `
--binding wf.std=wf.std
uv run wf --config wf.python.config.json run start python_echo.default `
--input '{"text":"hello workflow"}'
```
When the ready queue is empty, the scheduler classifies the run as completed,
interrupted, failed, or deadlocked.
Expected run result:
# Foreach \status{implemented}
## Serial
Each iteration creates a child frame, blocks the parent, and runs to
completion before the next item starts.
## Concurrent
Multiple item frames run simultaneously. Each gets its own **lineage**---a
write overlay that isolates sibling state. At the barrier, patches commit in
item-index order through declared reducers.
```mermaid
flowchart TD
PF["foreach parent"] --> C1["item 0"]
PF --> C2["item 1"]
PF --> C3["item 2"]
C1 & C2 & C3 --> BUF["buffer patches"]
BUF --> BARRIER["barrier: all done?"]
BARRIER --> COMMIT["commit in index order"]
COMMIT --> DONE["done / completed_with_errors"]
```json
{
"status": "completed",
"outcome": "ok",
"output": {"echoed": "hello workflow"}
}
```
Capacity is bounded by `ForeachConcurrentPolicy`: `max_active` (default 4)
limits ready/running item frames; `max_outstanding` (default 20) limits
active + blocked. Item error policies: **fail** (stop), **skip** (continue,
no state), **collect** (continue, write structured error records).
## What Works Today
# Interrupts \status{implemented}
- CLI can target local or remote workflow servers.
- JSON-RPC server can run from neutral config.
- `wf config validate` checks config shape, config-relative paths, and trusted
Python source imports.
- `wf status` summarizes target, sources, capabilities, runs, admin surfaces,
and registry availability.
- Capabilities can be listed, inspected, and called directly.
- Drafts can be created from capabilities and saved as immutable artifacts.
- Deployments bind logical sources to concrete sources.
- Runs are persisted at stopped boundaries and can be inspected/listed.
- MCP upstream sessions are stateful through `McpRuntimePool`.
- Python sources can run through the full draft -> artifact -> deployment -> run
lifecycle.
Interrupts are graph-native and typed, not arbitrary line-level pauses.
## Honest Limits
```mermaid
sequenceDiagram
participant N as Node
participant I as InterruptNode
participant R as Runtime
participant E as Caller
- Python sources are trusted in-process code; there is no sandbox.
- Python sources are static at server startup; no hot reload yet.
- Python source registry/apply support is not implemented yet.
- `WorkflowSourceProvider` covers static inventory only, not runtime/admin/apply
lifecycle.
- Run deletion is not implemented.
- File-backed stores are the proven storage backend; SQL/secret-manager stores
are future work.
- MCP app/widget passthrough is not a durable workflow product feature yet.
N->>I: outcome "needs_input"
I->>R: InterruptRequest{kind, payload}
R->>E: surface request
E->>R: resume payload + outcome
R->>I: map resume into state
I->>I: continue routing
```
## Next Direction
Pause points stay visible in the graph. Payloads are validated by `kind`.
Traces stay clean.
Near-term work should make source providers more regular without prematurely
flattening them:
# Subgraph Composition \status{implemented}
- Provider lifecycle for add/update/remove/apply/reload across source families.
- OpenAPI source provider using the same `CapabilitySource` shape.
- Clearer config/status diagnostics for source health.
- Optional Python source development reload.
- Production-grade auth/secret store integration.
A workflow can be used as a node, giving **graph composition** with one
consistent contract.
## Reading Map
```mermaid
flowchart LR
subgraph Parent["Parent"]
P1["A"] --> SG["SubgraphNode"]
SG --> P2["C"]
end
subgraph Child["Child (prepared)"]
C1["X"] --> C2["Y"]
end
SG --> C1
C2 --> SG
```
The parent supplies input bindings. The child runs in its own scope with its
own lineage. Output bindings project child state back to the parent.
**Interrupt bubbling (v1):** When a child subgraph hits an interrupt node, the
runtime constructs an `InterruptRoute` that identifies the child frame, scope,
and workflow ref. The parent subgraph frame stays `BLOCKED`; the entire run
returns `INTERRUPTED`. Resume restores the child scope and continues inside it.
This works for prepared local children. Artifact/deployment resolution for
nested saved children remains outside core; the platform resolves saved
descendants before execution starts.
# Authoring Layer \status{implemented}
## WorkflowBuilder
Fluent Python DSL for constructing workflows without raw dicts:
```mermaid
flowchart LR
WB["WorkflowBuilder"] -->|"use(spec)"| NU["NodeUse"]
WB -->|"condition()"| CN["Condition"]
WB -->|"foreach()"| FE["Foreach"]
WB -->|"interrupt()"| IN["Interrupt"]
WB -->|"compile()"| WF["Workflow"]
```
Common operations: `use(spec)`, `connect(from, outcome, to)`,
`branch(from, branches)`, `when(cond, then, otherwise)`,
`match(value, cases)`, `foreach(over, mode)`.
## @node Decorator
Converts a typed Python function into a reusable `NodeSpec`:
```python
@node(name="summarize", outcomes=["ok"])
def summarize_doc(input: SummarizeInput, ctx: RuntimeContext) -> SummarizeOutput:
"""Summarize a single document."""
return SummarizeOutput(summary=input.text[:500])
```
The decorator infers input/output models from type annotations, detects async,
and generates the `NodeDef` with schema refs.
# MCP Platform \status{implemented}
## Architecture
```mermaid
flowchart TB
subgraph Server["Unified MCP Server"]
Admin["wf.admin.*"]
Wf["wf.workflow.*"]
Proxy["connection.tool_name"]
end
subgraph Broker["WfMcpService"]
Cat["catalog"]
Disc["discovery"]
Art["artifacts"]
Drf["drafts"]
end
subgraph ProxyLayer["ProxyRuntime"]
Mount["mount registry"]
FMP["proxy per connection"]
end
Server --> Broker
Server --> ProxyLayer
ProxyLayer --> Upstream["Upstream MCP Servers"]
```
Each upstream connection gets a namespaced `FastMCPProxy` mount
(`connection_id.tool_name`). Hot reload is best-effort; FastMCP lacks a
complete unmount lifecycle.
## Tool Conversion
```mermaid
flowchart LR
DT["DiscoveredTool"] --> WDT["wrap_discovered_tool()"]
WDT --> NS["NodeSpec"]
NS --> CS["CapabilitySource"]
```
Every MCP tool gets `outcomes=("ok", "error")` by default. The wrapper
preserves the original JSON Schema as a contract and generates an async
handler that calls the upstream tool.
# Artifact Lifecycle \status{implemented}
## Draft to Deployment
```mermaid
flowchart LR
D["Draft<br/>(mutable)"] -->|"compile"| A["Artifact<br/>(immutable)"]
A -->|"bind sources"| DP["Deployment"]
DP -->|"execute"| R["Run"]
```
**Draft workspaces** support iterative LLM authoring with optimistic
concurrency (revision-checked patches). **Artifacts** are versioned snapshots
with dependency contract hashes. **Deployments** bind logical source aliases
to concrete MCP connections.
## Durable Runs \status{v1 boundaries}
Every `run_deployment` call persists a stopped run record (file-backed JSON)
with a stable `run_id`. Interrupted runs can be resumed via `resume_run` after
process restart, provided the same `FileRunStore` root is accessible.
**V1 limits:**
- Checkpointing happens at stopped boundaries only (interrupted, completed,
failed). No per-node or mid-call crash recovery.
- Resume revalidates the pinned dependency environment. If a required source is
missing or disabled, resume returns `resume_readiness=blocked` without
consuming the payload.
- During live execution, source failures produce a `failed` run, not a
`blocked` one. The `blocked` gate applies only at resume time.
Two deployments can point the same artifact at different accounts:
```
summarize_docs.personal -> context7.personal
summarize_docs.work -> context7.work
```
# Validation \status{implemented}
## Structural (Pre-Execution)
```mermaid
flowchart LR
VW["validate_workflow()"] --> A["node def uniqueness"]
VW --> B["node id uniqueness"]
VW --> C["edge source/dest existence"]
VW --> D["outcome declarations"]
VW --> E["binding path validity"]
VW --> F["reachable outcome wiring"]
```
Reports multiple issues via `ValidationReport` instead of failing at the
first.
## Runtime
At runtime: outcome is declared, output matches schema, merge conflicts are
caught, final output validates against `output_schema`.
# Open Questions
- Should the legacy same-name top-level state output fallback be removed once
explicit final output bindings are used everywhere?
- Trace model for raw extra node output?
- Isolated pure-Python execution: node type, tool backend, or separate API?
- Retry policy vs non-idempotent side-effecting tools?
# Stack
| Layer | Technology |
|:------|:-----------|
| Language | Python 3.14+ |
| Validation | Pydantic v2 |
| Tool protocol | MCP |
| MCP framework | FastMCP 3.2.4+ |
| Storage | File-backed JSON stores today; SQLite/PostgreSQL planned |
---
*Incomplete. See `docs/README.md` for the full documentation index.*
- [`wf_cli.md`](../wf_cli.md): CLI command reference.
- [`runbooks/python-source.md`](../runbooks/python-source.md): Python source
runbook.
- [`source_architecture.md`](../source_architecture.md): source provider
package map.
- [`project_map.md`](../project_map.md): package and entrypoint map.
- [`current_roadmap.md`](../current_roadmap.md): active roadmap.
+43
View File
@@ -0,0 +1,43 @@
# goal:
# use pandoc -M to change the key: diagram:engine:mermaid:outputFormat to svg or pdf if output is html or pdf.
# use the script at stuff/pandoc-diagram.ps1 to set the env vars and pass the filter to pandoc.
param(
[string]$type,
[Parameter(ValueFromRemainingArguments = $true)]
[string[]]$RemainingArgs
)
function funny([string] $output) {
return @{
"diagram" = @{
"engine" = @{
"mermaid" = @{
"outputFormat" = "$output"
}
}
}
}
}
#
if ($type -eq "html") {
$outputFormat = "svg"
}
elseif ($type -eq "pdf") {
$outputFormat = "pdf"
}
else {
Write-Error "Unsupported type: $type. Supported types are: html, pdf."
exit 1
}
$metadata = funny $outputFormat | ConvertTo-Json -Depth 10
$pandoc_diagram = Join-Path $PSScriptRoot "../../stuff/pandoc-diagram.ps1"
$metatempfile = New-TemporaryFile
try {
Set-Content -Path $metatempfile -Value $metadata
& $pandoc_diagram --metadata-file=$metatempfile --embed-resources --standalone @RemainingArgs
}
finally {
Remove-Item $metatempfile
}
+235 -7
View File
@@ -14,6 +14,49 @@ The short version:
> The LLM plans. The runtime executes. Source providers expose capabilities.
> Stores preserve durable workflow state.
## Research Question
Primary research question:
> How can an AI-agent-facing workflow platform represent, validate, execute, and
> persist reusable workspace automations while keeping planning separate from
> deterministic execution?
Product motivation:
> How can external AI agents help workspace operators create reusable
> automations without requiring them to write scripts, while preserving
> validation, inspection, and durable execution?
## Main Contribution
The thesis contribution is a platform architecture, not a new foundation model:
1. typed workflow artifact/deployment/run lifecycle
2. source-provider boundary for MCP, Python, and future OpenAPI sources
3. durable server/API/CLI surface that external agents can drive
4. validation and inspection mechanisms that reduce planner trial-and-error
5. next-action guidance that points an agent toward useful lifecycle operations
without replacing validation
## Working Title
Safer current title:
> Design and Implementation of lda.chat: Infrastructure for AI Agents to Author
> and Execute Workspace Workflows
Aspirational product title:
> Design and Implementation of lda.chat: An AI Agent Platform for Authoring and
> Executing Workspace Workflows
Use the safer title if the thesis must describe exactly what exists today:
external agents such as Claude Desktop and OpenCode can drive the platform, but
`lda.chat` does not yet bundle its own autonomous agent brain. Use the
aspirational title only if the thesis explicitly frames `lda.chat` as the
platform intended to host or serve AI agents, not as a completed built-in agent.
## 1. Problem Statement
Current agent/tool systems often let the LLM directly orchestrate side effects
@@ -27,9 +70,14 @@ through ad hoc tool calls. This creates practical problems:
The thesis should frame the platform as a response to those pressures.
The automation target is reusable workspace procedures, not arbitrary office
work end-to-end. Examples include document transformation, data collection,
tool/API calls, report preparation, monitoring checks, and scheduled workspace
operations.
## 2. Thesis
The proposed model is a workflow platform for AI-assisted work:
The proposed model is a prototype platform for AI-assisted work:
- workflows are typed graphs
- source capabilities are exposed through explicit contracts
@@ -38,21 +86,54 @@ The proposed model is a workflow platform for AI-assisted work:
- clients interact through stable APIs/transports rather than direct runtime
internals
Natural language belongs in the authoring loop: a workspace operator can express
intent to an external LLM agent, but the reusable output should be a typed
workflow artifact and deployment rather than an opaque prompt transcript.
Be explicit about actors:
- the workflow owner wants to see useful workflow runs and outputs
- the external LLM agent may be the one driving CLI/API operations
- the developer/operator configures sources, secrets, server processes, and
trusted Python code
The CLI should be described as agent-operable first and human-usable second. Its
structured output, status/inspect/list commands, validation commands, compact
summaries, and guarded destructive actions make it a practical surface for
external agents.
This is not a claim that LLMs cannot operate tools. It is a claim that durable,
inspectable, reusable work benefits from a separate execution substrate.
Use “prototype platform” deliberately. The implementation proves the core
lifecycle and architecture, but it is not yet a finished product with scheduling,
visual workflow editing, production secrets, general fork/gather, and broad
real-world evaluation.
Next actions and repairable validation failures should be framed as
machine-client UX. Human interfaces use buttons and affordances; an agent-facing
API needs compact structured hints, stable diagnostic fields, and suggested next
operations so an LLM agent can recover without blind probing.
## 3. Design Goals
The design goals should be stated early and then revisited in evaluation:
- deterministic execution
- validation-centered lifecycle for LLM-authored workflows
- typed inputs, state, outputs, and node payloads
- explicit source binding
- scoped workflow portability through artifact requirements and deployment
binding contracts
- durable artifacts, deployments, and stopped runs
- inspectable trace slices
- reviewable lifecycle points for drafts, deployments, runs, diagnostics, and
guarded destructive actions
- resumability after interruption
- transport neutrality for CLI, server, and future UI/MCP clients
- source-provider extensibility for MCP, Python, OpenAPI, and future families
- source-provider correctness, especially for external systems whose tools,
resources, prompts, or authentication depend on initialized stateful sessions
## 4. Architecture
@@ -69,18 +150,39 @@ wf_cli
Important layers:
- `wf_core`: deterministic workflow kernel
- `wf_authoring`: Python authoring helpers and `NodeSpec` creation
- `wf_authoring`: authoring support for `NodeSpec`, drafts, wrappers, API
surfaces, source providers, and MCP/admin tools
- `wf_api`: application surface for capabilities, drafts, artifacts,
deployments, runs, and admin/source operations
- `wf_server`: durable server composition boundary
- `wf_transport_rpc_http`: JSON-RPC-over-HTTP transport
- `wf_sources_mcp`: MCP upstream source implementation and persistent runtime
- `wf_sources_python`: trusted in-process Python source loading
- `wf_mcp`: legacy/special-purpose MCP frontend and compatibility package
- `wf_mcp`: legacy/special-purpose MCP compatibility package
The thesis should explain why the old “everything in MCP” shape was split:
transport, source provider, workflow API, and runtime concerns are different.
Architecture spine:
- workflow core: deterministic execution semantics for graph, state, outcomes,
trace, and resume rules
- platform domain: artifacts, deployments, runs, stores, sources, binding
contracts, validation, and admin concepts
- workflow API surface: lifecycle operations exposed to clients
- server/transport composition: concrete stores, sources, runtimes, and
communication mechanisms
JSON-RPC is an implementation of the Workflow API Surface. It should not be
presented as the product boundary or as the place where workflow semantics live.
`wf_server` is composition: it assembles concrete stores, sources, runtimes, and
admin surfaces into a long-lived service. It should not own workflow semantics.
`wf_authoring` is support infrastructure used by drafts, API surfaces, source
providers, and MCP tools; it is not a fifth runtime/product layer.
If MCP is discussed, distinguish upstream MCP sources from a future client-facing
MCP frontend. The former exists as a source family; the latter should not be
claimed as a completed clean platform surface.
## 5. Workflow Model
Describe workflows as typed graphs:
@@ -94,11 +196,43 @@ Describe workflows as typed graphs:
- interrupts: represent typed external input points
- subgraphs: compose workflows as nodes
Separate lifecycle objects:
- draft workspace: mutable authoring state for agent/user iteration
- workflow artifact: immutable versioned workflow definition
- deployment: binding contract from artifact version to concrete source/runtime
context
- run: execution record with status, diagnostics, output, trace, and resumable
stopped/interrupted state where applicable
Key distinction:
- outcome controls routing
- output carries business data
The graph model is also a safety boundary. It is not safer because it can make
all tools safe; it is safer than arbitrary generated scripts because structure,
schemas, source bindings, state, outcomes, and review points are explicit. This
is the answer to “why not just have the AI write a Playwright script?” Scripts
can be simple and maintainable, but they do not automatically provide the same
validation and lifecycle affordances.
Code ends at the source-provider boundary. A workflow can call trusted Python,
Playwright, API, MCP, or future LLM capabilities, but those should appear as
typed source capabilities. The workflow itself remains an orchestration artifact,
not an embedded code blob.
Durability is a contract over time, not just storage. Artifacts, deployments,
bindings, run records, and traces preserve workflow intent. Validation against
the current source catalog determines whether that intent is still runnable. If
a source changes incompatibly and a deployment becomes `unrunnable`, the system
has preserved the contract instead of silently drifting.
Trace claims should be grounded in the current code: run summaries expose
`trace_count`, and clients can request caller-bounded trace slices for debugging.
Do not overstate this as production observability, distributed tracing, metrics,
or OpenTelemetry support.
## 6. Source Model
The common boundary is `CapabilitySource`.
@@ -116,6 +250,9 @@ The thesis should stress that the runtime does not care where a `NodeSpec` came
from. Source-specific behavior belongs in provider packages and server
composition.
MCP should be presented as one source family and a useful stress test for
source-provider correctness, not as the platform identity.
Current provider seam:
```python
@@ -126,6 +263,10 @@ class WorkflowSourceProvider(Protocol):
This seam is intentionally narrow: it covers static inventory, not runtime
pools, admin/apply, auth, or live health checks.
For MCP, source-provider correctness includes stateful runtime behavior. A
workflow capability call should not silently turn a stateful external provider
into a fresh one-off client call when provider state is part of correctness.
## 7. Implementation Vertical Slice
Use the working product path as evidence:
@@ -155,6 +296,10 @@ A strong demonstration is the Python source flow:
This shows the source abstraction is not MCP-only.
Frame Python sources as trusted developer extensibility. They are useful because
project-local code can become typed workflow capabilities quickly, but they are
not sandboxed non-programmer plugins yet.
## 8. Evaluation
Evaluation should use concrete evidence:
@@ -163,12 +308,44 @@ Evaluation should use concrete evidence:
- live smoke test against `wf-rpc-server`
- durable run/resume tests
- stateful MCP session reuse tests
- MCP source-provider correctness tests covering tools, resources, prompts, and
session reuse through the same server path
- Python source workflow-run integration test
- config validation catching import/path errors before server startup
- planner-efficiency checks: validation, source catalogs, compact output, and
inspectable errors should reduce repeated blind LLM attempts
- next-action guidance should reduce planner uncertainty before and after
validation calls
- attempt-count comparison on representative tasks, for example old interaction
traces with many failed attempts versus the current structured lifecycle
- draft-validation and run-failure analysis, especially cases where old session
or source assumptions caused repeated failed runs
- source-drift cases where old deployments become unrunnable with diagnostics
instead of silently executing against incompatible capabilities
Avoid vague claims such as “robust” or “production-ready” unless backed by
specific checks.
Evidence package:
- architecture/code walkthrough tied to the four-layer model
- automated tests for lifecycle, validation, source providers, persistence,
resume, stateful MCP reuse, and Python source integration
- live CLI/server smoke run
- before/after failed-attempt case study from old ad-hoc interaction to
structured workflow lifecycle
- explicit limitations and future work
Recommended case study:
- document/report preparation, not an echo demo
- deterministic current sources first, such as Python source text transforms
- optional/future LLM summarization as a typed source capability, not required
- output should be a structured report or Markdown/JSON artifact that a workflow
owner would plausibly want
- package the example with fixture input, Python source code, workflow config,
store/environment setup, and CLI/server commands
Possible evaluation questions:
- Can a source capability be discovered, called, saved into a workflow, deployed,
@@ -177,6 +354,43 @@ Possible evaluation questions:
- Can the same server be used through CLI and JSON-RPC transport?
- Can a new source family be added without changing `wf_core`?
- Are large/raw provider payloads bounded in CLI output?
- Can an external LLM agent converge on a valid workflow without spending most
of the interaction on tool-output spam and trial-and-error?
- Does the structured surface reduce failed attempts before success compared to
earlier ad-hoc agent/tool interaction traces?
- Do `wf draft validate`, deployment validation, and run inspection catch or
explain the kinds of issues that previously caused repeated failed runs?
- Does deployment validation surface source drift as runnable/unrunnable state
with diagnostics rather than silent behavior changes?
## 8.1 Positioning Against Existing Automation Platforms
The thesis should discuss the space it fits into through multiple baselines:
direct LLM tool use, manual scripts, Zapier-style automation platforms, RPA
tools, and workflow engines. The goal is not to claim feature parity with mature
products. The goal is to explain the trade-off this prototype explores.
Zapier and similar platforms are stronger today at:
- polished non-programmer UI
- large integration catalogs
- hosted scheduling and triggers
- operational maturity
Manual scripts are powerful and often faster for technical users, so the thesis
should not dismiss them. The fair comparison is accessibility and adaptability:
how much skill and maintenance effort is required before a workspace operator or
external agent can turn a repeated task into a reusable workflow?
This prototype explores a different center of gravity:
- external AI agents can drive the authoring/execution lifecycle directly
- workflows are typed graphs with explicit schemas and source bindings
- local Python, MCP, and future OpenAPI sources can share one workflow surface
- runs, traces, artifacts, and deployments are first-class inspectable records
Use the comparison to position the work, not as a claim that the prototype
outperforms existing automation products.
## 9. Limitations
@@ -185,11 +399,19 @@ State limitations explicitly:
- Python sources are trusted in-process code; no sandbox yet.
- Python sources are static at server startup; no hot reload yet.
- Source provider lifecycle is early, especially for non-MCP mutable sources.
- File-backed stores are the proven storage backend; SQL/secret manager support
is future work.
- Workflow portability is scoped; local Python code, MCP catalogs, auth records,
and source stores can differ between environments.
- File-backed stores are the current implementation proof for durable lifecycle;
durability itself should not be framed as filesystem-specific.
- Auth records/admin surfaces exist as prototype plumbing, but end-to-end
production credential handling is not verified as a core thesis claim.
- Run deletion is not implemented.
- MCP widgets/apps are not carried through the durable workflow path.
- MCP widget/resource proxying is not supported; upstream interactive widgets
are not carried through the durable workflow path.
- Crash recovery is at stopped boundaries, not arbitrary mid-node checkpoints.
- Offline scheduling is not implemented yet.
- General fork/gather workflow control is future work.
- There is no full approval, roles, policy, or multi-user review system.
Limitations make the thesis more credible. They also motivate future work.
@@ -198,12 +420,17 @@ Limitations make the thesis more credible. They also motivate future work.
Likely future-work sections:
- provider lifecycle: add/update/remove/apply/reload for multiple source families
- OpenAPI source provider
- OpenAPI or fetch-style source provider for broader HTTP integration
- Python development reload
- LLM nodes as typed source capabilities
- production auth/secret stores
- SQL/transactional stores
- scheduler/server daemon operations
- offline scheduling for deployments
- fork/gather workflow control
- richer run rewind/time-travel debugging beyond stopped/interrupted resume
- UI/admin dashboard
- first-party workflow UI for listing, inspecting, and editing workflows
- richer evaluation with real workflows and larger source catalogs
## What Not To Do
@@ -220,6 +447,7 @@ Do not claim unimplemented production properties:
- no general provider hot reload
- no production secret manager
- no full MCP widget passthrough
- no unmeasured performance or production-readiness claims
The strongest version is an honest systems argument: