Your Institution — Chains¶
A chain row declares "when {{ parent }} fires, one of these {{ children }} SHOULD fire too". A row with one child encodes "required" (every parent firing must invoke that child); a row with two or more children encodes "XOR" alternation (exactly one of the listed children MUST fire per parent invocation). The L2 Flow Tracing app's Chain Orphans check fails when the expected child firing is missing.
Total: 6 chain rows declared on
spec_example.yaml. The chains diagram on the
overview shows the
edges visually.
| Parent | Children | Cardinality | Description |
|---|---|---|---|
| ExternalReconciliationCycle | ChainChildSpec(name='ReconciliationClosing', fan_in=False, expected_parent_count=None) | required | Every reconciliation cycle SHOULD be closed by a matching ReconciliationClosing leg; an unmatched cycle surfaces as an orphan in the L2FT Chains explorer. |
| ReconciliationLeg | ChainChildSpec(name='MerchantSettlementCycle', fan_in=False, expected_parent_count=None) | required | Demonstrates template-as-chain-child semantics. Every ReconciliationLeg firing initiates a MerchantSettlementCycle reconciliation downstream; all leg_rails of that template share one Transfer and one parent_transfer_id. The L1 chain_parent_disagreement invariant flags any leg that claims a different parent. |
| BatchPayoutTrigger | ChainChildSpec(name='BatchedPayoutBatch', fan_in=True, expected_parent_count=2) | required | Demonstrates N:1 fan-in chain semantics (AB.6 per-child shape). Multiple BatchPayoutTrigger firings (the upstream settlements) share one BatchedPayoutBatch Transfer (the downstream batch payout). The L1 fan_in_disagreement invariant flags batches whose parent_count != expected_parent_count (missing contributor or extra contributor — both ETL bugs). |
| SettlementTimingCycle | ChainChildSpec(name='BatchedPayoutBatch', fan_in=False, expected_parent_count=None) | required | Demonstrates Template-parent + Template-child chain semantics (AG.1 Gap B regression coverage). Without the AG.1 fix, state.firings[SettlementTimingCycle] is empty at chain-emit time and this chain silently emits zero rows — the locked seed surfaces the bug on regression. With the fix, the parent template fires once per business day and the chain threads transfer_parent_id through BatchedPayoutBatch's leg_rail. |
| BulkAccrualSettlement | ChainChildSpec(name='BulkAccrualSettleACH', fan_in=False, expected_parent_count=None), ChainChildSpec(name='BulkAccrualSettleWire', fan_in=False, expected_parent_count=None) | xor | Demonstrates the multi-XOR chain contract: every BulkAccrualSettlement firing SHOULD be followed by exactly ONE of the two settlement-vehicle rails. The L1 multi_xor_violation invariant flags violations: 'missed' when no child fires (the chain.md contract was dropped on the floor) or 'overlap' when both fire (XOR alternation collapsed into a duplicate). |
| DisbursementCycle | ChainChildSpec(name='DisbursementSettleACH', fan_in=False, expected_parent_count=None), ChainChildSpec(name='DisbursementSettleCheck', fan_in=False, expected_parent_count=None) | xor | Demonstrates a TEMPLATE-parent multi-XOR chain: every DisbursementCycle firing SHOULD be settled by exactly ONE of the two vehicle rails (ACH transfer or paper check). The L1 multi_xor_violation invariant flags 'missed' (neither vehicle fired) and 'overlap' (both fired). Unlike the Rail-parent BulkAccrualSettlement chain, the parent here is a TransferTemplate whose leg also fires standalone — the composition that the Template-parent regression coverage exists to protect. |