docs: add thesis report workflow evidence
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
# Report Workflow Example
|
||||
|
||||
This example is the deterministic thesis case study. It demonstrates a trusted
|
||||
Python source that turns project notes into a typed report object without using
|
||||
remote OAuth, LLM calls, or provider quota.
|
||||
|
||||
## Files
|
||||
|
||||
- `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.
|
||||
- `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
|
||||
source.
|
||||
|
||||
## Run
|
||||
|
||||
From the repository root:
|
||||
|
||||
```powershell
|
||||
uv run wf config validate examples/report_workflow/wf.config.json
|
||||
uv run wf-rpc-server --config examples/report_workflow/wf.config.json
|
||||
```
|
||||
|
||||
In another terminal:
|
||||
|
||||
```powershell
|
||||
uv run wf --config examples/report_workflow/wf.config.json status
|
||||
uv run wf --config examples/report_workflow/wf.config.json cap list --source local.report
|
||||
uv run wf --config examples/report_workflow/wf.config.json cap call local.report.extract_report --input-file examples/report_workflow/cap-input.json --format compact
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```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 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
|
||||
uv run wf --config examples/report_workflow/wf.config.json run list --limit 5
|
||||
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
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
## Thesis Evidence
|
||||
|
||||
The example supports these claims:
|
||||
|
||||
- Python sources can expose typed capabilities through the same workflow surface
|
||||
as built-in and MCP sources.
|
||||
- The case-study path is deterministic and does not depend on an LLM or remote
|
||||
provider.
|
||||
- The workflow lifecycle can be exercised through config validation, capability
|
||||
inventory, capability calls, artifacts, deployments, runs, inspect, and trace.
|
||||
@@ -0,0 +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"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
# Weekly Project Update
|
||||
|
||||
Summary:
|
||||
The workflow platform demo is ready for a deterministic thesis case study. The
|
||||
team wants a repeatable report that does not depend on remote OAuth, LLM output,
|
||||
or provider quotas.
|
||||
|
||||
Actions:
|
||||
- Alice | Prepare demo config | Friday
|
||||
- Bao | Run five agent attempts | Monday
|
||||
- Casey | Capture trace screenshots | Tuesday
|
||||
|
||||
Risks:
|
||||
- Google Drive MCP quota is too low for regression evidence
|
||||
- Unbounded provider output can waste tokens
|
||||
|
||||
Followups:
|
||||
- Add optional Markdown renderer
|
||||
- Compare direct script baseline against workflow lifecycle
|
||||
@@ -0,0 +1,110 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from wf_authoring import node
|
||||
|
||||
|
||||
class ReadInput(BaseModel):
|
||||
path: str = Field(description="Path to a UTF-8 Markdown notes file.")
|
||||
|
||||
|
||||
class ReadOutput(BaseModel):
|
||||
text: str
|
||||
|
||||
|
||||
class ExtractInput(BaseModel):
|
||||
text: str
|
||||
|
||||
|
||||
class ActionItem(BaseModel):
|
||||
owner: str
|
||||
task: str
|
||||
due: str
|
||||
|
||||
|
||||
class ReportOutput(BaseModel):
|
||||
title: str
|
||||
summary: str
|
||||
action_items: list[ActionItem]
|
||||
risks: list[str]
|
||||
followups: list[str]
|
||||
|
||||
|
||||
class MarkdownInput(BaseModel):
|
||||
report: ReportOutput
|
||||
|
||||
|
||||
class MarkdownOutput(BaseModel):
|
||||
markdown: str
|
||||
|
||||
|
||||
@node(name="read_notes")
|
||||
def read_notes(payload: ReadInput) -> ReadOutput:
|
||||
return ReadOutput(text=Path(payload.path).read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
@node(name="extract_report")
|
||||
def extract_report(payload: ExtractInput) -> ReportOutput:
|
||||
title = ""
|
||||
summary_lines: list[str] = []
|
||||
actions: list[ActionItem] = []
|
||||
risks: list[str] = []
|
||||
followups: list[str] = []
|
||||
section: str | None = None
|
||||
|
||||
for raw_line in payload.text.splitlines():
|
||||
line = raw_line.strip()
|
||||
if not line:
|
||||
continue
|
||||
if line.startswith("# "):
|
||||
title = line.removeprefix("# ").strip()
|
||||
continue
|
||||
if line.endswith(":"):
|
||||
section = line[:-1].lower()
|
||||
continue
|
||||
if section == "summary":
|
||||
summary_lines.append(line)
|
||||
elif section == "actions" and line.startswith("- "):
|
||||
parts = [part.strip() for part in line.removeprefix("- ").split("|")]
|
||||
if len(parts) == 3:
|
||||
owner, task, due = parts
|
||||
actions.append(ActionItem(owner=owner, task=task, due=due))
|
||||
elif section == "risks" and line.startswith("- "):
|
||||
risks.append(line.removeprefix("- ").strip())
|
||||
elif section == "followups" and line.startswith("- "):
|
||||
followups.append(line.removeprefix("- ").strip())
|
||||
|
||||
return ReportOutput(
|
||||
title=title,
|
||||
summary=" ".join(summary_lines),
|
||||
action_items=actions,
|
||||
risks=risks,
|
||||
followups=followups,
|
||||
)
|
||||
|
||||
|
||||
@node(name="render_markdown_report")
|
||||
def render_markdown_report(payload: MarkdownInput) -> MarkdownOutput:
|
||||
report = payload.report
|
||||
lines = [
|
||||
f"# {report.title}",
|
||||
"",
|
||||
report.summary,
|
||||
"",
|
||||
"## Action Items",
|
||||
]
|
||||
lines.extend(
|
||||
f"- {item.owner}: {item.task} (due: {item.due})"
|
||||
for item in report.action_items
|
||||
)
|
||||
lines.extend(["", "## Risks"])
|
||||
lines.extend(f"- {risk}" for risk in report.risks)
|
||||
lines.extend(["", "## Followups"])
|
||||
lines.extend(f"- {followup}" for followup in report.followups)
|
||||
return MarkdownOutput(markdown="\n".join(lines))
|
||||
|
||||
|
||||
registry = [read_notes, extract_report, render_markdown_report]
|
||||
@@ -0,0 +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"
|
||||
}
|
||||
Reference in New Issue
Block a user