docs: address thesis review nits

This commit is contained in:
lda
2026-06-14 18:19:45 +07:00 Verified
parent 8180847195
commit 38f4ed769e
7 changed files with 95 additions and 20 deletions
+7 -2
View File
@@ -46,6 +46,10 @@ header-includes:
- \setkeys{Gin}{width=\linewidth,height=0.55\textheight,keepaspectratio}
- \renewcommand{\arraystretch}{1.3}
- \hypersetup{pdfauthor={lda.chat}, pdftitle={Design and Implementation of lda.chat}}
diagram:
engine:
mermaid:
theme: neutral
---
# Introduction
@@ -635,7 +639,8 @@ call, remote OAuth, or provider quota.
## Case Study Components
The example bundle lives at `examples/report_workflow/` and contains:
The example bundle lives at
[`examples/report_workflow/`](../../examples/report_workflow/) and contains:
- `ops.py` --- a Python source exposing `read_notes`, `extract_report`, and
`render_markdown_report` as typed `NodeSpec` capabilities.
@@ -1170,7 +1175,7 @@ uv run wf --config examples/report_workflow/wf.config.json run trace <run_id> --
## Appendix B: Evidence Index
See `evidence-index.md` for the full claim-to-evidence map.
See [evidence-index.md](evidence-index.md) for the full claim-to-evidence map.
| Claim | Evidence |
| --- | --- |
+1 -1
View File
@@ -64,7 +64,7 @@ The case study should exist as a runnable example, not only prose. Target shape:
`README.md`, and commands for config validation, server startup, capability
calls, draft/artifact/deployment creation, run, inspect, and trace.
The runnable evidence bundle for this case study lives at
`examples/report_workflow/README.md`.
`examples/report_workflow/`.
Keep the thesis-critical path deterministic. Do not require an LLM call inside
+5 -4
View File
@@ -113,10 +113,11 @@ auth admin are implemented. The next work is polish, not new broad surfaces.
- Completed source provider docs: `docs/source_provider_guide.md` now covers
MCP HTTP, MCP stdio, Python sources, auth refs, OAuth refresh-token setup,
diagnostics, and the Google Drive MCP caveat.
- Completed platform source policy: `wf.*` process-provided sources are marked
as platform sources. They resolve by fixed source id, do not require
self-bindings, and deployment validation rejects explicit platform-source
bindings as stale configuration.
- Completed platform source policy: documented fixed-id sources such as `wf.std`
and `wf.source` are platform sources. They resolve by fixed source id, do not
require self-bindings, and deployment validation rejects explicit
platform-source bindings as stale configuration. Other `wf.*` namespaces are
described by their own source docs/policies.
- Completed `wf.source.read_resource`: resource refs are inert pass-by-value
data using `logical_source`; explicit platform helper nodes dereference them
through runtime/platform context with bounded output.
+4 -2
View File
@@ -72,8 +72,10 @@ see [`workflow platform presentation`](add/2026-06-workflow-platform-presentatio
## Documentation
- `docs/add/system-design-implementation.md` — formal thesis/system-design draft.
- `docs/add/evidence-index.md` — claim-to-evidence map for the thesis draft.
- [`docs/add/system-design-implementation.md`](add/system-design-implementation.md)
— formal thesis/system-design draft.
- [`docs/add/evidence-index.md`](add/evidence-index.md) — claim-to-evidence
map for the thesis draft.
## Tests
+34 -7
View File
@@ -6,6 +6,8 @@ from pydantic import BaseModel, Field
from wf_authoring import node
_EXAMPLE_DIR = Path(__file__).resolve().parent
class ReadInput(BaseModel):
path: str = Field(description="Path to a UTF-8 Markdown notes file.")
@@ -43,11 +45,25 @@ class MarkdownOutput(BaseModel):
@node(name="read_notes")
def read_notes(payload: ReadInput) -> ReadOutput:
return ReadOutput(text=Path(payload.path).read_text(encoding="utf-8"))
return _read_notes(payload)
@node(name="extract_report")
def extract_report(payload: ExtractInput) -> ReportOutput:
return _extract_report(payload)
@node(name="render_markdown_report")
def render_markdown_report(payload: MarkdownInput) -> MarkdownOutput:
return _render_markdown_report(payload)
def _read_notes(payload: ReadInput) -> ReadOutput:
path = _resolve_example_path(payload.path)
return ReadOutput(text=path.read_text(encoding="utf-8"))
def _extract_report(payload: ExtractInput) -> ReportOutput:
title = ""
summary_lines: list[str] = []
actions: list[ActionItem] = []
@@ -86,25 +102,36 @@ def extract_report(payload: ExtractInput) -> ReportOutput:
)
@node(name="render_markdown_report")
def render_markdown_report(payload: MarkdownInput) -> MarkdownOutput:
def _render_markdown_report(payload: MarkdownInput) -> MarkdownOutput:
report = payload.report
lines = [
f"# {report.title}",
"",
"Summary:",
report.summary,
"",
"## Action Items",
"Actions:",
]
lines.extend(
f"- {item.owner}: {item.task} (due: {item.due})"
f"- {item.owner} | {item.task} | {item.due}"
for item in report.action_items
)
lines.extend(["", "## Risks"])
lines.extend(["", "Risks:"])
lines.extend(f"- {risk}" for risk in report.risks)
lines.extend(["", "## Followups"])
lines.extend(["", "Followups:"])
lines.extend(f"- {followup}" for followup in report.followups)
return MarkdownOutput(markdown="\n".join(lines))
def _resolve_example_path(path: str) -> Path:
"""Resolve user input to a file inside this example directory only."""
candidate = Path(path)
if candidate.is_absolute():
raise ValueError("read_notes only accepts paths relative to the example")
resolved = (_EXAMPLE_DIR / candidate).resolve()
if not resolved.is_relative_to(_EXAMPLE_DIR):
raise ValueError("read_notes path must stay inside the example directory")
return resolved
registry = [read_notes, extract_report, render_markdown_report]
+12 -4
View File
@@ -1,24 +1,32 @@
from __future__ import annotations
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
def markdown_links(text: str) -> set[str]:
"""Return inline Markdown link hrefs from docs smoke-test files."""
return set(re.findall(r"(?<!!)\[[^\]]+\]\(([^)]+)\)", text))
def test_big_doc_links_case_study_and_evidence_index() -> None:
doc = (ROOT / "docs" / "add" / "system-design-implementation.md").read_text(
encoding="utf-8"
)
links = markdown_links(doc)
assert "examples/report_workflow" in doc
assert "docs/add/evidence-index.md" in doc or "evidence-index.md" in doc
assert "evidence-index.md" in links
assert any(link.startswith("../../examples/report_workflow") for link in links)
def test_project_map_links_big_doc() -> None:
project_map = (ROOT / "docs" / "project_map.md").read_text(encoding="utf-8")
links = markdown_links(project_map)
assert "system-design-implementation.md" in project_map
assert "evidence-index.md" in project_map
assert any(link.endswith("system-design-implementation.md") for link in links)
assert any(link.endswith("evidence-index.md") for link in links)
def test_big_doc_keeps_mcp_as_source_family() -> None:
@@ -4,12 +4,44 @@ from pathlib import Path
import pytest
from examples.report_workflow.ops import (
ActionItem,
ExtractInput,
MarkdownInput,
ReadInput,
ReportOutput,
_extract_report,
_read_notes,
_render_markdown_report,
)
from wf_config import load_workflow_config
from wf_server.config import build_workflow_server_from_workflow_config
EXAMPLE_DIR = Path(__file__).resolve().parents[2] / "examples" / "report_workflow"
def test_report_workflow_read_notes_rejects_paths_outside_example() -> None:
with pytest.raises(ValueError, match="inside the example"):
_read_notes(ReadInput(path="../pyproject.toml"))
def test_report_workflow_markdown_renderer_round_trips_through_extractor() -> None:
report = ReportOutput(
title="Weekly Project Update",
summary="Demo summary.",
action_items=[
ActionItem(owner="Alice", task="Prepare demo config", due="Friday")
],
risks=["Quota is low"],
followups=["Render Markdown"],
)
rendered = _render_markdown_report(MarkdownInput(report=report))
extracted = _extract_report(ExtractInput(text=rendered.markdown))
assert extracted == report
@pytest.mark.asyncio
async def test_report_workflow_python_source_loads_and_calls_capability(
tmp_path,