Tags & rules
Open a tag, attach rules as flags or options, close it. A small, composable grammar — not regex.
On the assembly deck, a droid is built from a small set of standard parts — chassis, sensor, manipulator — and each part gets QA stamps before it leaves the line. mediva works the same way: tags are the parts, rules are the stamps.
mediva has two building blocks: tags (the construct) and rules (the checks you attach). Snap a few parts together, stamp each one, and you've described exactly the document you'll accept.
Tags
A tag is the construct you attach rules to, and there are exactly two kinds of it. A section owns a heading — open it immediately before the ATX heading, close it after the section's whole body, child sections and leaves included — and it's the only tag that nests. A leaf governs one construct inside a section and never carries a heading of its own. The full parts bin:
section— heading-owning, nestinglist·evidenceList·taskList·choice·table·code·line·media— leaves, all headinglessdocument·front— structural markers, no end tag
Use evidenceList when each bullet is proof, verification, or a source-backed claim rather than just another list item. Both taskList and choice are checkbox lists, split by intent: a taskList is a checklist whose boxes should all get ticked (allChecked), while a choice is a pick-from-options control (oneChecked for single-select, minChecked/maxChecked to bound a multi-select). line governs a single labeled line inside a section — an attestation like Status: Healthy — and media governs an image or video. A leaf is a compile error outside a section; a section owns only the presence, word-count, and heading rules over its own prose, never a leaf's item-level rules.
<!-- mdv: section required -->
## Systems
<!-- mdv: table -->
| System | Status |
| --------- | ------ |
| Hyperdrive| ? |
<!-- mdv: endtable -->
<!-- mdv: endsection -->See every tag in the Reference.
Rules
A rule is a check on a tag — a QA stamp — written one of two ways:
- flag —
required,noPlaceholder,allChecked - option —
minWords=20,cols=[Area, Owner],lang=sh
Stack as many stamps as the part needs:
<!-- mdv: section required minWords=20 noPlaceholder -->
## Summary
<!-- mdv: endsection -->That section must exist, carry at least 20 real words, and contain no TODO/TBD placeholder — three stamps, one part. Each rule belongs to a family (presence, content, strictness, …) — the category you'll see in the reference's Props table — and each rule is only legal on the component that owns it: presence and word-count rules attach to a section, and item-level rules like cols= attach to the leaf that wraps the construct. A rule on the wrong component is a compile error, not a warning.
Content rules
Beyond word counts, two stamps inspect what a section's body actually says. contains demands something be present; forbid demands something be absent. contains values can be a construct name (table, code, image, …), a /regex/, or a "quoted literal"; forbid values are a /regex/, a "quoted literal", or bare literal terms (no construct names). Both accept one array literal when more than one value is required.
<!-- mdv: section required contains=[table, /because/] forbid=/tbd|tba/i -->
## Decision
State the call, justify it with the word "because", and back it with a table.
<!-- mdv: endsection -->contains=[table, /because/] requires both a table and prose matching /because/ (a raw substring regex — add \b…\b if you need a whole-word match); contains=[code, image] would require both a code block and an image. forbid=/tbd|tba/i rejects the body if "TBD" or "TBA" appears in any case — regexes are case-sensitive unless you add the i flag. A typo'd construct name (contains=heading — a heading is not a construct contains recognizes) is caught as a compile error, not a silent pass.
Strictness
Some parts support a closure stamp that forbids anything undeclared — exactLabels (checklists), noExtraCols (tables), noExtraSections (sections). Add it to go from "must contain" to "exactly this, nothing smuggled aboard." See Strictness.
A rule's severity can also be retuned or silenced per directive with Severity overrides.
Plugins add rules and tags
Both parts bins are extensible: a plugin can contribute its own rules — namespaced ${name}/rule, like compliance/noWeasel — and its own tags: defineLeaf for a new headingless construct, defineSectionPreset for a bundled rule set on an ordinary section, or defineCompositeSection for a construct that expands at compile time into a section plus child leaves. A plugin tag can never own a heading — that's section's alone. Namespaced rules attach exactly like core ones, and their diagnostics take overrides the same way.
Why not regex?
mediva checks Markdown structure, so a checklist contract can say "exactly these labels, all checked" without rebuilding a parser in regex.
The regex version starts brittle fast:
/## Checklist\s+[-*] \[x\] Tests added or updated\s+[-*] \[x\] Docs updated(?!\s+[-*] \[[ x]\])/mThe mediva version says the thing directly:
<!-- mdv: section required -->
## Checklist
<!-- mdv: taskList required allChecked exactLabels -->
- [ ] Tests added or updated
- [ ] Docs updated
<!-- mdv: endtaskList -->
<!-- mdv: endsection -->A tag knows it's a table, a checklist, a code fence — so rules read the document's structure, not its bytes. If the checklist is missing, unchecked, renamed, or has an extra line, mediva points at the exact failing line on the bench instead of a cryptic byte offset. Regex sees a wall of characters; mediva sees the droid.
Next: tune the contract with Strictness, Overrides, Frontmatter, and Evidence lists. Keep the Reference nearby for the full prop catalog, and use the Glossary when the vocabulary gets dense.