ADR-0003 — TOML for humans, JSON for agents, one canonical schema
- Status: Accepted
- Date: 2026-05-11
- Related specs: 03-pipelines.md
Context
Section titled “Context”Hakiri’s manifest needs to be authored by two distinct populations:
- Humans edit by hand, want comments inline (“every 15m because the GitHub rate limit is per-token-per-hour”), and care about diffability.
- LLM agents emit and edit by tool call, want a strict schema for validation, and reliably produce flawless JSON but occasionally lose a
[[pipeline]]table header when round-tripping TOML.
A single-format choice forces a compromise on one side. YAML — the third common option — has indent footguns that bite agents (string-vs-number quoting, anchors, multi-document streams) and humans alike.
Decision
Section titled “Decision”Hakiri accepts both TOML (hakiri.toml, pipelines/*.toml) and JSON (pipelines/*.json) manifests. Both deserialize into the same Rust PipelineSpec and validate against the same JSON Schema, which is itself derived from the Rust type via schemars.
Pipelines merge by id across all manifest files. Duplicate ids across files are a hard error with explicit diagnostics. The MCP server’s pipeline.create defaults to JSON; pipeline.edit preserves the existing file’s format; pipeline.convert round-trips between them losslessly (except for TOML comments, which JSON cannot represent).
Consequences
Section titled “Consequences”Positive
- Each authoring population uses the syntax that suits it without compromising the other.
- One source of truth in Rust (
PipelineSpec) —serde_tomlandserde_jsonare both off-the-shelf;schemarsgenerates the JSON Schema automatically. - Editor tooling works for both: JSON gets stock JSON Schema LSP support (VS Code, JetBrains, Helix) via the
$schemaline; TOML getstaplofortaplo.toml-driven validation. - Agent-authored pipelines land in
pipelines/*.jsonwith$schemaset, so they’re validated at write time, not first run.
Negative
- Two deserialization paths to maintain. Mitigated by the fact that both target the same Rust type, so there is no parallel evolution.
- One merger to maintain. The merge logic is small (group by id, error on collision) but it is non-trivial code that wouldn’t exist with a single-format choice.
- TOML comments do not survive a
pipeline.convert <id> --to json. Documented; not silently dropped.
Neutral
- We will never accept YAML. The decision rules it out explicitly so reviewers don’t relitigate.
Alternatives considered
Section titled “Alternatives considered”TOML only. Loses JSON Schema’s validation surface at the file level — taplo’s TOML+JSON Schema support exists but lags JSON’s tooling, and agents occasionally drop table headers when round-tripping. Editor experience for agents is worse.
JSON only. Humans lose inline comments (closing braces all look the same, diffs grow noisier, “why this schedule” context goes elsewhere). For a config a team co-edits over years, that loss is real.
YAML. Indent footguns, ambiguous types (the “Norway problem”), and a long history of being misparsed by both humans and agents. We need less ambiguity, not more.
HCL / Jsonnet / Dhall. Powerful but require teaching every reader a new language. Hakiri’s manifest is data, not a program; templating belongs outside the manifest (env interpolation is enough).
References
Section titled “References”schemarstoml_edit— preserves comments and field ordering on TOML edits- The Norway problem (YAML)
- Specs: 03-pipelines.md