table
A Markdown table. The template body shows one example row; the validator checks the generated output, not the example. Close with `endtable`.
Leaf tag · open <!-- mdv: table … --> · close <!-- mdv: endtable -->
<!-- mdv: table cols=[Area, Owner, Status] minRows=1 -->
| Area | Owner | Status |
| --------- | ----- | ------ |
| Warp Core | Scott | green |
<!-- mdv: endtable -->Props
| Prop | Syntax | Category | Description |
|---|---|---|---|
cellPercent | cellPercent=<option> | content | Every n (p%) cell recomputes p from n and the column header's declared (N=…) denominator. Skips columns without an N header and non-matching cells; flags counts exceeding N. Warns only. |
columnEquation | columnEquation=<option> | content | A target column must equal a ±-expression of other columns per row (unit=raw|pct|bps). Skips rows with blank/unparseable cells; reports invalid-option for a named column the table lacks. Warns only. |
crossFoot | crossFoot | content | Cross-foot a table: each numeric data column must sum to its declared Total row within tolerance. Skips columns with any unparseable cell; silent when there is no Total row. Warns only. Compose with crossFoot.totalLabel= (custom total-row label) and crossFoot.tolerance= (explicit absolute tolerance) on the same directive. |
crossFoot.totalLabel | crossFoot.totalLabel=<option> | content | Modifier for crossFoot: the total row becomes the LAST row whose first cell contains this label (case-insensitive, Unicode-aware word-bounded — the same matching the default "Total"/"Totals" detection uses). Rows matching the label are excluded from the summed data rows. Quote labels with spaces, using either double or single quotes; either style may contain escaped ", ', and \ (only the quote that opened the atom closes it). Consumed by crossFoot on the same directive; an empty or whitespace-only value reports invalid-option, and so does malformed quoting — trailing text after the closing quote (e.g. "Net"x), or no valid closing quote at all — with its own dedicated message. |
crossFoot.tolerance | crossFoot.tolerance=<option> | content | Modifier for crossFoot: an explicit absolute tolerance (a finite number ≥ 0, in the column's unit) replacing the auto-derived halfUlp(precision)×(cells+1) default; 0 = exact, up to the 1e-9 floating-point guard withinTolerance always applies. Absent, the auto-derived default applies unchanged. Consumed by crossFoot on the same directive; a malformed value reports invalid-option. |
returnChain | returnChain=<option> | content | The product of (1+rᵢ) over named period rows must equal the stated cumulative row within tolerance. Silent when any named row/column is absent or unparseable. Warns only. |
col | col | table | Constrain a named column with col.<name>.<constraint>, including oneOf, required, matches, mask, and unique checks. |
cols | cols=<option> | table | Require the table to contain these columns, in order; extra columns are allowed (pair with noExtraCols to forbid them). |
noExtraCols | noExtraCols | strictness | Reject header columns not listed in cols=; without cols=, this rule has no effect. |
rows.in | rows.in=<option> | table | Require every table body row to jointly equal one canon record from a records-projected source, matched by header text = field name. |
minRows | minRows=<option> | table | Require the Markdown table to have at least N data rows (the header and separator rows do not count). |
maxRows | maxRows=<option> | table | Allow the Markdown table to have at most N data rows (the header and separator rows do not count). |
Prop details
cellPercent
Every n (p%) cell recomputes p from n and the column header's declared (N=…) denominator. Skips columns without an N header and non-matching cells; flags counts exceeding N. Warns only.
cellPercent=<option> · category: content · status: stable · src/pipeline/rules/numeric/table/cell-percent.ts
Recompute every n (p%) table cell against the denominator declared in its column header's
(N=<int>) annotation: p must equal 100·n/N at the displayed precision. A column whose header
carries no (N=...) is skipped whole; a cell that isn't <int> (<pct>%) is skipped; a count that
exceeds its column's N is always flagged. Default tolerance is half a unit in the stated percent's
last decimal; tol=<pp> overrides it. Warns only.
<!-- mdv: table cellPercent= -->
| Preferred term | Drug (N=120) | Placebo (N=118) |
| -------------- | ------------ | --------------- |
| Nausea | 12 (10.0%) | 6 (5.1%) |
<!-- mdv: endtable -->columnEquation
A target column must equal a ±-expression of other columns per row (unit=raw|pct|bps). Skips rows with blank/unparseable cells; reports invalid-option for a named column the table lacks. Warns only.
columnEquation=<option> · category: content · status: stable · src/pipeline/rules/numeric/table/column-equation.ts
Per-row column equation: a target column must equal a ±-expression of other columns, row by row.
unit=raw (default) compares like-for-like (End=Begin+Flows+PnL); unit=pct/bps express the
expression as a percent / basis-points of the SECOND operand (Excess=Fund-Benchmark;unit=bps).
Every row with a blank or unparseable operand/target cell is skipped silently; a column the
contract names but the table lacks is reported once as invalid-option. Warns only.
<!-- mdv: table columnEquation=End=Begin+Flows+PnL -->
| Period | Begin | Flows | PnL | End |
| ------ | ----- | ----- | --- | ------ |
| Q1 | 1,000 | 200 | 50 | 1,250 |
<!-- mdv: endtable -->crossFoot
Cross-foot a table: each numeric data column must sum to its declared Total row within tolerance. Skips columns with any unparseable cell; silent when there is no Total row. Warns only. Compose with crossFoot.totalLabel= (custom total-row label) and crossFoot.tolerance= (explicit absolute tolerance) on the same directive.
crossFoot · category: content · status: stable · src/pipeline/rules/numeric/table/cross-foot.ts
Cross-foot a table: every numeric data column must sum to its declared Total row (the last row
whose first cell reads "Total"/"Totals" — a case-insensitive, word-bounded substring match),
within a rounding-aware tolerance. Deliberately conservative — a column with ANY
unparseable/ambiguous cell is skipped whole (never partially summed), a table with no Total row is
a silent no-op, and the label column is never summed. Warns only; a contract hardens it with
error=cross-foot-mismatch.
Two modifier atoms on the same directive compose it (both optional; defaults unchanged when
absent): crossFoot.totalLabel="Grand Total" replaces the "Total"/"Totals" label
detection (same case-insensitive word-bounded matching, last matching row wins), and
crossFoot.tolerance=<n> replaces the auto-derived rounding tolerance with an explicit
absolute one (0 = exact, up to the 1e-9 floating-point guard withinTolerance always applies).
<!-- mdv: table crossFoot -->
| Line | Amount |
| -------- | ------ |
| Product | 8,000 |
| Services | 2,000 |
| Total | 10,000 |
<!-- mdv: endtable -->crossFoot.totalLabel
Modifier for crossFoot: the total row becomes the LAST row whose first cell contains this label (case-insensitive, Unicode-aware word-bounded — the same matching the default "Total"/"Totals" detection uses). Rows matching the label are excluded from the summed data rows. Quote labels with spaces, using either double or single quotes; either style may contain escaped ", ', and \ (only the quote that opened the atom closes it). Consumed by crossFoot on the same directive; an empty or whitespace-only value reports invalid-option, and so does malformed quoting — trailing text after the closing quote (e.g. "Net"x), or no valid closing quote at all — with its own dedicated message.
crossFoot.totalLabel=<option> · category: content · status: stable · src/pipeline/rules/numeric/table/cross-foot.ts
<!-- mdv: table crossFoot crossFoot.totalLabel="Grand Total" -->
| Line | Amount |
| -------- | ------ |
| Product | 8,000 |
| Services | 2,000 |
| Grand Total | 10,000 |
<!-- mdv: endtable -->crossFoot.tolerance
Modifier for crossFoot: an explicit absolute tolerance (a finite number ≥ 0, in the column's unit) replacing the auto-derived halfUlp(precision)×(cells+1) default; 0 = exact, up to the 1e-9 floating-point guard withinTolerance always applies. Absent, the auto-derived default applies unchanged. Consumed by crossFoot on the same directive; a malformed value reports invalid-option.
crossFoot.tolerance=<option> · category: content · status: stable · src/pipeline/rules/numeric/table/cross-foot.ts
Modifier for crossFoot: replaces the auto-derived rounding tolerance (half an ULP of
the column's displayed precision per summed cell) with an explicit absolute tolerance, in the
column's own unit. 0 means exact, up to the 1e-9 floating-point guard withinTolerance
always applies (so binary-representation dust from summing floats never false-positives); a
value can tighten or widen the default. Consumed by crossFoot via the shared
FieldContract; never reports on its own except invalid-option for a malformed value.
<!-- mdv: table crossFoot crossFoot.tolerance=0 -->
| Line | Amount |
| -------- | ------ |
| Product | 8,000 |
| Services | 2,000 |
| Total | 10,000 |
<!-- mdv: endtable -->returnChain
The product of (1+rᵢ) over named period rows must equal the stated cumulative row within tolerance. Silent when any named row/column is absent or unparseable. Warns only.
returnChain=<option> · category: content · status: stable · src/pipeline/rules/numeric/table/return-chain.ts
Compounding check: the product of (1 + rᵢ/100) over named source rows must equal the stated
cumulative row, within tolerance. Values are percents as displayed (1.2% or 1.2). Any named
row or the value column being absent/unparseable is a SILENT skip — absence in the DOCUMENT is not
misconfiguration; only a malformed option string is reported as invalid-option. Warns only.
<!-- mdv: table returnChain=Return;from=Jan|Feb|Mar;to=Q1 -->
| Period | Return |
| ------ | ------ |
| Jan | 1.0% |
| Feb | 2.0% |
| Mar | 3.0% |
| Q1 | 6.11% |
<!-- mdv: endtable -->col
Constrain a named column with col.<name>.<constraint>, including oneOf, required, matches, mask, and unique checks.
col · category: table · status: stable · src/pipeline/rules/table/col.ts
Checks constraints on named table columns; targets a table.
col.<name>.mask may embed a |in: membership filter (col.Version.mask="<semver|in: securityPolicy?>"): once a cell matches the mask shape, the captured value is checked against
ctx.document.sources(ref) and reported as unknown-entity on a miss — same posture as
heading.mask/pattern.mask: a shape mismatch never triggers membership on top of it, and no
sources capability supplied degrades to a silent no-op (issue #1058 — this rule used to compile
|in: filters, and even collect them as a declared dependency, without ever enforcing them).
<!-- mdv: table col.Status.oneOf=[open, done] col.ID.unique -->
| Area | Status |
| ---- | ------ |
| Docking | done |
<!-- mdv: endtable -->cols
Require the table to contain these columns, in order; extra columns are allowed (pair with noExtraCols to forbid them).
cols=<option> · category: table · status: stable · src/pipeline/rules/table/cols.ts
Checks that a table contains the named columns, in order; extra columns are allowed (the declared
columns must appear as an ordered subsequence of the headers). Pair with noExtraCols to also
forbid columns outside this list. Targets a table.
<!-- mdv: table cols=[Area, Owner, Status] -->
| Area | Owner | Status |
| ---- | ----- | ------ |
| Relay | Dana | done |
<!-- mdv: endtable -->noExtraCols
Reject header columns not listed in cols=; without cols=, this rule has no effect.
noExtraCols · category: strictness · status: stable · src/pipeline/rules/table/no-extra-cols.ts
<!-- mdv: table cols=[Area, Owner] noExtraCols -->
| Area | Owner |
| ---- | ----- |
| Hangar | Dana |
<!-- mdv: endtable -->rows.in
Require every table body row to jointly equal one canon record from a records-projected source, matched by header text = field name.
rows.in=<option> · category: table · status: stable · src/pipeline/rules/table/rows-in.ts
Every table body row's columns, matched to a records-projected source's fields by HEADER
TEXT = FIELD NAME, must jointly equal one canon record (row/cell diagnostics land on the DATA
ROW's own line — E1a row-line threading). Header binding: for each projected field, exactly one
table header must normalize to it (NFC, lowercase, outer-trimmed, internal-whitespace
collapsed) — a missing field or a normalization collision emits ONE rows-header-mismatch at
the table header (never a row flood) and suppresses row membership for the whole table.
Reordered headers are fine; multiword headers work with no column-selector grammar (matching is
by full header text). The target tuple is assembled in the source's OWN records: declaration
order regardless of visible header order; extra table columns are ignored. Row equality uses the
source's own compare mode (exact/folded/mention — default mention). Nearest-record hinting is
bounded: the first projected field is the diagnostic key; a row whose first-field value matches
one or more canon records under compare names the best candidate in that bucket (fewest
differing fields, source order); no global fuzzy search. Honest-null without a sources
capability, and when the bound source is not a records projection.
<!-- mdv: table rows.in=./metrics.json -->
| metric | period | unit | value |
| ------- | ------ | ---- | ----- |
| Latency | Q1 | ms | 120 |
<!-- mdv: endtable -->minRows
Require the Markdown table to have at least N data rows (the header and separator rows do not count).
minRows=<option> · category: table · status: stable · src/pipeline/rules/table/rows.ts
<!-- mdv: table minRows=1 -->
| Area | Owner | Status |
| ---- | ----- | ------ |
| Reactor | Dana | done |
<!-- mdv: endtable -->maxRows
Allow the Markdown table to have at most N data rows (the header and separator rows do not count).
maxRows=<option> · category: table · status: stable · src/pipeline/rules/table/rows.ts
<!-- mdv: table maxRows=3 -->
| Area | Owner | Status |
| ---- | ----- | ------ |
| Airlock | Dana | done |
<!-- mdv: endtable -->section
The one heading-owning, recursive container tag. Wraps its ATX heading plus any own-prose or nested tags (child sections and leaves), and is the thing presence/body rules (`required`, `minWords`, `noPlaceholder`, …) and `occurs=` target. Sections nest, so a schema is an AST of sections — close every one with its `endsection` end tag. Opens directly before its ATX heading.
list
A bullet or numbered list. Constrain it with `minItems`, or hold each item to a bar with `each.falsifiable` / `each.sourced`. Close with `endlist`.