docs: expand report workflow case study
This commit is contained in:
@@ -257,7 +257,7 @@ report remain grounded in repository evidence.
|
||||
The document uses these terms with specific meanings:
|
||||
|
||||
| Term | Meaning | Example |
|
||||
| --- | ----- | --- |
|
||||
| --- | ------ | ---- |
|
||||
| Workflow capability | A workflow-facing callable operation exposed by a source. | `local.report.extract_report` |
|
||||
| `NodeSpec` | The authoring-layer typed contract produced by decorators or source adapters. | a Python `@node` projection |
|
||||
| `NodeDef` | The core-level serializable node contract: input schema, output schema, and declared outcomes. | a workflow plan node definition |
|
||||
@@ -347,7 +347,7 @@ composition.
|
||||
Representative sources and source families today:
|
||||
|
||||
| Source | Kind | Role |
|
||||
| --- | --- | --- |
|
||||
| --- | --- | ----- |
|
||||
| `wf.std` | `system` | Built-in workflow nodes and reducers |
|
||||
| `wf.source` | `system` | Built-in source resource helper |
|
||||
| `wf.recipes` | `system` | First-party workflow recipes |
|
||||
@@ -598,7 +598,7 @@ provider-specific knowledge.
|
||||
The implementation is organized into focused packages with clear boundaries:
|
||||
|
||||
| Package | Responsibility |
|
||||
| --- | --------- |
|
||||
| --- | -------- |
|
||||
| `wf_core` | Deterministic workflow kernel: graph execution, state, outcomes, trace, resume |
|
||||
| `wf_authoring` | Authoring primitives: `NodeSpec`, `WorkflowBuilder`, DSL, reducer authoring, recipes |
|
||||
| `wf_platform` | Neutral source DTOs, source visibility, permission metadata, and policy |
|
||||
@@ -839,11 +839,12 @@ expressiveness. Graph features such as interrupts, foreach, subgraphs, joins,
|
||||
and reducer behavior are covered by targeted tests and code evidence in the
|
||||
evaluation section.
|
||||
|
||||
The thesis-critical automated report-workflow run is intentionally narrow: it
|
||||
executes a single deterministic extraction node through the artifact,
|
||||
deployment, and run lifecycle. The same Python source exposes additional
|
||||
capabilities for discovery and extensibility evidence, and the supplemental
|
||||
browser-click example covers a serial three-node workflow.
|
||||
The thesis-critical automated report-workflow run executes the full deterministic
|
||||
report pipeline through the artifact, deployment, and run lifecycle:
|
||||
`read_notes -> extract_report -> render_markdown_report`. This keeps the case
|
||||
study small enough to audit while still exercising source discovery, multi-node
|
||||
dataflow, state mapping, artifact saving, deployment binding, run output, and
|
||||
trace inspection.
|
||||
|
||||
## Case Study Components
|
||||
|
||||
@@ -855,7 +856,9 @@ The example bundle lives at
|
||||
- `input.md` --- fixture Markdown notes with summary, actions, risks, and
|
||||
followups sections.
|
||||
- `cap-input.json` --- a capability-call payload generated from the fixture.
|
||||
- `run-input.json` --- a workflow-run payload generated from the fixture.
|
||||
- `run-input.json` --- a workflow-run payload pointing at the fixture.
|
||||
- `workflow.plan.json` --- the three-node raw workflow plan used for artifact
|
||||
creation.
|
||||
- `wf.config.json` --- a local server and client config using the
|
||||
`local.report` Python source.
|
||||
|
||||
@@ -946,54 +949,70 @@ demonstrate the agent-operable surface:
|
||||
wf cap call local.report.extract_report --input-file cap-input.json --format compact
|
||||
```
|
||||
|
||||
6. **Draft creation.** A draft workspace is seeded from the capability's
|
||||
6. **Draft bootstrap.** A draft workspace can be seeded from a capability's
|
||||
input/output schemas:
|
||||
|
||||
```powershell
|
||||
wf draft create-from-capability report_ws local.report.extract_report
|
||||
```
|
||||
|
||||
7. **Draft validation.** The draft is checked for schema conformance and source
|
||||
This is intentionally a best-effort bootstrap, not a complete workflow
|
||||
synthesizer. It creates an editable one-step draft from wrapper hints.
|
||||
|
||||
7. **Focused draft edits.** Common edits can be applied without writing JSON
|
||||
Patch manually:
|
||||
|
||||
```powershell
|
||||
wf draft set-name report_ws --revision 1 --name report_case_study
|
||||
wf draft set-input report_ws --revision 2 --step call --map input.text=text
|
||||
wf draft set-output report_ws --revision 3 --step call --map title=state.title --map summary=state.summary
|
||||
```
|
||||
|
||||
These commands edit existing draft fields. Structural edits, such as adding
|
||||
the `read_notes` and `render_markdown_report` steps around `extract_report`,
|
||||
still use `wf draft patch` or the raw-plan import path below.
|
||||
|
||||
8. **Draft validation.** The draft is checked for schema conformance and source
|
||||
availability:
|
||||
|
||||
```powershell
|
||||
wf draft validate report_ws
|
||||
```
|
||||
|
||||
8. **Artifact saving.** The draft becomes an immutable artifact with source
|
||||
bindings:
|
||||
9. **Artifact saving.** The tested case study imports the complete three-node
|
||||
plan as an immutable artifact with source bindings:
|
||||
|
||||
```powershell
|
||||
wf draft save report_ws --artifact report_case_study --version 1 --title "Report Case Study" --binding local.report=local.report
|
||||
wf artifact create-from-plan workflow.plan.json --artifact report_case_study --version 1 --title "Report Case Study" --outcome ok --binding local.report=local.report
|
||||
```
|
||||
|
||||
9. **Deployment saving.** The artifact is bound into a runnable deployment:
|
||||
10. **Deployment saving.** The artifact is bound into a runnable deployment:
|
||||
|
||||
```powershell
|
||||
wf deploy save report_case_study.default --artifact report_case_study --version 1 --binding local.report=local.report
|
||||
```
|
||||
|
||||
10. **Deployment validation.** The deployment is checked for bound source
|
||||
11. **Deployment validation.** The deployment is checked for bound source
|
||||
availability and compatibility:
|
||||
|
||||
```powershell
|
||||
wf deploy validate report_case_study.default
|
||||
```
|
||||
|
||||
11. **Run execution.** A workflow run starts from the deployment:
|
||||
12. **Run execution.** A workflow run starts from the deployment:
|
||||
|
||||
```powershell
|
||||
wf run start report_case_study.default --input-file run-input.json --trace-from 0 --trace-limit 5
|
||||
```
|
||||
|
||||
12. **Run inspection.** The run status, output, and diagnostics can be
|
||||
13. **Run inspection.** The run status, output, and diagnostics can be
|
||||
inspected:
|
||||
|
||||
```powershell
|
||||
wf run inspect <run_id>
|
||||
```
|
||||
|
||||
13. **Run trace.** Trace frames recorded during execution can be listed:
|
||||
14. **Run trace.** Trace frames recorded during execution can be listed:
|
||||
|
||||
```powershell
|
||||
wf run trace <run_id> --from 0 --limit 5
|
||||
@@ -1009,9 +1028,10 @@ The case study produces a structured report with:
|
||||
- Three action items with owner, task, and due date
|
||||
- Risks mentioning Google Drive MCP quota
|
||||
- Followups for Markdown rendering and baseline comparison
|
||||
- Rendered Markdown beginning with `# Weekly Project Update`
|
||||
|
||||
The output matches the typed `ReportOutput` schema, making validation
|
||||
deterministic.
|
||||
The workflow output includes both the typed `ReportOutput` object and a
|
||||
Markdown rendering produced by the final node, making validation deterministic.
|
||||
|
||||
## Automated Test Evidence
|
||||
|
||||
@@ -1023,19 +1043,16 @@ programmatically:
|
||||
fixture input. The test asserts the outcome is `ok`, the title matches, and
|
||||
the action items and risks contain expected values.
|
||||
|
||||
2. **Artifact/deployment/run path.** A test builds a workflow plan with a
|
||||
single `extract` node, saves the artifact, creates a deployment with source
|
||||
bindings, starts a run, and asserts that the run completes with the expected
|
||||
typed output.
|
||||
2. **Artifact/deployment/run path.** A test loads `workflow.plan.json`, which
|
||||
runs `read_notes -> extract_report -> render_markdown_report`, saves the
|
||||
artifact, creates a deployment with source bindings, starts a run, and
|
||||
asserts that the run completes with both structured report output and
|
||||
rendered Markdown output.
|
||||
|
||||
The thesis-critical run path therefore demonstrates the full lifecycle using a
|
||||
single deterministic extraction node. The surrounding Python source includes
|
||||
additional capabilities used for capability discovery and extensibility
|
||||
evidence; it is not evaluated as a multi-node read -> extract -> render
|
||||
pipeline in this artifact.
|
||||
A supplemental browser-click example demonstrates a serial three-node workflow
|
||||
with bounded before/after snapshots; it is supporting evidence, not the main
|
||||
case study.
|
||||
deterministic three-node pipeline. A supplemental browser-click example remains
|
||||
supporting evidence for human-interaction-style workflows and before/after
|
||||
snapshot outputs.
|
||||
|
||||
(Evidence: `tests/examples/test_report_workflow_example.py`.)
|
||||
|
||||
@@ -1437,14 +1454,32 @@ cap call local.report.extract_report `
|
||||
--input-file examples/report_workflow/cap-input.json --format compact
|
||||
```
|
||||
|
||||
### Draft Creation
|
||||
### Draft Bootstrap And Focused Edits
|
||||
|
||||
`create-from-capability` is a best-effort bootstrap. It creates a one-step
|
||||
draft from the selected capability's wrapper hints. Focused commands then cover
|
||||
common edits without requiring the agent to write RFC 6902 patches by hand.
|
||||
|
||||
```powershell
|
||||
uv run wf --config examples/report_workflow/wf.config.json `
|
||||
draft create-from-capability report_ws local.report.extract_report `
|
||||
--name report_case_study --title "Report Case Study"
|
||||
|
||||
uv run wf --config examples/report_workflow/wf.config.json `
|
||||
draft set-name report_ws --revision 1 --name report_case_study
|
||||
|
||||
uv run wf --config examples/report_workflow/wf.config.json `
|
||||
draft set-input report_ws --revision 2 --step call `
|
||||
--map input.text=text
|
||||
|
||||
uv run wf --config examples/report_workflow/wf.config.json `
|
||||
draft set-output report_ws --revision 3 --step call `
|
||||
--map title=state.title --map summary=state.summary
|
||||
```
|
||||
|
||||
Structural edits, such as adding `read_notes` before `extract_report` and
|
||||
`render_markdown_report` after it, use `draft patch` or a complete raw plan.
|
||||
|
||||
### Draft Validation
|
||||
|
||||
```powershell
|
||||
@@ -1454,10 +1489,14 @@ draft validate report_ws
|
||||
|
||||
### Artifact Saving
|
||||
|
||||
The tested case-study artifact imports the complete three-node plan:
|
||||
|
||||
```powershell
|
||||
uv run wf --config examples/report_workflow/wf.config.json `
|
||||
draft save report_ws --artifact report_case_study --version 1 `
|
||||
--title "Report Case Study" --binding local.report=local.report
|
||||
artifact create-from-plan examples/report_workflow/workflow.plan.json `
|
||||
--artifact report_case_study --version 1 `
|
||||
--title "Report Case Study" --outcome ok `
|
||||
--binding local.report=local.report
|
||||
```
|
||||
|
||||
### Deployment Saving
|
||||
|
||||
@@ -8,7 +8,9 @@ remote OAuth, LLM calls, or provider quota.
|
||||
|
||||
- `input.md` — fixture notes.
|
||||
- `cap-input.json` — capability-call payload generated from the fixture notes.
|
||||
- `run-input.json` — workflow-run payload generated from the fixture notes.
|
||||
- `run-input.json` — workflow-run payload pointing at the fixture notes.
|
||||
- `workflow.plan.json` — three-node raw plan:
|
||||
`read_notes -> extract_report -> render_markdown_report`.
|
||||
- `ops.py` — Python source exposing `read_notes`, `extract_report`, and
|
||||
`render_markdown_report`.
|
||||
- `wf.config.json` — local server/client config using the `local.report` Python
|
||||
@@ -32,13 +34,11 @@ uv run wf --config examples/report_workflow/wf.config.json cap call local.report
|
||||
```
|
||||
|
||||
The full artifact/deployment/run path is covered by
|
||||
`tests/examples/test_report_workflow_example.py`. To exercise the same lifecycle
|
||||
manually through the CLI, use the source capability as the draft seed:
|
||||
`tests/examples/test_report_workflow_example.py`. To exercise the same
|
||||
three-node lifecycle manually through the CLI, import the raw plan:
|
||||
|
||||
```powershell
|
||||
uv run wf --config examples/report_workflow/wf.config.json draft create-from-capability report_ws local.report.extract_report --name report_case_study --title "Report Case Study"
|
||||
uv run wf --config examples/report_workflow/wf.config.json draft validate report_ws
|
||||
uv run wf --config examples/report_workflow/wf.config.json draft save report_ws --artifact report_case_study --version 1 --title "Report Case Study" --binding local.report=local.report
|
||||
uv run wf --config examples/report_workflow/wf.config.json artifact create-from-plan examples/report_workflow/workflow.plan.json --artifact report_case_study --version 1 --title "Report Case Study" --outcome ok --binding local.report=local.report
|
||||
uv run wf --config examples/report_workflow/wf.config.json deploy save report_case_study.default --artifact report_case_study --version 1 --binding local.report=local.report
|
||||
uv run wf --config examples/report_workflow/wf.config.json deploy validate report_case_study.default
|
||||
uv run wf --config examples/report_workflow/wf.config.json run start report_case_study.default --input-file examples/report_workflow/run-input.json --trace-from 0 --trace-limit 5
|
||||
@@ -47,12 +47,29 @@ uv run wf --config examples/report_workflow/wf.config.json run inspect <run_id>
|
||||
uv run wf --config examples/report_workflow/wf.config.json run trace <run_id> --from 0 --limit 5
|
||||
```
|
||||
|
||||
Draft workspaces are still useful when an agent starts from one capability and
|
||||
edits toward a complete workflow:
|
||||
|
||||
```powershell
|
||||
uv run wf --config examples/report_workflow/wf.config.json draft create-from-capability report_ws local.report.extract_report --name report_case_study --title "Report Case Study"
|
||||
uv run wf --config examples/report_workflow/wf.config.json draft set-name report_ws --revision 1 --name report_case_study
|
||||
uv run wf --config examples/report_workflow/wf.config.json draft set-input report_ws --revision 2 --step call --map input.text=text
|
||||
uv run wf --config examples/report_workflow/wf.config.json draft set-output report_ws --revision 3 --step call --map title=state.title --map summary=state.summary
|
||||
uv run wf --config examples/report_workflow/wf.config.json draft validate report_ws
|
||||
```
|
||||
|
||||
`draft create-from-capability` is a best-effort bootstrapper. Focused commands
|
||||
cover common edits to existing draft fields; structural edits such as adding the
|
||||
`read_notes` and `render_markdown_report` steps use `draft patch`, or the raw
|
||||
plan import shown above.
|
||||
|
||||
The expected report includes:
|
||||
|
||||
- title: `Weekly Project Update`
|
||||
- three action items
|
||||
- at least one risk mentioning Google Drive MCP quota
|
||||
- followups for Markdown rendering and baseline comparison
|
||||
- rendered Markdown beginning with `# Weekly Project Update`
|
||||
|
||||
## Thesis Evidence
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
{
|
||||
"text": "# Weekly Project Update\n\nSummary:\nThe workflow platform demo is ready for a deterministic thesis case study. The\nteam wants a repeatable report that does not depend on remote OAuth, LLM output,\nor provider quotas.\n\nActions:\n- Alice | Prepare demo config | Friday\n- Bao | Run five agent attempts | Monday\n- Casey | Capture trace screenshots | Tuesday\n\nRisks:\n- Google Drive MCP quota is too low for regression evidence\n- Unbounded provider output can waste tokens\n\nFollowups:\n- Add optional Markdown renderer\n- Compare direct script baseline against workflow lifecycle\n"
|
||||
"path": "input.md"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
{
|
||||
"name": "report_case_study",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"path": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"path"
|
||||
]
|
||||
},
|
||||
"state_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"notes": {
|
||||
"type": "string",
|
||||
"reducer": "wf.std.replace"
|
||||
},
|
||||
"report": {
|
||||
"type": "object",
|
||||
"reducer": "wf.std.replace"
|
||||
},
|
||||
"markdown": {
|
||||
"type": "string",
|
||||
"reducer": "wf.std.replace"
|
||||
}
|
||||
}
|
||||
},
|
||||
"output_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"report": {
|
||||
"type": "object"
|
||||
},
|
||||
"markdown": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"report",
|
||||
"markdown"
|
||||
]
|
||||
},
|
||||
"outcomes": [
|
||||
"ok"
|
||||
],
|
||||
"start": "read",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "read",
|
||||
"type": "node",
|
||||
"node": "local.report.read_notes",
|
||||
"input": [
|
||||
{
|
||||
"path": {
|
||||
"root": "input",
|
||||
"parts": [
|
||||
"path"
|
||||
]
|
||||
},
|
||||
"target": {
|
||||
"root": "local",
|
||||
"parts": [
|
||||
"path"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": [
|
||||
{
|
||||
"source": {
|
||||
"root": "local",
|
||||
"parts": [
|
||||
"text"
|
||||
]
|
||||
},
|
||||
"target": {
|
||||
"root": "state",
|
||||
"parts": [
|
||||
"notes"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "extract",
|
||||
"type": "node",
|
||||
"node": "local.report.extract_report",
|
||||
"input": [
|
||||
{
|
||||
"path": {
|
||||
"root": "state",
|
||||
"parts": [
|
||||
"notes"
|
||||
]
|
||||
},
|
||||
"target": {
|
||||
"root": "local",
|
||||
"parts": [
|
||||
"text"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": [
|
||||
{
|
||||
"source": {
|
||||
"root": "local",
|
||||
"parts": []
|
||||
},
|
||||
"target": {
|
||||
"root": "state",
|
||||
"parts": [
|
||||
"report"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "render",
|
||||
"type": "node",
|
||||
"node": "local.report.render_markdown_report",
|
||||
"input": [
|
||||
{
|
||||
"path": {
|
||||
"root": "state",
|
||||
"parts": [
|
||||
"report"
|
||||
]
|
||||
},
|
||||
"target": {
|
||||
"root": "local",
|
||||
"parts": [
|
||||
"report"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"output": [
|
||||
{
|
||||
"source": {
|
||||
"root": "local",
|
||||
"parts": [
|
||||
"markdown"
|
||||
]
|
||||
},
|
||||
"target": {
|
||||
"root": "state",
|
||||
"parts": [
|
||||
"markdown"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"from": "read",
|
||||
"outcome": "ok",
|
||||
"to": "extract"
|
||||
},
|
||||
{
|
||||
"from": "extract",
|
||||
"outcome": "ok",
|
||||
"to": "render"
|
||||
},
|
||||
{
|
||||
"from": "render",
|
||||
"outcome": "ok",
|
||||
"to": "__end__"
|
||||
}
|
||||
],
|
||||
"output": [
|
||||
{
|
||||
"path": {
|
||||
"root": "state",
|
||||
"parts": [
|
||||
"report"
|
||||
]
|
||||
},
|
||||
"target": {
|
||||
"root": "local",
|
||||
"parts": [
|
||||
"report"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": {
|
||||
"root": "state",
|
||||
"parts": [
|
||||
"markdown"
|
||||
]
|
||||
},
|
||||
"target": {
|
||||
"root": "local",
|
||||
"parts": [
|
||||
"markdown"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@@ -76,51 +77,7 @@ async def test_report_workflow_artifact_deployment_run_path(tmp_path) -> None:
|
||||
config.server.store.root = tmp_path / "store"
|
||||
server = build_workflow_server_from_workflow_config(config)
|
||||
|
||||
plan = {
|
||||
"name": "report_case_study",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {"text": {"type": "string"}},
|
||||
"required": ["text"],
|
||||
},
|
||||
"state_schema": {
|
||||
"type": "object",
|
||||
"properties": {"report": {"type": "object", "reducer": "wf.std.replace"}},
|
||||
},
|
||||
"output_schema": {
|
||||
"type": "object",
|
||||
"properties": {"report": {"type": "object"}},
|
||||
"required": ["report"],
|
||||
},
|
||||
"outcomes": ["ok"],
|
||||
"start": "extract",
|
||||
"nodes": [
|
||||
{
|
||||
"id": "extract",
|
||||
"type": "node",
|
||||
"node": "local.report.extract_report",
|
||||
"input": [
|
||||
{
|
||||
"path": {"root": "input", "parts": ["text"]},
|
||||
"target": {"root": "local", "parts": ["text"]},
|
||||
}
|
||||
],
|
||||
"output": [
|
||||
{
|
||||
"source": {"root": "local", "parts": []},
|
||||
"target": {"root": "state", "parts": ["report"]},
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
"edges": [{"from": "extract", "outcome": "ok", "to": "__end__"}],
|
||||
"output": [
|
||||
{
|
||||
"path": {"root": "state", "parts": ["report"]},
|
||||
"target": {"root": "local", "parts": ["report"]},
|
||||
}
|
||||
],
|
||||
}
|
||||
plan = json.loads((EXAMPLE_DIR / "workflow.plan.json").read_text(encoding="utf-8"))
|
||||
|
||||
await server.api.create_artifact_from_plan(
|
||||
artifact_id="report_case_study",
|
||||
@@ -140,9 +97,10 @@ async def test_report_workflow_artifact_deployment_run_path(tmp_path) -> None:
|
||||
)
|
||||
run = await server.api.run_deployment(
|
||||
deployment_id="report_case_study.default",
|
||||
workflow_input={"text": (EXAMPLE_DIR / "input.md").read_text(encoding="utf-8")},
|
||||
workflow_input={"path": "input.md"},
|
||||
)
|
||||
|
||||
assert run["status"] == "completed"
|
||||
assert run["output"]["report"]["title"] == "Weekly Project Update"
|
||||
assert len(run["output"]["report"]["action_items"]) == 3
|
||||
assert run["output"]["markdown"].startswith("# Weekly Project Update")
|
||||
|
||||
Reference in New Issue
Block a user