Ravel Map schema guide

Purpose

A Ravel Map is the JSON contract between a format/profile adapter and Ravel core. It says what chunks a source document contains, where they came from, and which structural declarations/effects the adapter found. It does not parse the language inside a chunk body; the chunk parser does that.

Markdown profile ─┐
AsciiDoc profile ─┼→ Ravel Map → Ravel core
Rix block model ──┘

Canonical shape

{
  "version": 1,
  "document": {
    "id": "greeting",
    "uri": "file:///project/greeting.md",
    "format": "markdown+ravel-v1"
  },
  "chunks": [
    {
      "id": "greeting::main",
      "identity": {
        "document": "greeting",
        "chunk": "main",
        "minor": null,
        "type": null
      },
      "name": "Main program",
      "body": "console.log(_\"format-greeting\");\n",
      "definitionPipeline": [],
      "metadata": { "language": "javascript", "tags": ["entrypoint"] },
      "source": { "uri": "file:///project/greeting.md", "range": "…" }
    }
  ],
  "directives": []
}

The canonical interchange form uses objects rather than positional arrays. The original tuple idea—name, pipeline, body, metadata, position—is the right information, but object keys make schema evolution and diagnostics safer. A compact tuple encoding can later be a cache/transport optimization without becoming the public contract.

Top-level fields

Field Required Meaning
version yes Positive integer schema version.
document yes Source identity and adapter format/profile.
chunks yes Ordered source chunks; order preserves narrative provenance.
directives no Adapter-discovered structural/effect declarations.
metadata no Document-level adapter metadata.

Chunk fields

Field Required Meaning
id yes Canonical serialized identity, unique within the joined graph.
identity yes Explicit document, chunk, minor, and type fields (each identifier or null).
name no Human-facing display label.
body yes Raw chunk text for the Ravel chunk parser.
definitionPipeline no Transform calls declared by the adapter/profile.
metadata.language no Language/display hint; never executable by itself.
metadata.tags no Labels for hosts, searches, and profiles.
metadata.data no JSON data owned by a profile/plugin namespace.
source yes URI and inclusive/exclusive source range.
fragments no Fine-grained source ranges if a chunk joins multiple regions.

Body is intentionally raw. The map can be inspected, saved, diffed, or created by an editor without implementing composition syntax.

id must exactly equal the canonical serialization of identity, such as guide::parser:preamble.javascript. guide:: denotes a document-root chunk; guide::.javascript and guide:::preamble.javascript are valid root variants. For an intentionally global chunk, set identity.document to null and use a non-null chunk; for example, the canonical ID shared has identity { "document": null, "chunk": "shared", "minor": null, "type": null }. Adapters must provide all four keys rather than infer them from an ambiguous string later.

Chunks emitted from pipes

The map is the input to chunk parsing, not the result of it. A body expression may use the core emit pipe step to declare a derived chunk. The parser records that declaration, then the graph-expansion phase adds a generated chunk to the Ravel Program. Emission preserves the owner’s document and base chunk, and its suffix controls only minor and type (for example emit(".js")). The generated chunk has the normal chunk fields plus provenance pointing to the emitting expression.

This is deliberately not an adapter concern and is not written back into the source map. A Markdown, AsciiDoc, or Rix adapter only needs to preserve the raw body and its range; it does not need to recognize emit.

Directives

Directives are separate because they often declare a graph edge or effect rather than contribute text. The initial directive set is:

Kind Required fields Meaning
in target Load the named Markdown or JSON Ravel Map relative to this map and merge its chunks before transformation.
out name and from Make the completed fully qualified source chunk a deliverable under file-like name.
create document, name, compose Create a document-local generated definition from staged composition IR.
alias document, name, reference Create an alternate document-local graph name whose provenance retains its target.

compose is an ordered array of append, newline, pipe, and pass nodes. Adapters preserve their source ranges and produce this portable IR; core owns resolution, staged accumulator evaluation, local emit expansion, and cycle diagnostics. A Markdown ravel fence is the first source-language spelling of these declarations. It is an adapter feature, not a special Markdown rule in core.

An out directive is a plan, not an automatic write. A Node host writes its deliverables under an explicit output directory and rejects absolute or directory-escaping names. A directive records its kind, source range, optional name/from/target, arguments, and metadata.

Unsupported directive forms are adapter diagnostics; hosts execute only the explicitly supported in and out effects.

Positions and errors

Positions are zero-based line, column, and UTF-16 offset values. Ranges start inclusively and end exclusively. UTF-16 offsets align with JavaScript, TypeScript, Monaco, and LSP conventions. The chunk parser later adds ranges for references and transforms inside body.

Profiles are adapter policy

The document format identifies the profile that produced the map. For example, markdown+ravel-v1 may require explicit chunk attributes, while markdown+litpro-compat-v1 may interpret heading depth and old link conventions. Both produce this schema. H5/H6-as-child-block is profile policy, not a Ravel core rule.

Versioning

Version changes only for incompatible map-shape changes. Optional namespaced metadata is the preferred extension mechanism. Adapters reject map versions they cannot understand rather than guessing semantics.

The schema is published with @pieceful/ravel-map, both as the portable RAVEL_MAP_SCHEMA JavaScript export and as the JSON module @pieceful/ravel-map/schema. The repository copy at schemas/ravel-map.schema.json is checked against that packaged artifact in the public map-contract test.

import { RAVEL_MAP_SCHEMA, validateRavelMap } from "@pieceful/ravel-map";

const diagnostics = validateRavelMap(map, { uri: "editor-buffer.ravel-map.json" });
console.log(RAVEL_MAP_SCHEMA.$id, diagnostics);

See the example in examples/greeting.ravel-map.json.