Publish JSON Schema for the YAML dialect — zero-install editor validation
Part of the "author sees errors while writing, not after run" push (see the L-slice discussion). This is the cheapest of that family: no editor extension needed. VS Code, Cursor, and JetBrains all speak JSON Schema for YAML files natively — a published flows.schema.json gives every author real-time squiggles on unknown headers, invalid verification blocks, wrong step-type discriminants, malformed input: selectors, and every other shape error the SDK already refuses at flows check.
Why this first (over LSP)
Editor validation for YAML flows has four possible shapes, in ascending cost:
| Path |
Zero-install for user? |
Editor coverage |
Effort |
| JSON Schema (this slice) |
Yes (VS Code / Cursor via the built-in YAML extension; JetBrains via built-in schema support; Neovim via yaml-language-server) |
Every structural shape refusal flows check produces |
Small |
| tsserver plugin |
For TS users only |
TS dialect only, not YAML |
Mid |
flows check --watch |
Terminal only |
Everything check catches, but no inline squiggles |
Small |
| Full LSP |
No — one extension per IDE family |
Universal, richest |
Large |
JSON Schema is the widest reach for the smallest lift, and it costs zero editor maintenance because the editors already ship the plumbing.
Scope
-
Generate flows.schema.json from packages/sdk/src/spec.ts — one source of truth. The generator walks the exported TS types (FlowSpec, StepSpec, DeterministicStepSpec, LlmStepSpec, AgentStepSpec, NamedAgentSpec, OutputVerificationSpec, PermissionsSpec, AgentSurfaces, RecoveryMode, the input: selector shape, etc.) and emits the corresponding JSON Schema (2020-12 draft to match the kernel's bounded-schema rule).
- Use
ts-json-schema-generator or a hand-rolled walker over the TypeScript AST — whichever produces smaller, more readable schema. Prefer no runtime deps in the generator.
- Emit
title, description, and examples fields per node so hover-docs are useful in editors.
- The discriminant on
StepSpec.type (deterministic / llm / agent) becomes a JSON Schema oneOf with const discriminators — every editor understands that.
-
Publish as @relayflows/schema npm package plus a stable URL under GitHub Pages (or the flows website when it exists). Two consumption paths:
- Package:
npm i -D @relayflows/schema, then reference ./node_modules/@relayflows/schema/flows.schema.json from .vscode/settings.json.
- URL:
https://schema.relayflows.dev/v0.1/flows.schema.json (final URL TBD — pick something under the flows GH Pages if that's cheaper first).
- The schema self-declares
$id pointing at the stable URL so refs from other schemas resolve.
-
Auto-inject on new flows. When flows check first sees a .flow.yaml file with no # yaml-language-server: comment, print a one-line hint in the report telling the author to add:
# yaml-language-server: $schema=https://schema.relayflows.dev/v0.1/flows.schema.json
Do not auto-mutate the file — hint only. flows.schema.json uses the same URL, so registering it once in settings.json also works for every flow with no comment.
-
Docs. Add docs/EDITOR.md (or an equivalent section in an existing doc) covering:
- VS Code / Cursor: add to
.vscode/settings.json under yaml.schemas.
- JetBrains (WebStorm, IDEA, PyCharm, GoLand): Settings → Languages & Frameworks → Schemas and DTDs → JSON Schema Mappings.
- Neovim +
yaml-language-server: LSP config snippet.
- The
# yaml-language-server: inline comment path (works everywhere).
-
Parity test. A single test asserts the schema and flows check agree on every testdata/*.flow.yaml — schema-valid ⇔ check-accepts. That's the promise the schema makes to authors, and drift is the failure mode most likely to erode trust.
-
CI publish. .github/workflows/schema-publish.yml on release tags: run the generator, publish @relayflows/schema, and upload flows.schema.json to a stable GH Pages path with versioning.
Not in scope (deferred)
- TypeScript dialect authoring checks (that's slice L2 —
@relayflows/ts-plugin).
- Full LSP with hover-docs on helper verbs,
use: resolution, live flows check diagnostics (L4 later, only if L1 + L2 + L3 leave a real gap).
- Provider-specific helper YAML verbs (
slack:, github:) — those come with each helper's plugin and add their own schema fragments.
- Schema-driven autocomplete on
input: { step: X, path: [...] } selector target keys — that requires cross-file/whole-project analysis and belongs to the tsserver-plugin/LSP path.
- Semantic gates (slice P).
Acceptance evidence
Files to touch
scripts/generate-json-schema.mjs — new. Walks packages/sdk/src/spec.ts types, emits flows.schema.json.
packages/schema/ — new package: package.json (name @relayflows/schema, main/exports pointing at the JSON file), flows.schema.json (generated + committed for zero-build consumption), README.md, tsconfig.json (types only, if any).
packages/schema/tests/parity.test.ts — schema/check parity test over testdata/*.flow.yaml.
.github/workflows/schema-publish.yml — release-tag-triggered publish job.
docs/EDITOR.md (new) — VS Code / JetBrains / Neovim registration guide.
packages/sdk/src/cli/check.ts — one-line hint for missing # yaml-language-server: header.
Push-only PR pattern (finn-mini)
The codex agent pushes the branch and DMs me L1 pushed sha=<HEAD>; I open the PR from the lead machine because gh returns 401 on fleet nodes.
Depends on
Nothing shipped. Compatible with every merged v2 slice; runs alongside slice L2 (tsserver plugin, TS dialect) and L3 (flows check --watch).
Follow-ups filed separately
- L2 —
@relayflows/ts-plugin for TS-dialect cross-file checks.
- L3 —
flows check --watch for terminal save-time feedback.
- L4 — full LSP, only if the other three leave a real gap.
Publish JSON Schema for the YAML dialect — zero-install editor validation
Part of the "author sees errors while writing, not after run" push (see the L-slice discussion). This is the cheapest of that family: no editor extension needed. VS Code, Cursor, and JetBrains all speak JSON Schema for YAML files natively — a published
flows.schema.jsongives every author real-time squiggles on unknown headers, invalidverificationblocks, wrong step-type discriminants, malformedinput:selectors, and every other shape error the SDK already refuses atflows check.Why this first (over LSP)
Editor validation for YAML flows has four possible shapes, in ascending cost:
yaml-language-server)flows checkproducesflows check --watchJSON Schema is the widest reach for the smallest lift, and it costs zero editor maintenance because the editors already ship the plumbing.
Scope
Generate
flows.schema.jsonfrompackages/sdk/src/spec.ts— one source of truth. The generator walks the exported TS types (FlowSpec,StepSpec,DeterministicStepSpec,LlmStepSpec,AgentStepSpec,NamedAgentSpec,OutputVerificationSpec,PermissionsSpec,AgentSurfaces,RecoveryMode, theinput:selector shape, etc.) and emits the corresponding JSON Schema (2020-12 draft to match the kernel's bounded-schema rule).ts-json-schema-generatoror a hand-rolled walker over the TypeScript AST — whichever produces smaller, more readable schema. Prefer no runtime deps in the generator.title,description, andexamplesfields per node so hover-docs are useful in editors.StepSpec.type(deterministic/llm/agent) becomes a JSON SchemaoneOfwithconstdiscriminators — every editor understands that.Publish as
@relayflows/schemanpm package plus a stable URL under GitHub Pages (or the flows website when it exists). Two consumption paths:npm i -D @relayflows/schema, then reference./node_modules/@relayflows/schema/flows.schema.jsonfrom.vscode/settings.json.https://schema.relayflows.dev/v0.1/flows.schema.json(final URL TBD — pick something under the flows GH Pages if that's cheaper first).$idpointing at the stable URL so refs from other schemas resolve.Auto-inject on new flows. When
flows checkfirst sees a.flow.yamlfile with no# yaml-language-server:comment, print a one-line hint in the report telling the author to add:# yaml-language-server: $schema=https://schema.relayflows.dev/v0.1/flows.schema.jsonDo not auto-mutate the file — hint only.
flows.schema.jsonuses the same URL, so registering it once insettings.jsonalso works for every flow with no comment.Docs. Add
docs/EDITOR.md(or an equivalent section in an existing doc) covering:.vscode/settings.jsonunderyaml.schemas.yaml-language-server: LSP config snippet.# yaml-language-server:inline comment path (works everywhere).Parity test. A single test asserts the schema and
flows checkagree on everytestdata/*.flow.yaml— schema-valid ⇔ check-accepts. That's the promise the schema makes to authors, and drift is the failure mode most likely to erode trust.CI publish.
.github/workflows/schema-publish.ymlon release tags: run the generator, publish@relayflows/schema, and uploadflows.schema.jsonto a stable GH Pages path with versioning.Not in scope (deferred)
@relayflows/ts-plugin).use:resolution, liveflows checkdiagnostics (L4 later, only if L1 + L2 + L3 leave a real gap).slack:,github:) — those come with each helper's plugin and add their own schema fragments.input: { step: X, path: [...] }selector target keys — that requires cross-file/whole-project analysis and belongs to the tsserver-plugin/LSP path.Acceptance evidence
testdata/hello-deterministic.flow.yamlin VS Code with@relayflows/schemaregistered shows no squiggles.identitty:instead ofidentity:) surfaces a squiggle immediately on save — noflows checkneeded.type: llmm) surfaces a squiggle.input:selector (input: { message: { step: "extract" } }missingpath) surfaces a squiggle only for the shapes we require; the wholly-optional-pathform is accepted.testdata/*.flow.yamlthatflows checkaccepts is schema-valid, and every one it refuses (seetestdata/json-schema-invalid.flow.yaml) is schema-invalid at the same node.scripts/generate-json-schema.mjsproduces byte-stable output on repeat invocations.Files to touch
scripts/generate-json-schema.mjs— new. Walkspackages/sdk/src/spec.tstypes, emitsflows.schema.json.packages/schema/— new package:package.json(name@relayflows/schema,main/exportspointing at the JSON file),flows.schema.json(generated + committed for zero-build consumption),README.md,tsconfig.json(types only, if any).packages/schema/tests/parity.test.ts— schema/check parity test overtestdata/*.flow.yaml..github/workflows/schema-publish.yml— release-tag-triggered publish job.docs/EDITOR.md(new) — VS Code / JetBrains / Neovim registration guide.packages/sdk/src/cli/check.ts— one-line hint for missing# yaml-language-server:header.Push-only PR pattern (finn-mini)
The codex agent pushes the branch and DMs me
L1 pushed sha=<HEAD>; I open the PR from the lead machine becauseghreturns 401 on fleet nodes.Depends on
Nothing shipped. Compatible with every merged v2 slice; runs alongside slice L2 (tsserver plugin, TS dialect) and L3 (
flows check --watch).Follow-ups filed separately
@relayflows/ts-pluginfor TS-dialect cross-file checks.flows check --watchfor terminal save-time feedback.