Skip to content

Schema Reference โ€‹

rune.schema.json is the canonical binding manifest โ€” the artifact that turns cross-layer contracts from aspirational to enforceable.

Without the schema, Rune is a convention. With it, Rune is a contract.

The schema is hosted at rune.semanticintent.dev/rune.schema.json (JSON Schema draft-07).


Manifest structure โ€‹

json
{
  "$schema": "https://rune.semanticintent.dev/rune.schema.json",
  "$rune": "1.1",
  "host": {
    "format": "react",
    "version": "1.0.0",
    "source": "app.tsx"
  },
  "bindings": {
    "risk-threshold": {
      "rune": "~",
      "type": "number",
      "min": 0.05,
      "max": 0.30,
      "intent": "approved by risk committee Q1-2025 โ€” review at quarter end"
    },
    "market-price": {
      "rune": "@",
      "type": "number",
      "intent": "live NBBO feed โ€” not manually entered"
    },
    "submit-order": {
      "rune": "!",
      "args": [{ "name": "order-id", "type": "string", "required": true }],
      "intent": "explicit, logged to OMS, irrevocable"
    },
    "screen-purpose": {
      "rune": "?",
      "intent": "trading terminal โ€” real-money consequences, every action logged"
    }
  }
}

Top-level fields โ€‹

FieldRequiredDescription
$schemaNoURI to the meta-schema โ€” enables IDE validation
$runeYesProtocol version โ€” must be "1.1"
hostNoMetadata about the host that owns these bindings
bindingsYesNamed binding declarations (see below)

host โ€‹

FieldDescription
formatHost format identifier โ€” "mere", "react", "csharp", "sql", etc.
versionVersion of the host document or application
sourcePath or identifier of the source document

Binding declaration โ€‹

Binding keys must be kebab-case (^[a-z][a-z0-9-]*$). This is the canonical form โ€” hosts translate to their own conventions.

Hostrisk-threshold becomes
SQLrisk_threshold
C#RiskThreshold
React'risk-threshold'

The schema is the arbiter when layers disagree on naming.

Binding fields โ€‹

FieldRequiredDescription
runeYes"@" read, "~" sync, "!" act, "?" intent
typeRecommendedValue type โ€” string, number, boolean, array, object
intentRequired for ?The ? annotation โ€” rationale, constraint, governance
argsFor ! onlyParameter declarations (see below)
minFor numberMinimum value โ€” RNE005 if violated
maxFor numberMaximum value โ€” RNE005 if violated
enumFor stringAllowed values โ€” RNE005 if an unlisted value is assigned
maxLengthFor stringMax character length โ€” maps to PIC X(n) in RECALL, VARCHAR(n) in SQL

Rune types โ€‹

RuneNameWhat it governs
@readOne-way, state โ†’ display. No side effects. Not writable.
~syncBidirectional. Input and state stay in sync. Mutable.
!actExplicit invocation. Nothing happens without a call.
?intentAnnotation only. No runtime effect. Travels with the binding.

args (for ! act bindings) โ€‹

json
"submit-order": {
  "rune": "!",
  "args": [
    { "name": "order-id",  "type": "string",  "required": true  },
    { "name": "quantity",  "type": "number",  "required": true  },
    { "name": "dry-run",   "type": "boolean", "required": false }
  ]
}
FieldDescription
nameParameter name
typestring, number, boolean, state-ref
requiredWhether the parameter must be supplied at call sites

state-ref means the argument value is a binding id โ€” the action receives the current state value of that binding.


Constraint validation (RNE005) โ€‹

The schema validator raises RNE005 when:

  • min or max is declared on a non-number binding
  • enum is declared on a non-string binding
  • min is greater than max
  • args is declared on a non-! binding

Complete example โ€‹

A Mere workbook binding manifest showing all four runes with full constraint declarations:

json
{
  "$schema": "https://rune.semanticintent.dev/rune.schema.json",
  "$rune": "1.1",
  "host": { "format": "mere", "source": "task-workbook.mp.html" },
  "bindings": {
    "tasks":           { "rune": "@", "type": "array",  "intent": "computed โ€” do not mutate directly" },
    "new-task":        { "rune": "~", "type": "string", "maxLength": 200, "intent": "cleared after !add-task fires" },
    "task-filter":     { "rune": "~", "type": "string", "enum": ["all", "active", "completed"], "intent": "controls @tasks subset" },
    "task-count":      { "rune": "@", "type": "number", "intent": "live count of active tasks โ€” display only" },
    "add-task":        { "rune": "!", "args": [{ "name": "title", "type": "state-ref", "required": true }], "intent": "no-op if ~new-task is empty" },
    "complete-task":   { "rune": "!", "args": [{ "name": "task-id", "type": "string", "required": true }], "intent": "irreversible" },
    "clear-completed": { "rune": "!", "intent": "destructive โ€” no undo" },
    "workbook-purpose":{ "rune": "?", "intent": "personal task tracking โ€” single user, no persistence beyond session" }
  }
}

See examples/task-workbook.rune.json in the repository.


Tooling โ€‹

Use rune validate from @semanticintent/rune-cli to validate any manifest against this schema:

sh
rune validate my-app.rune.json
rune validate my-app.rune.json --format json   # CI-friendly

Use rune extract to generate a manifest from existing source:

sh
rune extract src/          # scan and bootstrap
rune validate rune.json    # see what's missing

See CLI reference โ†’