recon_gen.common.handbook.invariants

L1 invariants parser — AA.C.2.

Reads src/recon_gen/docs/L1_Invariants.md (the single source of truth for the seven L1 SHOULD-constraints + the Supersession Audit diagnostic surface) and returns a typed mapping of invariant kind -> InvariantSection. Phase O.1’s mkdocs vocabulary keeps the doc on disk; this parser lets the dashboard generators pull the same prose into sheet-bottom panels (AA.C.3) and the Studio trainer pane (AA.C.5) without duplicating the text.

Bundled-doc input has two Jinja-style affordances the parser strips by default so the output is dashboard-paneable as-is:

  • {{ l2_instance_name }} substitution in view-name headings (e.g., ` `{{ l2_instance_name }}_drift` `) – collapsed to the bare view suffix so panel titles read drift not {{ l2_instance_name }}_drift.

  • {% if vocab.fixture_name == "X" %} ... {% endif %} worked-example blocks – dropped entirely; they belong in the handbook for that specific demo, not in a dashboard panel that might render against any L2.

Pass strip_jinja=False to get the raw doc content (e.g., for re-rendering through the mkdocs pipeline).

Functions

load_bundled_invariants(*[, strip_jinja])

Read the bundled L1_Invariants.md from recon_gen.docs and return parsed sections.

panel_markdown(section)

Compose a sheet-bottom panel for an InvariantSection.

parse_l1_invariants(md_text, *[, strip_jinja])

Walk the markdown source and yield one section per recognized heading.

Classes

InvariantSection(kind, title, ...)

One parsed section from L1_Invariants.md.

class recon_gen.common.handbook.invariants.InvariantSection(kind, title, short_statement, body, columns, what_to_do)[source]

Bases: object

One parsed section from L1_Invariants.md.

Two heading shapes feed this:

  • The seven numbered ### N. `{{ l2_instance_name }}_<kind>` -- <title> sections under “## The seven L1 SHOULD-constraints”.

  • The ## Diagnostic surface -- Supersession Audit section, mapped to kind="supersession_audit" for symmetry.

Parameters:
  • kind (str)

  • title (str)

  • short_statement (str)

  • body (str)

  • columns (tuple[str, ...])

  • what_to_do (str)

body: str

Prose paragraphs after the blockquote (or the heading, for sections without one). The **What to do:** ... line is extracted into what_to_do and dropped from body so the dashboard panel can render the remediation in its own styled block. The **Columns:** ... line stays inline.

columns: tuple[str, ...]

Parsed column names from the **Columns:** ... line. Empty tuple when the section doesn’t declare columns (the Supersession Audit section, for instance).

kind: str

Bare view-name suffix – "drift" / "limit_breach" / "supersession_audit". Joins to INVARIANT_KIND_TO_SHEET.

short_statement: str

The blockquote SHOULD-constraint, one paragraph stripped of the leading ``> `` markers. Empty string for sections without a blockquote (the Supersession Audit section is descriptive, not a SHOULD).

title: str

Human heading after the em-dash – "Sub-ledger drift".

what_to_do: str

The operator-facing remediation paragraph parsed from the **What to do:** ... line. One-paragraph guidance: what does a row in this matview mean for the integrator, and what should they do about it. Empty string when the section omits the line (a soft contract – AA.C.2 added the line to all 8 sections in L1_Invariants.md and AA.C.3.f tests pin every kind has one).

recon_gen.common.handbook.invariants.load_bundled_invariants(*, strip_jinja=True)[source]

Read the bundled L1_Invariants.md from recon_gen.docs and return parsed sections.

Single call site for the dashboard-side consumers (AA.C.3 + AA.C.5) – they don’t need to know where the doc lives.

Return type:

dict[str, InvariantSection]

Parameters:

strip_jinja (bool)

recon_gen.common.handbook.invariants.panel_markdown(section)[source]

Compose a sheet-bottom panel for an InvariantSection.

Returns a markdown string suitable for passing through rich_text.markdown(...) and into a SheetTextBox content block. The shape, top-to-bottom:

  1. Bold title (the section’s human heading).

  2. The SHOULD-constraint as a blockquote (omitted for the descriptive Supersession Audit section).

  3. The body prose (with the **Columns:** ... line inline, since the column list is useful context for operators reading the panel).

  4. A bold Action. line carrying what_to_do.

AA.C.3 wires one of these per L1 invariant sheet via apps/l1_dashboard/app.py. The L1 Exceptions sheet composes its own intro panel (AA.C.3.e) rather than stacking seven of these.

Return type:

str

Parameters:

section (InvariantSection)

recon_gen.common.handbook.invariants.parse_l1_invariants(md_text, *, strip_jinja=True)[source]

Walk the markdown source and yield one section per recognized heading. Returns {kind: InvariantSection}.

See module docstring for the Jinja stripping contract – pass strip_jinja=False only when you want raw doc content (e.g., re-rendering through the mkdocs pipeline).

Return type:

dict[str, InvariantSection]

Parameters:
  • md_text (str)

  • strip_jinja (bool)