3.9 KiB
3.9 KiB
T3.2 — WorkflowFormat trait + YAML and JSON
| Field | Value |
|---|---|
| Phase | P3 — Workflow as data |
| Size | M — 1 to 3 days |
| Status | Not started |
| Flags | — |
| Spec | inlined below |
| Blocks | — |
Goal
Formats are plugins. Ship YAML and JSON; prove a third format needs no kernel change.
Facts (inlined — no spec read needed)
pub trait WorkflowFormat: Send + Sync {
fn extensions(&self) -> &[&str];
fn parse(&self, src: &[u8]) -> Result<WorkflowDef, ParseError>;
}
- A user wanting a DSL, Starlark, or a database row implements the trait.
- Validation and canonicalization live in the kernel and run on the IR (T3.1, T3.3), so a new format inherits every check without reimplementing one. A format that does its own validation has forked the rules.
- Because the version hash is over the canonical IR, the same workflow in two
formats must produce the same
WorkflowVersion— that equality is the test that the boundary is in the right place. - Defaults ship working; every default is a port. A user who wants built-in behaviour writes no code; a user who wants their own writes an impl, not a fork.
Steps
- Define the trait exactly as above.
parsereturns the IR and nothing else — no side effects, no registration, no validation. - Implement
YamlFormatandJsonFormatoverserde. Both target the same IR types from T3.1. - Build the format registry keyed by extension. Resolution is by extension, with a clear error when two formats claim the same one.
- Make
ParseErrorcarry position information where the format supplies it — this is the only error type that legitimately knows about source text. - Write the same workflow in YAML and in JSON; assert equal
WorkflowVersion. - In a separate test crate, implement a third trivial format (for example
TOML or a line-based DSL) against the public API only. If it needs anything
pub(crate), the trait is short — fix the trait, not the test.
Acceptance
- The same workflow expressed in both formats produces the same
WorkflowVersion. - A third format added in a test crate needs no kernel change.
Verify
Harness: a separate test crate outside the workspace depending only on the published API — that crate's existence is the deliverable, not a convenience.
Integration test — tests/it_format_equivalence.rs plus
third-format-crate/:
- Express one non-trivial workflow (all five
StepKinds) in YAML and in JSON. - Parse both; assert identical
WorkflowVersion. - Assert the parsed IRs are byte-identical after canonicalization.
- In the external crate, implement a third format (TOML or a line DSL) against
the public API only. Build it. If it needs any
pub(crate)item, the trait is short — that is a finding, not a test workaround. - Register the third format and run the same validation suite (T3.3) against it; assert it inherits every check with no new code.
- Extension collision: register two formats claiming
.yaml; assert a clear error rather than last-one-wins.
Command: cargo test -p workflow format_equivalence && cargo test --manifest-path third-format-crate/Cargo.toml
False pass:
- The third-format crate living inside the workspace, where it can reach internals. It then passes while the public API is unusable — which is the entire thing being tested.
- Step 5 omitted: a format that does its own validation passes steps 1–4 and has quietly forked the rules.
Traps
- Format-specific validation creeping into
parse. Two formats then disagree about what is legal. - Leaking
serdetypes into the IR, which makes the third-format test need a serde dependency it should not have.
Background (not required to do this task): rust-agentic-sys.md §1, §4.2 · rust-agentic-task.md