Description
Contract events flow through the listener → projection → webhook pipeline, and while individual stages have retry/dedup logic, there's no end-to-end exactly-once guarantee spanning all three stages together — a reconnect at the listener stage combined with a retry at the webhook stage can still, in rare interleavings, produce a duplicate customer-visible webhook.
Requirements and Context
- Design an end-to-end idempotency fencing token that originates at event ingestion (ledger + operation index) and is threaded unmodified through projection writes and webhook payloads
- Every stage (projection write, webhook dispatch) must be provably a no-op when replayed with the same fencing token
- Build an integration test harness that deliberately reintroduces duplicate/reordered events at each stage boundary and proves exactly-once delivery holds end-to-end
- Document the fencing token contract for future event producers/consumers
- Proof of work (required): this is a hard/complex issue — the PR description must include (1) a screenshot of the full relevant test suite run showing all tests passing, and (2) a screenshot of a successful project/module build or compile (
npm run build) with no errors. PRs missing either screenshot will not be reviewed.
Suggested Execution
Branch: feat/integration-exactly-once-fencing-tokens
Implement Changes
- Define the fencing token format and thread it through
stellarEventListener.ts → projection services → webhookDeliveryService.ts
- Add idempotency enforcement at each stage keyed on the fencing token, extending
webhookDeduplication.ts patterns to the earlier stages
- Build the chaos-style integration test that duplicates and reorders events at each boundary and asserts the customer-visible webhook count matches the true unique-event count exactly
- Document the fencing token contract
Test and Commit
Run npx vitest run src/__tests__/integration/exactlyOnceDelivery.integration.test.ts — duplicated and reordered events at every stage boundary still produce exactly one customer-visible webhook per unique event. Then run npm run build and confirm it completes with no errors. Attach a screenshot of both the passing test run and the successful build to the PR description as proof of work.
Example Commit Message
feat(integration): end-to-end exactly-once delivery guarantee with idempotency fencing tokens
Closes #<issue>
Guidelines
- Branch from
main, open a PR back to main
- All new code must have corresponding tests
- Run
npm run lint and npm run test before pushing
- Follow existing naming conventions and file structure
- PR description must reference this issue number (e.g.,
Closes #<issue>)
- Keep commits atomic and use conventional commit format
- This is a hard/complex issue: PR must include a screenshot of the test suite passing and a screenshot of a successful build/compile — PRs without both screenshots will be marked incomplete and not merged
Description
Contract events flow through the listener → projection → webhook pipeline, and while individual stages have retry/dedup logic, there's no end-to-end exactly-once guarantee spanning all three stages together — a reconnect at the listener stage combined with a retry at the webhook stage can still, in rare interleavings, produce a duplicate customer-visible webhook.
Requirements and Context
npm run build) with no errors. PRs missing either screenshot will not be reviewed.Suggested Execution
Branch:
feat/integration-exactly-once-fencing-tokensImplement Changes
stellarEventListener.ts→ projection services →webhookDeliveryService.tswebhookDeduplication.tspatterns to the earlier stagesTest and Commit
Run
npx vitest run src/__tests__/integration/exactlyOnceDelivery.integration.test.ts— duplicated and reordered events at every stage boundary still produce exactly one customer-visible webhook per unique event. Then runnpm run buildand confirm it completes with no errors. Attach a screenshot of both the passing test run and the successful build to the PR description as proof of work.Example Commit Message
Guidelines
main, open a PR back tomainnpm run lintandnpm run testbefore pushingCloses #<issue>)