Grounded templates
Bind logical source names to real data — files, commands, loaders, or in-memory values — so a contract checks claims against current truth, not just shape.
AIRLOCK CONTROL: Visitor "@jsmith" is spelled correctly and is not on the crew manifest. Please wait outside.
A grounded rule checks a claim against real data, not just its shape.
<!-- mdv: section content.mask="On call: <mention|in: ./team.json>" -->
## Incident Roles["maya", "priya"]## Incident Roles
On call: @jsmithincident-roles.md
3 error "@jsmith" not found in "./team.json" unknown-entity
help: Correct a misspelt name to the source's spelling; never swap in another nameNaming a source
| Spelling | Example | Bound by |
|---|---|---|
. (contract-relative, no .) | <mention|in: . | the file beside the contract |
| logical name | <issue|in: openIssues?> | the host, at check time |
A trailing ? makes the ref optional.
| Case | Result |
|---|---|
| optional, unbound | silent skip: shape-only until wired |
| required, unbound | loud failure naming the missing wiring |
| bound but failing to load | always loud |
Binding
Later wins: ground, then sources., then --source.
// mediva.config.mjs
import { defineConfig } from "mediva";
export default defineConfig({
ground: {
// mdv sources sync writes .mdv/openIssues.json; auto-bound
openIssues: "gh issue list --state open --json number --jq '[.[].number | tostring]'",
},
sources: {
declare: {
changedFiles: "./.mdv/changed.json", // a path
team: ["@ana", "@bo"], // literal data
// changedFiles: async () => computeChangedFiles(), // or a live loader
},
},
});--source NAME=FILE[,NAME=FILE] pins names to static dumps for one run. In code, use schema..
Projections
| Entry | Source becomes |
|---|---|
{ path, members: [. | the named fields of a JSON array of objects |
{ path, markdown: { headings: { level, mask? } } } | the text of each heading at level |
{ path, entries: "stem" | "name" } | the directory's child filenames |
{ path, records: [. | rows kept as tuples, for table rows. |
declare: {
metrics: { path: "./metrics.json", records: ["metric", "period", "unit", "value"] },
reviewers: { path: "./team.json", members: ["login"], compare: "exact" },
},<!-- mdv: section required -->
## Metrics
<!-- mdv: table rows.in=metrics -->
| metric | period | unit | value |
| ------- | ------ | ---- | ----- |
| Latency | Q1 | ms | 120 |compare | Equality |
|---|---|
exact | NFC, then code points |
folded | NFC, then lowercase |
mention (default) | folded, minus one leading @ |
CLI
mdv sources sync # run ground producers into .mdv/ (generated, gitignored)
mdv sources status --check # exit 1 if anything is absent or stale
mdv sources list # every binding and where it came from
mdv sources doctor # unbound refs, orphaned config keys
mdv check --source-snapshot capture.json # record capture.json when missing, else replay it offline
mdv check --source team=team.json # pin one name to a file for this runA failed producer keeps the last good file. A capture pins the mediva version, the bindings and the contract hashes; when a contract or a binding has changed since, the replay cannot run: delete the capture and run again to re-record it.
Check new documents against current data; old ones drift. In CI the capture is an artifact of the run, not a committed file: a committed capture makes every later run replay stale data, with only a note when a source file's bytes changed.
- run: mdv sources sync
- run: mdv sources status --check
- run: mdv check --source-snapshot source-capture.json
- uses: actions/upload-artifact # reproduce locally: mdv check --source-snapshot source-capture.json
with: { name: source-capture, path: source-capture.json }