recon_gen.common.spine.drift
Drift family — concrete Invariant + ViolationGenerator impls.
Two L1 invariants share one generator (the many-to-many edge that motivates the registry):
DriftInvariant — sub-ledger drift. Fires when a LEAF internal account’s stored balance ≠ Σ posted legs at business_day_end. Reads from <prefix>_drift.
LedgerDriftInvariant — parent (ledger) drift. Fires when a PARENT internal account’s stored balance ≠ Σ(child stored balances) + Σ(direct postings). Reads from <prefix>_ledger_drift.
DriftGenerator emits a child account with stored money OFF by magnitude from its leg-total, AND a parent account with stored money equal to the clean leg-total. So the child drifts (stored−computed = magnitude) AND the parent drifts (parent.stored − Σ child.money is also off, by ‑magnitude). One emission, two detectors fire — that’s the spine’s many-to-many edge.
Per the AS.1 RNG convention: scenario_for accepts seed; the generator carries rng: random.Random. Drift’s emission itself is deterministic by construction (one account, one day, one leg) — the RNG hook is there for the convention’s structural uniformity, not because drift needs choice. Anomaly (AT.2) WILL use it.
Classes
|
Emit a child account whose stored money drifts from its leg-total by magnitude, plus a parent account whose stored money equals the CLEAN leg-total. |
|
Sub-ledger drift detector. |
|
Plant a parent+child pair where the parent's stored money diverges from Σ child stored by |
|
Parent-ledger drift detector. |
- class recon_gen.common.spine.drift.DriftGenerator(child_account_id, child_role, parent_role, parent_account_id, parent_account_role, anchor_day, magnitude, rng=<factory>, leg_amount=100.0, prefix='spec_example')[source]
Bases:
objectEmit a child account whose stored money drifts from its leg-total by magnitude, plus a parent account whose stored money equals the CLEAN leg-total. Result: child drifts (stored−computed = magnitude) AND parent drifts (parent.stored − Σ child.money = −magnitude).
The pre-AS.3 simple shape: one day, one account pair, one leg. AS.3 promotes this to a stateful day-by-day fold; AS.4 generalizes to cross-account vector state.
- Parameters:
child_account_id (str)
child_role (str)
parent_role (str)
parent_account_id (str | None)
parent_account_role (str | None)
anchor_day (date)
magnitude (float)
rng (Random)
leg_amount (float)
prefix (str)
- property also_trips_ledger_drift: RuleViolation | None
when this generator’s child-drift propagates up to the parent’s _ledger_drift. None when no parent account is present in the shape (the L2 instance has no account with the child’s parent_role).
- Type:
The secondary edge
- anchor_day: date
- child_account_id: str
- child_role: str
- property claimed_accounts: frozenset[str]
The child account_id this plant drifts + the parent account_id when the shape has one (ledger_drift edge fires on the parent). AV.5.
- emit(conn, *, scenario_id=None)[source]
- Return type:
None- Parameters:
conn (SyncConnection)
scenario_id (str | None)
- property intended: RuleViolation
- leg_amount: float = 100.0
Clean leg amount; the child’s stored money is this + magnitude.
- magnitude: float
- parent_account_id: str | None
- parent_account_role: str | None
- parent_role: str
- prefix: str = 'spec_example'
- rng: Random
- class recon_gen.common.spine.drift.DriftInvariant(prefix='spec_example')[source]
Bases:
objectSub-ledger drift detector. Persona-blind (no L2 join in the matview SQL), so scenario_for(role) resolves against any leaf internal account with that role.
- Parameters:
prefix (str)
- name: ClassVar[str] = 'drift'
- prefix: str = 'spec_example'
Prefix of the deployed L2 instance’s matviews. Concrete invariants carry this so detect() can read the right matview; AS.1’s Protocol stayed minimal (no prefix field) since not every invariant variant needs one.
- scenario_for(role, *, magnitude=5.0, seed=None, instance=None, child_account_id=None, parent_account_id=None)[source]
Resolve the role against the shape and return a generator that manufactures a drift breach on a leaf account of that role.
instance=None loads the bundled spec_example; AS.x callers thread the real instance.
AY.4.c — child_account_id / parent_account_id override the default synthetic IDs. The plant adapter (AY.4.c.3) threads OLD DriftPlant.account_id through these kwargs so N drift plants on the same role produce N distinct generators (the default f”acct-drift-child-{role}” derivation would collide). Existing test callers can pass nothing → preserves the synthetic defaults byte-stable.
- Return type:
- Parameters:
role (str)
magnitude (float)
seed (int | None)
instance (L2Instance | None)
child_account_id (str | None)
parent_account_id (str | None)
- class recon_gen.common.spine.drift.LedgerDriftGenerator(parent_account_id, parent_role, child_account_id, child_role, anchor_day, delta, rng=<factory>, leg_amount=100.0, prefix='spec_example')[source]
Bases:
objectPlant a parent+child pair where the parent’s stored money diverges from Σ child stored by
delta. Child is CLEAN (stored matches its single leg), so onlyledger_driftfires — notdrift.Distinct from DriftGenerator (which fires BOTH drift on the child + ledger_drift on the parent as a coupled pair): this generator targets ledger_drift in isolation, modeling the real-world case where a control account is adjusted directly without touching the sub-ledger leaves.
Uses a UNIQUE synthetic
parent_roleso the matview’scomputed_ledger_balancefor our synthetic parent only sums OUR synthetic child (no bleed from baseline accounts that happen to share a real parent_role).- Parameters:
parent_account_id (str)
parent_role (str)
child_account_id (str)
child_role (str)
anchor_day (date)
delta (float)
rng (Random)
leg_amount (float)
prefix (str)
- anchor_day: date
- child_account_id: str
- child_role: str
- property claimed_accounts: frozenset[str]
- delta: float
- emit(conn, *, scenario_id=None)[source]
- Return type:
None- Parameters:
conn (SyncConnection)
scenario_id (str | None)
- property intended: RuleViolation
- leg_amount: float = 100.0
- parent_account_id: str
- parent_role: str
- prefix: str = 'spec_example'
- rng: Random
- class recon_gen.common.spine.drift.LedgerDriftInvariant(prefix='spec_example')[source]
Bases:
objectParent-ledger drift detector. Fires when a parent’s stored money ≠ Σ(child stored) + Σ(direct legs). DriftGenerator’s child-drift causes Σ(child stored) to shift — so this fires on the parent too.
- Parameters:
prefix (str)
- name: ClassVar[str] = 'ledger_drift'
- prefix: str = 'spec_example'