Skip to content

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.