# T872 — race retry and duplicate charge regression

> Status: `done`（2026-08-11）
> Milestone: `M7`
> Est. change: `~190 lines`

## Goal

完成後多 worker、provider retry、HTTP retry、跨 watch／query、產品改名／改痛點都證明不重建、不重判、不重扣。

## Depends on

- T852、T853、T861、T871

## Inputs

- `apps/backend/internal/module/radar/repository/**product**_test.go`
- `apps/backend/internal/module/radar/usecase/**_test.go`
- Spec §5.3、驗收 #16–#22

## Outputs

### 程式變更（預期路徑）

| 路徑 | 動作 | 說明 |
|------|------|------|
| `apps/backend/internal/module/radar/usecase/opportunity_inbox_race_test.go` | add | worker／job／provider race matrix |
| `apps/backend/internal/module/radar/repository/opportunity_inbox_race_test.go` | add | tombstone／judgment uniqueness |
| `apps/backend/internal/module/usage/**_test.go` | edit | usage idempotency assertion |

### 行為變更

- duplicate_judgment_or_charge_count 必須為 0；產品只改名稱不改 input version，實質改痛點才開新版本。

### API / 契約（若有）

- Redis lock value、guarded job status、usage source 與唯一鍵符合 spec。

## Out of scope

- 不提高並行數、不做壓力測試平台建置。

## Acceptance

- [x] race test 綠；重跑只處理未完成候選。
- [x] 同候選 accepted／rejected 不多扣；invalid AI 不留有效 cache。
- [ ] 指令：

```bash
cd apps/backend && go test ./internal/module/radar/... ./internal/module/usage/... -race -run 'Inbox|Duplicate|Meter|Sweep' -count=1
```

## Notes

若與 brand-product-radar T774 重疊，保留一份測試並在另一 INDEX 交叉引用。
