Files
lda-wf/docs/superpowers/specs/2026-06-22-wf-schema-command-design.md
T

312 lines
10 KiB
Markdown

# `wf schema` Command Design
## Status
Approved design for implementation planning.
## Purpose
`wf schema` gives human and machine clients a public discovery surface for the
workflow document models accepted by the product. It should cover draft, raw
plan, core workflow, and referenced component shapes without requiring agents
to inspect tests or implementation code.
The command has two output levels:
- a compact machine-readable outline by default;
- a complete valid JSON Schema document with `--verbose`.
## Goals
1. Cover every schema definition reachable from workflow draft, raw plan, and
core workflow roots.
2. Keep default output compact enough for agent context windows.
3. Keep all output valid JSON.
4. Make verbose output a valid, self-contained JSON Schema document.
5. Provide discoverable aliases for the common root models.
6. Fail clearly and nonzero for unknown schema names.
7. Avoid adding runtime dependencies for trivial in-process caching.
## Non-Goals
- Building a general-purpose JSON Schema browser.
- Searching arbitrary source-code models outside the workflow document domain.
- Producing Python source declarations or generated Pydantic models.
- Replacing command-specific help, validation diagnostics, or workflow docs.
- Guaranteeing that compact outlines preserve every JSON Schema keyword.
## Command Surface
```text
wf schema
wf schema list
wf schema draft
wf schema raw
wf schema core
wf schema WorkflowDraft
wf schema RawWorkflowPlan
wf schema NodeUse
wf schema InputPathBinding
wf schema raw --verbose
wf schema NodeUse --verbose
```
`wf schema` with no argument is equivalent to `wf schema list`.
The command remains a single Typer command with an optional positional name.
`draft`, `raw`, and `core` are aliases, not subcommands.
## Root Models And Aliases
| Alias | Canonical model | Purpose |
| --- | --- | --- |
| `draft` | `WorkflowDraft` | Patch-friendly draft workspace document |
| `raw` | `RawWorkflowPlan` | Raw graph accepted by artifact creation |
| `core` | `Workflow` | Canonical core workflow graph |
Canonical names are also accepted directly. Definitions exposed through the
combined root schemas are queryable by their exact canonical names.
## Schema Catalog
The implementation builds one cached catalog from:
- `TypeAdapter(WorkflowDraft).json_schema()`;
- `TypeAdapter(RawWorkflowPlan).json_schema()`;
- `TypeAdapter(Workflow).json_schema()`.
The catalog records:
- canonical root schemas;
- aliases;
- every reachable `$defs` entry;
- description/title metadata;
- which roots reference each definition.
Use `functools.cache` for catalog construction. The CLI process is short-lived;
a TTL cache and the `cachetools` dependency provide no useful behavior.
Pydantic's `TypeAdapter.json_schema()` remains the authority for schema
generation. Do not reimplement model-to-JSON-Schema conversion, discriminated
unions, aliases, defaults, validation constraints, or reference construction.
Use the existing `jsonschema` package to validate emitted full schemas. Custom
code is limited to catalog indexing, collision checks, and the explicitly
non-validating compact presentation layer.
If two roots expose different definitions with the same canonical name, catalog
construction must fail with a clear internal error rather than silently choose
one. Structurally identical definitions may be deduplicated.
## List Output
`wf schema` and `wf schema list` emit JSON:
```json
{
"schemas": [
{
"name": "NodeUse",
"aliases": [],
"kind": "definition",
"description": "Concrete use of a reusable node definition."
},
{
"name": "WorkflowDraft",
"aliases": ["draft"],
"kind": "root",
"description": "Patch-friendly JSON authoring document."
}
]
}
```
Entries are sorted by canonical name. Search/filtering is deferred until real
catalog size or usage demonstrates a need.
## Compact Outline Output
The default named-schema output is a JSON **schema outline**, not a JSON Schema
document. It must label itself accordingly and must not emit dangling `$ref`
values.
Example:
```json
{
"name": "RawWorkflowPlan",
"kind": "schema_outline",
"type": "object",
"description": "Raw authoring plan using core graph models.",
"required": [
"name",
"input_schema",
"state_schema",
"output_schema",
"start",
"nodes",
"edges"
],
"properties": {
"name": {"type": "string"},
"start": {"type": "string"},
"nodes": {
"type": "array",
"items": {
"one_of": [
"NodeUse",
"SubgraphNode",
"ConditionNode",
"ForeachNode",
"EndNode",
"InterruptNode"
]
}
},
"edges": {"type": "array", "items": "Edge"}
},
"related": ["Edge", "NodeUse", "OutputBinding"],
"full_schema_command": "wf schema raw --verbose"
}
```
### Outline Projection Rules
- Preserve `name`, `title`, `description`, object `required`, defaults, enums,
constants, and basic validation bounds when present.
- Preserve object property names.
- Convert local `$ref` values to canonical definition-name strings.
- Convert `oneOf` reference unions to `one_of` and `anyOf` reference unions to
`any_of` when branches are named references.
- Preserve simple inline primitive unions in compact JSON form.
- For arrays, summarize the item schema recursively.
- Add a sorted `related` list containing definitions referenced by the outline.
- Do not recursively inline referenced definitions; callers can query them by
name.
- Do not claim the outline is directly accepted as a validation schema.
The projection should be deterministic so snapshots and prompt provenance are
stable across runs when source models do not change.
## Verbose JSON Schema Output
`wf schema <name> --verbose` emits a complete, self-contained JSON Schema
document through the existing `wf_cli.io.emit_json` helper.
For root aliases/canonical root names, emit the complete Pydantic-generated root
schema with its `$defs` table.
For a component definition such as `NodeUse`, emit a valid root document:
```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$ref": "#/$defs/NodeUse",
"$defs": {
"NodeUse": {"type": "object", "properties": {"node": {"type": "string"}}},
"InputPathBinding": {"type": "object", "properties": {"path": {"type": "string"}}},
"InputValueBinding": {"type": "object", "properties": {"value": true}},
"OutputBinding": {"type": "object", "properties": {"to": {"type": "string"}}}
}
}
```
The short `$defs` bodies above are representative excerpts, not complete
runtime output. The real `$defs` table contains the full combined
Pydantic-generated definition catalog. Do not hand-roll transitive reference
pruning or JSON Schema reference resolution in the first implementation. The
larger verbose payload is an acceptable tradeoff for correctness; `--verbose`
is explicitly the unbounded form.
All verbose documents must pass `Draft202012Validator.check_schema()` and a
validator-backed local-reference resolution test.
## Output And Errors
- Use `emit_json`; never pass dictionaries directly to `typer.echo`.
- Unknown names raise `typer.BadParameter` and exit nonzero.
- Unknown-name messages include close canonical/alias matches when available,
using the standard library `difflib.get_close_matches`.
- Missing names list the catalog rather than showing an empty command group.
- Internal catalog collisions or malformed generated schemas fail loudly with
actionable errors.
- `--verbose` applies to both aliases and canonical names.
Example error:
```text
Invalid value: unknown schema 'Node'. Did you mean 'NodeUse'?
```
## Help Text
Help must describe the real single-command shape:
```text
Usage: wf schema [OPTIONS] [NAME]
Print a compact workflow schema outline, or a full JSON Schema with --verbose.
Use `wf schema` or `wf schema list` to discover available names.
Common aliases: draft, raw, core.
```
Do not call aliases convenience subcommands.
## Skills And Documentation
Update user-facing agent instructions so they use public discovery surfaces in
this order:
1. `wf schema` to list available workflow document shapes;
2. `wf schema draft` or `wf schema raw` for compact guidance;
3. `wf schema <definition>` for component details;
4. `--verbose` only when a complete validation schema is necessary;
5. workflow validation commands to check an authored document.
Remove the stale statement that `wf schema` is an empty WIP group. Keep test and
implementation file paths out of user-facing skills.
## Tests
Focused CLI tests must cover:
- `wf schema` and `wf schema list` return the same sorted catalog;
- catalog output is valid JSON and includes roots, aliases, and components;
- aliases and canonical root names resolve to the same model;
- compact output is valid JSON and contains no `$ref` keys;
- compact references are represented by queryable canonical names;
- verbose root output passes JSON Schema validation;
- verbose component output has no unresolved local `$ref` values;
- `--verbose` works for aliases and canonical component names;
- unknown names exit nonzero and offer a close-match suggestion;
- help text describes aliases rather than subcommands;
- the removed WIP help assertions are replaced with behavioral tests.
Run focused verification against the schema command and existing CLI app tests,
then run Ruff and basedpyright on modified files.
## Migration
The current uncommitted prototype is replaced rather than preserved:
- remove `cachetools` from runtime dependencies and the lockfile;
- remove TTL caches;
- replace `_sonset_sandstorm` with explicit catalog helpers;
- replace Python-repr output with JSON;
- replace detached `$defs` fragments with compact outlines;
- update stale CLI tests and skills.
No compatibility behavior is required because the previous `wf schema` surface
was explicitly an empty work-in-progress group and the prototype has not been
released as a documented contract.
## Acceptance Criteria
- Agents can discover all supported workflow schema names without reading code.
- Default output is compact, deterministic, valid JSON, and has no dangling
references.
- `--verbose` output is valid, self-contained JSON Schema.
- Unknown names fail nonzero.
- No new runtime dependency is needed.
- Focused CLI tests, Ruff, and basedpyright pass.
- User-facing skills direct agents to `wf schema` instead of tests or source.