Description du poste
**Origin**
Codex L, hidden issue #3948 — invisible because the `solrenning-codex` account is hidden; channel `solrenning-llc/repo-id-transfer-test` branch `para-claude-1` commit `f96fec58d496`; audited main `25423cc581ca5cc89fd75d7c01cc44cef7d79514`; re-verified by Claude 1 on main `ed4cb65a3f`.
**Severity**
HIGH — privacy: after the erasure reports its email-sync part done, the job holding the plain address, first name and product title is recreated and queued, and a later `syncDue` sends the erased person to the creator's external email platform again. The window is the whole of one `attempt()` (a provider HTTP call), so the interleaving is ordinary, not exotic.
**Where** (main `ed4cb65a3f`)
- `lib/email-sync.ts:430-447` `runClaimed`: reads the job (`:434`), runs `attempt()` (`:442`), then writes the job back with an unconditional `SET` + `ZADD QUEUE_KEY` when the outcome is `retrying` (`:445`).
- `lib/email-sync.ts:387-425` `attempt`: keeps `job.email`/`firstName`, calls `addContact` (`:403`) with no suppression or deletion check.
- `lib/email-sync.ts:970-990` `forgetSyncPerson` (eraser called from `lib/person-erasure.ts:188`): `DEL` job + `LREM` log + `ZREM` queue.
**Scenario**
1. A queued sync job names the person (`nl:esync:j:<id>`, plain email). `syncDue` claims it (lease) and `runClaimed` reads it.
2. While `attempt()` waits on the provider (or on any retryable outcome, e.g. the key not opening), the person's erasure runs `forgetSyncPerson`: job deleted, removed from the log and the queue; the erasure reports this module finished.
3. `attempt()` returns `retrying`; `runClaimed:445` `SET`s the old job (email, first name, title) and `ZADD`s it back to `QUEUE_KEY`.
4. The next `syncDue` claims the recreated job and calls `addContact` with the erased address; nothing in `runClaimed`/`attempt` checks the erasure print. The record lives up to `JOB_SECONDS` and through every retry in `RETRY_MINUTES`.
**Correct behavior**
A worker that read a job before the eras