Category: Bug
Difficulty: Medium
Description
TrackingPollWorker.run does, for each shipped escrow reported delivered by the provider:
await this.escrowRepository.markDelivered(escrow.id, deliveredAt);
await this.contractService.recordDelivery(escrow.id);
If recordDelivery throws, the catch logs and moves on. But the escrow is already DELIVERED in the
database, so findShippedWithTracking (which selects state: 'SHIPPED') will not return it again.
The contract is never told, and no retry is possible. The escrow is left permanently out of step
with the chain, and nothing is written to the dead-letter queue either, so the failure is only a log
line.
AutoReleaseWorker in the same directory gets this right: it claims the escrow, calls the chain, and
clears the claim on failure so the next cycle retries.
Location
src/workers/tracking-poll.worker.ts
src/workers/auto-release.worker.ts
src/escrow/escrow.repository.ts
src/dlq/dlq.service.ts
test/unit/tracking-poll.worker.spec.ts
Example commits
fix(workers): make delivery recording recoverable when the contract call fails
test(workers): assert a failed recordDelivery leaves the escrow retryable
Acceptance Criteria
Technical Notes
AutoReleaseWorker.run is the pattern to follow. EscrowRepository already has
markAutoReleaseSubmitting and clearAutoReleaseSubmitting as an example of the claim-and-release
shape.
Enqueueing to the DLQ is the alternative if you prefer not to add another claim column; either is
acceptable, say which you chose and why.
Out of scope: the polling interval and the provider integration itself.
Before you start
- Set up with the steps in CONTRIBUTING.md. Node 22 is required (
.nvmrc), use npm ci rather than npm install, and copy .env.example to .env before running npx prisma generate — the Prisma config reads DATABASE_URL at load.
- Branch from
dev and open your pull request against dev. main is the released baseline.
- Tests that need an authenticated caller should use the
bearer() helper in test/auth-helper.ts, which mints a genuinely signed SEP-10 token. Do not send a raw Stellar address as a bearer token; that path was removed deliberately.
- You may cover more than one issue in a single pull request.
Category: Bug
Difficulty: Medium
Description
TrackingPollWorker.rundoes, for each shipped escrow reported delivered by the provider:If
recordDeliverythrows, the catch logs and moves on. But the escrow is already DELIVERED in thedatabase, so
findShippedWithTracking(which selectsstate: 'SHIPPED') will not return it again.The contract is never told, and no retry is possible. The escrow is left permanently out of step
with the chain, and nothing is written to the dead-letter queue either, so the failure is only a log
line.
AutoReleaseWorkerin the same directory gets this right: it claims the escrow, calls the chain, andclears the claim on failure so the next cycle retries.
Location
src/workers/tracking-poll.worker.tssrc/workers/auto-release.worker.tssrc/escrow/escrow.repository.tssrc/dlq/dlq.service.tstest/unit/tracking-poll.worker.spec.tsExample commits
Acceptance Criteria
recordDeliveryleaves the escrow in a state the next poll cycle picks up again, or enqueues a DLQ record for replay.Technical Notes
AutoReleaseWorker.runis the pattern to follow.EscrowRepositoryalready hasmarkAutoReleaseSubmittingandclearAutoReleaseSubmittingas an example of the claim-and-releaseshape.
Enqueueing to the DLQ is the alternative if you prefer not to add another claim column; either is
acceptable, say which you chose and why.
Out of scope: the polling interval and the provider integration itself.
Before you start
.nvmrc), usenpm cirather thannpm install, and copy.env.exampleto.envbefore runningnpx prisma generate— the Prisma config readsDATABASE_URLat load.devand open your pull request againstdev.mainis the released baseline.bearer()helper intest/auth-helper.ts, which mints a genuinely signed SEP-10 token. Do not send a raw Stellar address as a bearer token; that path was removed deliberately.