OutboxEventStore stages SQL event rows and outbox rows using its inner
store’s provider transaction. The non-SQL fallback only writes to the inner
store; it does not create outbox entries. The outbox table is not created
automatically. An SQLite installation using the current relay needs a table
with event_id (unique text), event_type, event_data, metadata,
timestamp, status, and nullable published_at columns. Provision and
migrate the schema before enabling the relay.
Acknowledged handoff
Section titled “Acknowledged handoff”A process-local EventBusImpl.publish() acknowledges enqueue, not durable
delivery or successful handler execution. OutboxPublisher therefore no
longer treats its legacy event_bus argument as an acknowledgement. Its
SQLite path requires an explicit acknowledged_sink implementing
AcknowledgedEventSink.deliver(event): return None only after a durable
handoff, and raise on failure. The caller is responsible for using a sink
that actually meets this contract. No such production sink is supplied here.
The PostgreSQL relay is currently refused rather than claiming unverified
behavior.
Register every SQLite dataclass event type on the inner SqliteEventStore
with register_event_type(ClassName, EventClass), or provide a trusted custom
event serializer/deserializer. The default relay reconstructs declared types,
checks the event class and ID, and rejects payloads that do not round-trip.
Unknown or malformed events remain pending; the relay logs failures. A custom
serializer is responsible for preserving the complete event and its types.
Relay calls must run outside a database transaction. The relay requires
local SQLiteProvider (direct or through DatabaseService) and, if pooled,
the local SQLiteConnectionPool; both must advertise the committed-read
capability. Older wheels, other providers and third-party pools are refused.
These checks prevent uncommitted rows from being delivered on the tested local
connection paths. A successful handoff is followed by a conditional update to
published; failure to update leaves the row pending or raises. A crash,
concurrent relays, or a failed update can produce duplicate handoffs. The
sink or downstream consumer must deduplicate by event ID or otherwise be
idempotent. Neither exactly-once delivery nor per-stream ordering is promised.
Failed entries remain pending and can fill the batch indefinitely, so operators
need monitoring and a retry/dead-letter policy outside this component.
The SQLite checks use local providers and sink doubles. They do not prove a production transport, multiple-process behavior, generated-application wiring, PostgreSQL behavior, or end-to-end delivery. Builder event effects remain blocked until their transaction, transport, schema and generated wiring are proven together.