# T861 — sweep checkpoint resume budget

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

## Goal

完成後 Sweep 會逐筆 checkpoint，在中斷、重跑或點數耗盡時只處理未完成候選，並準確回報 partial／blocked budget。

## Depends on

- T842、T853、T860

## Inputs

- `apps/backend/internal/module/radar/domain/sweep.go`
- `apps/backend/internal/module/radar/usecase/sweep_run.go`
- `apps/backend/internal/module/job/**`
- Spec §5.3–§5.4、驗收 #18–#20

## Outputs

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

| 路徑 | 動作 | 說明 |
|------|------|------|
| `apps/backend/internal/module/radar/domain/sweep.go` | edit | checkpoint、budget state、deferred count |
| `apps/backend/internal/module/radar/usecase/sweep_progress.go` | add | guarded per-candidate progress |
| `apps/backend/internal/module/radar/usecase/sweep_resume_test.go` | add | interruption／quota／race tests |

### 行為變更

- CreditCeiling／方案額度用完停止新 provider 呼叫，已完成保留，剩餘為 deferred。
- Redis lock value 保持 workerID；晚到 worker 不覆寫終態。

### API / 契約（若有）

- status 為 complete／partial_budget／blocked_budget／failed；不以成功空資料表示 budget stop。

## Out of scope

- 不改 usage meter cost 定義、不做 live page mapper。

## Acceptance

- [x] 第五筆中斷重跑不重扣前五筆。
- [x] 多 worker race 不突破 owner ceiling。
- [ ] 指令：

```bash
cd apps/backend && go test ./internal/module/radar/... ./internal/module/job/... -run 'Sweep|Budget|Progress' -race -count=1
```

## Notes

job status 更新必須 guarded；只由 lock owner 續期／釋放。
