docs: archive completed implementation plans
This commit is contained in:
@@ -0,0 +1,311 @@
|
||||
# Canonical TOML Path Strings Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Make canonical TOML-key strings the advertised and serialized representation for workflow paths while retaining structured-object input compatibility for existing stored data.
|
||||
|
||||
**Architecture:** Keep `GraphSourcePath`, `StatePath`, and `LocalPath` as structured immutable values. Move TOML-key parsing into `wf_core.paths`, add one canonical formatter there, and make every Pydantic path serializer emit strings. `wf_authoring` delegates to the core parser instead of owning a second grammar.
|
||||
|
||||
**Tech Stack:** Python 3.14, stdlib `tomllib`, Pydantic v2 core schemas, pytest, Ruff, basedpyright.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Define The Core TOML-Key Grammar
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/wf_core/paths.py`
|
||||
- Test: `tests/core/test_path_values.py`
|
||||
|
||||
- [x] **Step 1: Write failing parser and formatter tests**
|
||||
|
||||
Add tests proving quoted segments round-trip and roots remain typed:
|
||||
|
||||
```python
|
||||
def test_toml_path_strings_round_trip_literal_segments() -> None:
|
||||
source = GraphSourcePath.parse('input."customer.name"."display name"')
|
||||
target = StatePath.parse('state."report.title"')
|
||||
local = LocalPath.parse('payload."raw.value"')
|
||||
|
||||
assert source.parts == ("customer.name", "display name")
|
||||
assert target.parts == ("report.title",)
|
||||
assert local.parts == ("payload", "raw.value")
|
||||
assert str(source) == 'input."customer.name"."display name"'
|
||||
assert str(target) == 'state."report.title"'
|
||||
assert str(local) == 'payload."raw.value"'
|
||||
```
|
||||
|
||||
Also test `LocalPath.parse(".")`, bare keys, malformed TOML, invalid roots, and empty writable state paths.
|
||||
|
||||
- [x] **Step 2: Run the focused test and verify RED**
|
||||
|
||||
Run: `uv run pytest tests/core/test_path_values.py -q`
|
||||
|
||||
Expected: quoted full paths fail because current core parsing uses `str.split(".")`.
|
||||
|
||||
- [x] **Step 3: Implement shared parsing and formatting**
|
||||
|
||||
In `src/wf_core/paths.py`, add the shared implementation:
|
||||
|
||||
```python
|
||||
_BARE_TOML_KEY = re.compile(r"^[A-Za-z0-9_-]+$")
|
||||
|
||||
|
||||
def parse_toml_path_segments(expr: str) -> tuple[str, ...]:
|
||||
"""Parse a TOML key expression into literal path segments."""
|
||||
try:
|
||||
parsed = tomllib.loads(f"{expr} = true")
|
||||
except tomllib.TOMLDecodeError as exc:
|
||||
raise PathResolutionError(
|
||||
f"invalid TOML path {expr!r}; quote path segments containing dots or spaces"
|
||||
) from exc
|
||||
|
||||
parts: list[str] = []
|
||||
current: object = parsed
|
||||
while isinstance(current, dict):
|
||||
if len(current) != 1:
|
||||
raise PathResolutionError(f"invalid TOML path {expr!r}")
|
||||
key, current = next(iter(current.items()))
|
||||
parts.append(_validate_segment(key, path_kind="TOML path"))
|
||||
if current is not True or not parts:
|
||||
raise PathResolutionError(f"invalid TOML path {expr!r}")
|
||||
return tuple(parts)
|
||||
|
||||
|
||||
def format_toml_path_segments(parts: tuple[str, ...]) -> str:
|
||||
"""Format literal segments as one canonical TOML key expression."""
|
||||
if not parts:
|
||||
raise PathResolutionError("cannot format an empty TOML path")
|
||||
return ".".join(
|
||||
part
|
||||
if _BARE_TOML_KEY.fullmatch(part)
|
||||
else json.dumps(part, ensure_ascii=False)
|
||||
for part in parts
|
||||
)
|
||||
```
|
||||
|
||||
Use `tomllib.loads(f"{expr} = true")` for parsing. The formatter emits bare
|
||||
TOML keys when legal and quoted TOML basic strings otherwise. Keep the local
|
||||
root marker `.` as a special complete path, not a TOML key expression.
|
||||
|
||||
Update:
|
||||
|
||||
```python
|
||||
GraphSourcePath.parse(raw)
|
||||
GraphSourcePath.__str__()
|
||||
StatePath.parse(raw)
|
||||
StatePath.__str__()
|
||||
LocalPath.parse(raw)
|
||||
LocalPath.__str__()
|
||||
```
|
||||
|
||||
`GraphSourcePath.parse` parses the whole expression, then treats the first
|
||||
segment as the graph root. `StatePath` requires root `state` plus at least one
|
||||
remaining segment. `LocalPath` has no serialized `local.` prefix.
|
||||
|
||||
- [x] **Step 4: Run focused core tests**
|
||||
|
||||
Run: `uv run pytest tests/core/test_path_values.py tests/core/test_nested_state_paths.py -q`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [x] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/wf_core/paths.py tests/core/test_path_values.py
|
||||
git commit -m "feat: define canonical TOML workflow paths"
|
||||
```
|
||||
|
||||
### Task 2: Serialize Paths As Strings And Advertise Strings In JSON Schema
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/wf_core/paths.py`
|
||||
- Modify: `tests/core/test_path_values.py`
|
||||
- Modify: `tests/core/test_canonical_node_bindings.py`
|
||||
|
||||
- [x] **Step 1: Write failing Pydantic projection tests**
|
||||
|
||||
Extend the existing Pydantic path payload test:
|
||||
|
||||
```python
|
||||
def test_path_models_serialize_strings_but_accept_structural_compat() -> None:
|
||||
payload = PathPayload.model_validate(
|
||||
{
|
||||
"source": {"root": "input", "parts": ["user.name"]},
|
||||
"target": {"root": "state", "parts": ["person name"]},
|
||||
"local": {"root": "local", "parts": ["payload.text"]},
|
||||
}
|
||||
)
|
||||
|
||||
assert payload.model_dump(mode="json") == {
|
||||
"source": 'input."user.name"',
|
||||
"target": 'state."person name"',
|
||||
"local": '"payload.text"',
|
||||
}
|
||||
assert PathPayload.model_json_schema()["properties"]["source"]["type"] == "string"
|
||||
```
|
||||
|
||||
- [x] **Step 2: Run tests and verify RED**
|
||||
|
||||
Run: `uv run pytest tests/core/test_path_values.py tests/core/test_canonical_node_bindings.py -q`
|
||||
|
||||
Expected: current serializers emit `{root, parts}` objects.
|
||||
|
||||
- [x] **Step 3: Change serializers and JSON schemas**
|
||||
|
||||
Make each path `_serialize` return `str(value)`. Replace the structural
|
||||
`_path_json_schema` with a string schema whose description documents TOML-key
|
||||
quoting. Do not include the compatibility object in generated JSON Schema;
|
||||
validators continue accepting it so old persisted records remain readable.
|
||||
|
||||
Keep a comment at the validator seam:
|
||||
|
||||
```python
|
||||
# Structural objects remain input-only compatibility for persisted records.
|
||||
# New schemas and serializers expose the canonical TOML-key string form.
|
||||
```
|
||||
|
||||
- [x] **Step 4: Update exact serialized binding expectations**
|
||||
|
||||
Adjust tests that assert JSON payloads to expect strings such as
|
||||
`input.message`, `state.echoed`, and `.`. Keep field-level assertions rather
|
||||
than replacing whole large snapshots.
|
||||
|
||||
- [x] **Step 5: Run core serialization tests**
|
||||
|
||||
Run: `uv run pytest tests/core/test_path_values.py tests/core/test_canonical_node_bindings.py tests/core/test_run_codec.py -q`
|
||||
|
||||
Expected: PASS, including decoding old structural path objects.
|
||||
|
||||
- [x] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add src/wf_core/paths.py tests/core/test_path_values.py tests/core/test_canonical_node_bindings.py
|
||||
git commit -m "feat: serialize canonical workflow path strings"
|
||||
```
|
||||
|
||||
### Task 3: Remove The Duplicate Authoring Parser
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/wf_authoring/dsl/path_inputs.py`
|
||||
- Modify: `tests/authoring/test_path_inputs.py`
|
||||
|
||||
- [x] **Step 1: Add delegation coverage**
|
||||
|
||||
Add a test proving full rooted strings and explicit-root expressions resolve to
|
||||
the same structured value:
|
||||
|
||||
```python
|
||||
def test_authoring_paths_share_core_toml_grammar() -> None:
|
||||
assert coerce_graph_path('state."person.name"') == GraphSourcePath(
|
||||
"state", ("person.name",)
|
||||
)
|
||||
assert coerce_graph_path('"person.name"', root="state") == GraphSourcePath(
|
||||
"state", ("person.name",)
|
||||
)
|
||||
```
|
||||
|
||||
- [x] **Step 2: Replace `_parse_toml_key_expr`**
|
||||
|
||||
Delete the local `tomllib` parser and import
|
||||
`parse_toml_path_segments` from `wf_core.paths`. Preserve iterable and
|
||||
structural-object coercion behavior for Python callers.
|
||||
|
||||
- [x] **Step 3: Run authoring tests**
|
||||
|
||||
Run: `uv run pytest tests/authoring/test_path_inputs.py tests/authoring/test_builder.py tests/authoring/test_conditions.py -q`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [x] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/wf_authoring/dsl/path_inputs.py tests/authoring/test_path_inputs.py
|
||||
git commit -m "refactor: share workflow path grammar"
|
||||
```
|
||||
|
||||
### Task 4: Update Draft, Transport, And Compatibility Coverage
|
||||
|
||||
**Files:**
|
||||
- Modify: `tests/wf_api/test_drafts_service.py`
|
||||
- Modify: `tests/wf_transport_rpc_http/test_app.py`
|
||||
- Modify: `tests/wf_transport_rpc_http/test_client.py`
|
||||
- Modify: `tests/wf_mcp/workflow_surface/test_drafts.py`
|
||||
|
||||
- [x] **Step 1: Add one stored-draft compatibility regression**
|
||||
|
||||
Create a workspace from a draft containing structural path objects, retrieve or
|
||||
patch it, and assert the next serialized draft uses canonical strings while
|
||||
preserving the same path values.
|
||||
|
||||
- [x] **Step 2: Update focused transport assertions**
|
||||
|
||||
Change only assertions for serialized path fields. RPC and MCP requests should
|
||||
advertise and return strings; input model tests must still accept structural
|
||||
objects.
|
||||
|
||||
- [x] **Step 3: Run affected suites**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_api/test_drafts_service.py tests/wf_transport_rpc_http/test_app.py tests/wf_transport_rpc_http/test_client.py tests/wf_mcp/workflow_surface/test_drafts.py -q
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [x] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add tests/wf_api/test_drafts_service.py tests/wf_transport_rpc_http/test_app.py tests/wf_transport_rpc_http/test_client.py tests/wf_mcp/workflow_surface/test_drafts.py
|
||||
git commit -m "test: cover canonical path transport compatibility"
|
||||
```
|
||||
|
||||
### Task 5: Documentation And Final Verification
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/workflow_drafts.md`
|
||||
- Modify: `docs/wf_authoring_control_flow.md`
|
||||
- Modify: `skills/wf-cli/SKILL.md`
|
||||
- Modify: `skills/wf-workflow/references/draft-workspaces.md`
|
||||
- Modify: `skills/wf-workflow/references/direct-plan-import.md`
|
||||
- Modify: `docs/current_roadmap.md`
|
||||
- Modify: `docs/superpowers/specs/2026-06-27-draft-semantic-authoring-boundary.md`
|
||||
- Move after completion: `docs/superpowers/plans/2026-06-27-canonical-toml-path-strings.md` -> `docs/historical/superpowers/plans/2026-06-27-canonical-toml-path-strings.md`
|
||||
|
||||
- [x] **Step 1: Replace structural path examples**
|
||||
|
||||
Use canonical examples:
|
||||
|
||||
```json
|
||||
{"path":"input.text","target":"text"}
|
||||
{"source":"result","target":"state.result"}
|
||||
```
|
||||
|
||||
Document TOML quoting with `state."field.with.dot"`. State that structural
|
||||
objects are input-only compatibility and must not be generated by agents.
|
||||
|
||||
- [x] **Step 2: Record implementation status**
|
||||
|
||||
Mark the canonical-path section implemented in the design spec and add a short
|
||||
completed roadmap entry.
|
||||
|
||||
- [x] **Step 3: Run verification**
|
||||
|
||||
```bash
|
||||
uv run pytest tests/core/test_path_values.py tests/core/test_canonical_node_bindings.py tests/authoring/test_path_inputs.py tests/authoring/test_builder.py tests/wf_api/test_drafts_service.py tests/wf_transport_rpc_http/test_app.py tests/wf_transport_rpc_http/test_client.py tests/wf_mcp/workflow_surface/test_drafts.py -q
|
||||
uv run ruff check
|
||||
uv run ruff format --check
|
||||
uv run basedpyright --level error
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Expected: all tests pass, Ruff is clean, basedpyright reports zero errors, and
|
||||
`git diff --check` reports no whitespace errors.
|
||||
|
||||
- [x] **Step 4: Archive and commit the plan**
|
||||
|
||||
```bash
|
||||
git mv docs/superpowers/plans/2026-06-27-canonical-toml-path-strings.md docs/historical/superpowers/plans/2026-06-27-canonical-toml-path-strings.md
|
||||
git add docs skills
|
||||
git commit -m "docs: document canonical workflow path strings"
|
||||
```
|
||||
Reference in New Issue
Block a user