Skip to content

flows: publish JSON Schema for YAML dialect — zero-install editor validation #320

Description

@kjgbot

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

  1. 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.
  2. 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.
  3. 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.

  4. 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).
  5. 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.

  6. 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

  • Opening testdata/hello-deterministic.flow.yaml in VS Code with @relayflows/schema registered shows no squiggles.
  • Introducing an unknown header key (e.g. identitty: instead of identity:) surfaces a squiggle immediately on save — no flows check needed.
  • Wrong step-type discriminant (type: llmm) surfaces a squiggle.
  • Malformed input: selector (input: { message: { step: "extract" } } missing path) surfaces a squiggle only for the shapes we require; the wholly-optional-path form is accepted.
  • Parity test: every fixture in testdata/*.flow.yaml that flows check accepts is schema-valid, and every one it refuses (see testdata/json-schema-invalid.flow.yaml) is schema-invalid at the same node.
  • Regen script scripts/generate-json-schema.mjs produces byte-stable output on repeat invocations.
  • Published tarball has no runtime dependencies (schema is data).

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions