SqlModelLedger
LedgerPort over host-owned SQLModel sessions (SPEC §8).
Every public method validates tenant_id FIRST (non-empty,
non-whitespace str, else auradefi.errors.TenantIsolationError)
before touching any session, then runs one session/commit per call.
Per-tenant monotonic seqs come from TenantSeqRow (first value 1).
The counter lives in the DB, so a second binding over the same
engine continues the sequence.
__init__
__init__(self, session_factory: Callable[[], Session]) -> None
Bind the HOST's session factory. ZERO I/O happens here.
The constructor stores the factory and nothing else: no engine is built, no connection opened, no DDL emitted. An empty database stays empty until the HOST creates the schema itself.
Parameters
session_factoryrequired, Callable[[], Session]none
upsert
upsert(self, tenant_id: str, txns: Sequence[LedgerTransaction]) -> list[SyncEvent]
Insert or update txns in one tenant's store (SPEC §6.4).
Diffs via auradefi.ledger.upsert.classify; new/changed rows
are written with the tenant's next monotonic seq from
TenantSeqRow (first 1) and emit ADDED events ascending by
seq. Payload-identical redelivery emits no event and bumps no
seq, UNLESS the STORED row is removed, in which case the txn
is resurrected (removed=False, bumped seq, ADDED).
Resurrection keys on the STORED removed flag; incoming
bookkeeping is never adopted. Duplicate ids within txns
raise auradefi.errors.ValidationError before any write.
Parameters
tenant_idrequired, strnone
txnsrequired, Sequence[LedgerTransaction]none
Raises
sync
sync(self, tenant_id: str, cursor: str | None = None, limit: int = 100) -> SyncPage
Page of changes since cursor, ascending seq (SPEC §6.4).
Pages rows with last_modified_seq > decode_cursor(cursor)
ascending: REMOVED iff the stored row is removed, else ADDED.
next_cursor encodes the last event's seq, or the decoded
input when the page is empty; has_more is accurate. A
malformed cursor raises auradefi.errors.CursorError; a
limit below 1 raises auradefi.errors.ValidationError.
Parameters
tenant_idrequired, strnone
cursoroptional, str | None, default Nonenone
limitoptional, int, default 100none
Raises
get
get(self, tenant_id: str, txn_id: str) -> LedgerTransaction
Fetch one transaction within THIS tenant (rule #6).
Raises auradefi.errors.NotFoundError when the id does not
exist in this tenant. Another tenant's transaction is
indistinguishable from a missing one.
Parameters
tenant_idrequired, strnone
txn_idrequired, strnone
Raises
mark_removed
mark_removed(self, tenant_id: str, txn_ids: Sequence[str]) -> list[SyncEvent]
Mark transactions removed (reorg semantics), one tenant.
Any unknown id raises auradefi.errors.NotFoundError BEFORE
any write. Live rows get removed=True with a bumped seq and
emit REMOVED events ascending by seq; an already-removed id is
a silent no-op (no event, no seq bump).
Parameters
tenant_idrequired, strnone
txn_idsrequired, Sequence[str]none
Raises
apply_reorg
apply_reorg(self, tenant_id: str, plan: ReorgPlan) -> list[SyncEvent]
Apply a ReorgPlan: mark_removed then upsert, atomically.
Duplicate ids within plan.add raise
auradefi.errors.ValidationError BEFORE any write. Both
halves run INSIDE ONE session/commit: a failure anywhere rolls
the whole plan back. The tenant is never left half-reorged.
Returns REMOVED then ADDED events ascending by seq.
Parameters
tenant_idrequired, strnone
planrequired, ReorgPlannone