TypeScript API

Compile a contract and validate Markdown programmatically, gate by gate (lint, shape, state), so LLM autofix is safe by construction.

Cooper: "TARS, what's your honesty setting?" TARS: "Ninety percent." Cooper: "Ninety?" TARS: Absolute honesty isn't always the most diplomatic… but I'll never tick a box I didn't earn.

Compile a contract, check a document, read the verdict.

import { compile } from "mediva";

const schema = compile(`
<!-- mdv: section required content.words.min=20 content.none=@placeholders -->
## Summary
<!-- mdv: endsection -->
`);

const result = schema.check(markdown);
//  { path, gate, diagnostics, failing, readout? }
//  gate: null passed; "could-not-run" judged nothing; else the gate that failed
if (result.gate !== null) console.error(result.failing); // the findings the verdict rests on

Gates: lint, shape, state

The gates run in order. gate names the first that failed, and diagnostics holds only its findings; state never runs on a document whose shape failed. When none fails, gate is null and diagnostics holds every gate's warnings. gate is "could-not-run" when nothing was judged: the contract is broken, or the run lacks an input it needs (an error such as missing-title).

GateAsksExampleAutofix
lintCan mediva read the Markdown?markdownlint, unclosed-fence, fenced-outputyes
shapeDoes the form match the contract?missing-section, label-line-mismatchyes
stateDoes what it claims hold?empty-section, unchecked-items, value-not-allowedno

The lint gate runs first on every document: markdownlint's own rules (default MD005, MD056 and MD046 fenced; the lint option replaces them, and a severity in it throws) plus mediva's own checks. Its findings are always errors; no contract lowers them.

MethodRuns
check(md, options)every gate: lint, shape, then state
check(md, { through: "shape" })lint, then shape
OptionEffect
paththe document's path, as the result names it
contextthe title, issues and values the contract reads
documentresolver and source capabilities for grounding rules
failOn"warning": any finding fails its gate, so a shape warning stops state (default "error")
lintmarkdownlint's configuration object, replacing the default rule set
through"shape": stop once the shape gate passed (no state rule runs)
schema.check(md, { through: "shape" }).gate // null: the form is right
const r = schema.check(md);
r.gate                                      // null, yet unfinished:
r.diagnostics                               // [unchecked-items]: a human owns that box

schema.check(md, {
  context: { issues: [{ number: 123, state: "open" }], title: "feat: retry uplink" },
  failOn: "warning",
});

Unticked boxes warn until the contract says error=unchecked-items. Advisory (heuristic) findings stay warnings even under error=.

Once shape passes, readout says what the document claims, so you compare state without parsing Markdown: { title?, front?, fields: [{ block, kind, present, empty, items?, lines?, rows? }] }. title is the title the run was given; front lists each declared front-matter key as { key, line, value }. A repeated section reports its fields once per instance. A checklist item carries label, line, ticked, evidence (nested evidence whole), reason and a disposition (done, claimed-without-evidence, na, not-done, open); a labeled line carries label, line, value, parts ({ token, value } per mask token), a disposition (given or not-done) and its reason; a table row carries line and cells by column header. A template slot (<why>) or a stub (TODO) is never a reason: it is placeholder-text.

Grounding: schema.bind()

Bind the source names a contract spells to data or loaders (grounded templates).

const bound = await schema.bind({
  sources: {
    openIssues: ["123", "456"],                             // literal data
    changedFiles: async ({ signal }) => fetchFiles(signal), // loader
    crew: { kind: "file", path: "/abs/crew.json", members: ["name"] }, // a file, projected (needs `read`)
  },
  context: { breaking: false },
});
const report = bound.check(md); // sync: loaders resolved once, at bind
OptionEffect
(default)binding-unknown, binding-missing or a source that fails to load throws GroundingError
{ partial: true }reports them instead
{ templatePath, read }loads the contract's ./ path refs (relative to templatePath) and file bindings through read
{ kind: "loader", load, timeoutMs, optional }optional: true turns its failure into a source-skipped note for a ? ref; a required ref still fails

Inspecting document state

schema.inspect(md) is the same check, with how each declared field stands.

const { gate, failing, fields, document } = schema.inspect(md);
// fields[]: { label, kind, present, empty, diagnostics }: a field fails when one of its
//   diagnostics is in `failing` (the run's one verdict)
// document: Diagnostic[] for frontmatter and structure

LLM autofix: check through shape

Show the model only form errors and it cannot fake state (the full loop).

let r = schema.check(md, { through: "shape" });
if (r.gate !== null && r.gate !== "could-not-run") {
  md = await llmFix(md, r.failing); // only FORM errors reach the model
  r = schema.check(md, { through: "shape" });
}
const state = schema.check(md);     // for a human, not for the model

Rendering: renderMarkdown

{ rules: true } keeps the rules as plain-English comments, for prompting an agent.

import { renderMarkdown } from "mediva";

renderMarkdown(contract);                  // the contract minus its hidden rules
renderMarkdown(contract, { rules: true });
// ## Description
// <!-- rules: required · at least 20 words · no placeholders (TODO, TBD…) -->

Plugins

compile takes plugins (authoring).

import plugin from "mediva-plugin-compliance";

const schema = compile(template, { plugins: [plugin] });

API for builds and servers (recipe); CLI for CI.

On this page