Validate a status table
Enforce the columns, allowed values, required cells, and uniqueness of a Markdown table.
A status board that lets you type "sleeping" in the reactor's Status column is not a status board. It's a rumour.
Plenty of contracts live in a Markdown table: a systems board, an on-call roster, an API surface, a dependency inventory. mediva can lock the table's shape and its cell values, so a half-filled or made-up row never passes review.
The contract
<!-- mdv: section required -->
## Systems Board
<!-- mdv: table required cols=[System, Owner, Status] minRows=1 noExtraCols col.System.unique col.Owner.required col.Status.oneOf=[nominal, degraded, offline] -->
| System | Owner | Status |
| ------ | ----- | ------ |
| Reactor | Dana | nominal |
| Life Support | Cole | nominal |
<!-- mdv: endtable -->
<!-- mdv: endsection -->Every atom on that one table directive earns its place, and the enclosing section just needs to exist:
required(on thetableleaf) — the Systems Board section must hold a table.cols=[System, Owner, Status]— those columns must appear, in that order. They're matched as an ordered subsequence, so extra columns are tolerated unless you also lock the set.noExtraCols— locks it: a strayNotescolumn is now[table-extra-columns].minRows=1— the board can't be empty (pair withmaxRows=Nto cap it).col.System.unique— no two rows can name the same system.col.Owner.required— every row needs an owner; an empty Owner cell fails.col.Status.oneOf=[nominal, degraded, offline]— Status must be one of the three real states. "sleeping" is a rumour, and it's rejected.
What it catches
Point the contract at a board where someone fudged a row:
## Systems Board
| System | Owner | Status | Notes |
| ------ | ----- | ------ | ----- |
| Reactor | Dana | sleeping | |
| Reactor | | nominal | dup |Form — edit the document
systems-board.md
3 error Unexpected table column table-extra-columns
The "Systems Board" table has an unexpected column "Notes".
fix: Remove "Notes", or add it to cols=.
State — do the work, then record it
systems-board.md
6 error Duplicate table cell table-cell-duplicate
In the "Systems Board" table, column "System" repeats "Reactor".
fix: This column is used as a unique identifier or distinct value. Work out what uniquely and truthfully identifies each affected row, then give each row that value.
6 error Required table cell empty table-cell-required
In the "Systems Board" table, column "Owner" is required but has an empty cell.
fix: This column is required for every row. Look up the real value for this row, then fill the cell with it. If the value genuinely does not exist, resolve that outside the document first.
5 error Invalid table cell table-cell-invalid
In the "Systems Board" table, column "Status" has "sleeping", which is not allowed.
fix: Use one of: nominal, degraded, offline.
contract: systems-board.mdv.md
Restructuring on purpose? Update the contract in the same change so document and contract move together. One-off exception: <!-- mdv: disable-next-line <rule> -- reason -->
✖ 4 problems (4 errors, 0 warnings)Four lies, four diagnostics, exit code 1. Fix the cells and the board passes clean.
The column constraints
Each col.<column>.<constraint> atom targets one named column:
| Constraint | Syntax | Checks |
|---|---|---|
required | col.Owner.required | the cell is non-empty in every row |
oneOf | col.Status.oneOf=[open, done] | the cell is one of the listed values |
matches | col.Version.matches=/^\d+\.\d+\.\d+$/ | the cell matches a regex |
unique | col.ID.unique | no value repeats down the column |
Empty cells are skipped by oneOf, matches, and unique — only required objects to a blank. So you can demand a format without demanding a value: add both col.X.required and col.X.matches=/.../ when a cell must be present and well-formed. A constraint naming a column the table doesn't have raises a [table-unknown-column] warning, so a typo in the contract surfaces instead of silently passing.
Run it in CI
Keep the contract beside the file as systems-board.mdv.md and the zero-config scan picks it up, or check it explicitly:
npx mediva check systems-board.md --schema systems-board.mdv.mdVariations
- For a roster where each person may own several systems, drop
col.System.uniquebut keepcol.Owner.required. - To allow a column the schema doesn't enumerate, omit
noExtraCols—cols=still requires the named columns, but extras are tolerated. - Combine a table contract with sibling
sectionheadings in the same file: thetableleaf is just one construct among the document's slots.