Docs Index
ephemeral payload retention.md

Proof Card: Ephemeral-Payload Retention Reaper

Status: production Last verified: 2026-05-20 against the PR #829 review head at the time of stamping. The card describes the implementation contract; specific commit SHAs are re-stamped on merge by the update-docs workflow rather than per R-round to avoid churn on intermediate review heads (matches the flow-templates.md convention).

Claim

A scheduled job (WorkflowPayloadRetentionJob) sweeps the workflow_central_tasks and workflow_central_instances tables every hour, NULLing the payload column on rows whose payload.mode === 'ephemeral_hosted' AND payload.expiresAt < now. Pairs with the lazy-expiry path in WorkflowCentralOperatorService (which returns 410 on read when an ephemeral payload is past its expiresAt): the lazy path stops serving stale data; this proactive sweep stops it sitting on disk indefinitely after the last read. Closes the "lazy-expiry → bounded sweep" follow-up named in docs/guides/SECURITY-AND-RATE-LIMITING.md ("WorkflowCentral governance without hosting data" section) and addresses Kerry's May-2026 Engineering ToDo item C1.

Source

Tests

Live vs Fixture

Known Gaps

Verification (60-second AI-reviewer recipe)

# Unit suite for the scheduled job (covers start/stop/tick/overlap-skip/error isolation):
npx jest --config=jest.fast.config.cjs tests/unit/services/workflowCentral/WorkflowPayloadRetentionJob.test.ts
# Expected: "Tests: 14 passed, 14 total"

# Unit suite for the repository method (covers the SQL contract end-to-end via in-memory SQLite):
npx jest --config=jest.fast.config.cjs tests/unit/services/workflowCentral/WorkflowCentralRepository.test.ts -t "clearExpiredEphemeralPayloads"
# Expected: "Tests: 21 passed" (plus skipped from unrelated describe blocks)

# Confirm the job is registered in DI and started by the server:
grep -n "WorkflowPayloadRetentionJob" src/inversify/types.ts src/inversify/inversify.config.ts src/index.ts
# Expected: TYPES symbol + binding + start call in Server.start + stop call in Server.stop

# Confirm the canonical Tier-B pattern is mirrored (start/stop/tick/inflight guard/unref):
grep -nE "intervalHandle|inflight|unref" src/services/workflowCentral/WorkflowPayloadRetentionJob.ts
# Expected: matches in start(), stop(), and tick() functions — same shape as EmbeddedRetentionJob