Skip to content

Commit a3a0da5

Browse files
authored
feat: add recoverable memory outbox (#108)
## Outcome Implements the memory-adapter reference semantics for the transactional outbox in #97, without changing AWS persistence yet. - atomically copy-on-write commits the sendable email, provider-neutral message, recipients, optional idempotency claim, and deterministic outbox item - validates linkage, pristine state, schedule/due time, unique envelope recipients, and unexpired idempotency - conditionally leases due items with exact expiry recovery and one active owner - publishes stable `job_id` values, acknowledges dispatch, and safely retries queue failures - preserves the same job identity after queue-acceptance ambiguity - supports continuous sweeps with AbortSignal shutdown - exposes privacy-safe due, leased, undispatched, oldest-age, and failure counters - injects failures at every staged commit/write and queue boundary Closes #97 Parent direction: #81 Execution plan: #84 ## Local evidence - `npm run check` - `npm run check:conformance` - `npm test` (25 files, 201 tests; 21 focused outbox tests) - `npm run build` - `npm run lint:openapi` - `sam validate --lint --template-file template.yaml --region us-east-1` - `actionlint` - `npm audit --audit-level=low` (0 vulnerabilities) - `npm outdated --json` (`{}`) - `npm pack --dry-run --json` (new port and reconciler included) - `gitleaks git --staged` (no leaks) - `git diff --check` Signed-off-by: Yusuke Hayashi <yusuke8h@gmail.com>
1 parent ae8875c commit a3a0da5

7 files changed

Lines changed: 1287 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ minor releases before v1.0.
1414
conformance cases, and generated schemas that CI checks for drift.
1515
- Define versioned provider-neutral message, recipient, attempt, provider-event,
1616
and outbox records with privacy-safe diagnostics and deterministic identities.
17+
- Add an atomic memory-store delivery commit and continuously recoverable
18+
deterministic outbox reconciler with lease and failure metrics.
1719

1820
## 0.1.0 - 2026-07-26
1921

docs/delivery-model.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,27 @@ Timestamps are offset-aware ISO 8601 values. Record schema version, provider
5454
name, provider adapter version, and capability document version are explicit
5555
so migrations and conformance evidence can reject silent drift.
5656

57+
## Memory outbox reference
58+
59+
`MemoryStore.commitDelivery` is the executable reference for the atomic
60+
boundary. It copy-on-write stages the existing sendable email, provider-neutral
61+
message, recipients, optional idempotency claim, and exactly one generation-zero
62+
dispatch item, then exposes every record in one state swap. A fault before the
63+
swap exposes none of them; process loss after the swap leaves due work for the
64+
reconciler and does not require a client replay.
65+
66+
`OutboxReconciler` conditionally leases due items, publishes a `send_email` job
67+
whose `job_id` is the outbox identity, and acknowledges dispatch. Queue
68+
acceptance followed by process loss can publish the job again after lease
69+
expiry, but both copies have the same identity. Publication failures release
70+
the item immediately with an allowlisted diagnostic category. Scheduled
71+
messages remain undispatched until `due_at`; the same sweep handles the exact
72+
clock boundary.
73+
74+
The privacy-safe metrics are available due count, active lease count, total
75+
undispatched count, oldest due age, and cumulative publication failures. They
76+
contain no address, subject, body, provider response, or queue endpoint.
77+
5778
## Privacy boundary
5879

5980
Addresses are allowed only on recipient records in the customer data plane.

0 commit comments

Comments
 (0)