recon_gen.common.l2.contract
Per-L2-primitive column contracts (BT.5 — derivation for BT.4 triage).
Pure-function derivation over a loaded L2Instance. The output is a
typed map that names, per L2 entity, the rows on <prefix>_transactions
that entity owns plus the per-column predicates those rows must satisfy.
BT.4’s exception-triage surface walks the result against an observed
runtime snapshot and surfaces each gap as a card with a deep link to
the relevant editor page.
BT.0 lock 4 anchored the design call: the typed L2 primitives carry enough to derive the contract — no new domain model, no DB-side work, no new validator. This module exists so BT.4 doesn’t have to re-walk the primitives itself.
Severability: imports primitives only. The editor-path strings
mirror common/html/_studio_editor_routes.py::_entity_id (the route
shape’s authoritative source) but the link target is just a string —
this module doesn’t import the html layer.
The selectors + predicates are renderer-agnostic on purpose. BT.4’s
SQL composer reads them; a unit test reads them; the same shape would
serve a CLI recon-gen probe if that ever lands.
Functions
|
Derive per-L2-primitive column contracts from a loaded |
Classes
|
Per-(parent, child) chain expectation. |
|
The full derivation result. |
|
One per-column expectation on the rows selected by a contract. |
|
Per-(parent_role, rail, direction) cap. |
|
Per-Rail expectations on rows tagged |
|
Narrows |
|
Per-TransferTemplate expectations on rows tagged |
- class recon_gen.common.l2.contract.ChainEdgeContract(parent, child, is_singleton, fan_in, expected_parent_count, predicates, editor_path)[source]
Bases:
objectPer-(parent, child) chain expectation.
Z.A grammar: a chain with one child encodes required semantics (the child always fires under the parent); a chain with multiple children encodes XOR (exactly one fires per parent invocation).
is_singletoncarries that distinction so BT.4 can phrase the diagnosis appropriately (“missing required child” vs “missing XOR sibling fire”).Singleton chains add
transfer_parent_id NOT NULLas a row-level predicate — under the SPEC, every firing of a required child IS a chain firing, sotransfer_parent_idis always populated. XOR chains do NOT add the predicate: the matview-level “exactly-one-fired” check lives in_v_config_chain_children(BS.5), not at the row level.The chain has no single selector column on the transactions table (the chain identity is parent-name + child-name + transfer_parent_id linkage), so the contract omits
selector— BT.4 composes the SQL against the rows tagged with the child name.- Parameters:
parent (Identifier)
child (Identifier)
is_singleton (bool)
fan_in (bool)
expected_parent_count (int | None)
predicates (tuple[ColumnPredicate, ...])
editor_path (str)
- child: Identifier
- editor_path: str
- expected_parent_count: int | None
- fan_in: bool
- is_singleton: bool
- parent: Identifier
- predicates: tuple[ColumnPredicate, ...]
- class recon_gen.common.l2.contract.ColumnContracts(rails, templates, chain_edges, limits)[source]
Bases:
objectThe full derivation result.
All four collections are tuples (frozen + iteration-stable). Order mirrors the L2Instance declaration order so a test asserting on a specific contract’s index stays deterministic across re-derivations of the same instance.
- Parameters:
rails (tuple[RailContract, ...])
templates (tuple[TemplateContract, ...])
chain_edges (tuple[ChainEdgeContract, ...])
limits (tuple[LimitContract, ...])
- chain_edges: tuple[ChainEdgeContract, ...]
- limits: tuple[LimitContract, ...]
- rails: tuple[RailContract, ...]
- templates: tuple[TemplateContract, ...]
- class recon_gen.common.l2.contract.ColumnPredicate(column, kind, expected)[source]
Bases:
objectOne per-column expectation on the rows selected by a contract.
columnis a plain transactions column name (account_role,amount_direction,transfer_parent_id) OR the dotted formmetadata.<key>for a JSON metadata field. BT.4’s SQL composer knows to translate the dotted form to aJSON_VALUEextraction.kinddiscriminates the predicate shape:equals—column = expected(expectedisstr)one_of—column IN expected(expectedistuple[str, ...])not_null—column IS NOT NULL(expectedisNone)
- Parameters:
column (str)
kind (Literal['equals', 'one_of', 'not_null'])
expected (object)
- column: str
- expected: object
- kind: Literal['equals', 'one_of', 'not_null']
- class recon_gen.common.l2.contract.LimitContract(parent_role, rail, direction, cap, editor_path)[source]
Bases:
objectPer-(parent_role, rail, direction) cap.
LimitSchedule is balance-side (aggregated over
<prefix>_daily_balances) rather than per-row; carrying it as a distinct contract kind keeps BT.4 from having to translatecapinto a row-level predicate. The triage page renders limits in their own panel.- Parameters:
parent_role (Identifier)
rail (RailName)
direction (Literal['Outbound', 'Inbound'])
cap (Decimal)
editor_path (str)
- cap: Decimal
- direction: Literal['Outbound', 'Inbound']
- editor_path: str
- parent_role: Identifier
- rail: RailName
- class recon_gen.common.l2.contract.RailContract(rail_name, leg_kind, selector, predicates, editor_path)[source]
Bases:
objectPer-Rail expectations on rows tagged
rail_name = <name>.leg_kinddiscriminates the source primitive so BT.4 can label the gap card (“Two-leg rail expects …”, “Single-leg rail expects …”) without re-walking the L2.- Parameters:
rail_name (Identifier)
leg_kind (Literal['two_leg', 'single_leg'])
selector (RowSelector)
predicates (tuple[ColumnPredicate, ...])
editor_path (str)
- editor_path: str
- leg_kind: Literal['two_leg', 'single_leg']
- predicates: tuple[ColumnPredicate, ...]
- rail_name: Identifier
- selector: RowSelector
- class recon_gen.common.l2.contract.RowSelector(column, equals)[source]
Bases:
objectNarrows
<prefix>_transactionsto the rows one contract owns.Always a column-equals-literal — the L2 primitives that own transactions rows (Rail / TransferTemplate) each project to exactly one selector column (
rail_name/template_name).- Parameters:
column (str)
equals (str)
- column: str
- equals: str
- class recon_gen.common.l2.contract.TemplateContract(template_name, selector, predicates, leg_rail_names, editor_path)[source]
Bases:
objectPer-TransferTemplate expectations on rows tagged
template_name = <name>.leg_rail_namesis retained on the side (rather than only as aone_ofpredicate overrail_name) so BT.4 can render the template’s per-leg fan-out — the predicate is the gate, the list is the explanation.- Parameters:
template_name (Identifier)
selector (RowSelector)
predicates (tuple[ColumnPredicate, ...])
leg_rail_names (tuple[Identifier, ...])
editor_path (str)
- editor_path: str
- leg_rail_names: tuple[Identifier, ...]
- predicates: tuple[ColumnPredicate, ...]
- selector: RowSelector
- template_name: Identifier
- recon_gen.common.l2.contract.derive_column_contracts(instance)[source]
Derive per-L2-primitive column contracts from a loaded
L2Instance.Pure function. No I/O, no caching, no mutation of the input. Output is BT.4-consumable directly: for each entity, BT.4 SELECTs the rows matching the entity’s selector then evaluates the predicates against the result set.
- Parameters:
instance (
L2Instance) – The L2 model to derive against.- Return type:
- Returns:
A
ColumnContractswith one entry per L2-declared Rail, one per TransferTemplate, one per (parent, child) chain edge, and one per LimitSchedule row.