← back to Tk 11331 Exec
scripts/README-d2-sweep.md
82 lines
# TK-11331 Decision 2 — hands-off daily sweep
Finishes D2 (set `global.v_prods_quantity_order_min="5"` + `global.unit_of_measure="Sold Per Yard"`
on the non-compliant X-prefix @52/54 products) automatically, one throttled batch per day, with a
fail-loud verify gate and an auto-stop.
## Files
- `scripts/d2_daily_sweep.sh` — the wrapper (one batch per invocation).
- `deploy/com.steve.tk11331-d2-sweep.plist` — launchd job (04:45 daily). **NOT installed** — Steve installs.
- `data/d2-sweep-state.json` — persisted offset (source of truth for "where are we").
- `data/d2-sweep-runs.log` — human-readable per-run trail.
- `data/sweep-stdout.log` / `data/sweep-stderr.log` — launchd captured output.
- `data/d2-sweep-FAILED` — halt marker (present ⇒ wrapper refuses to run; **fail-loud**).
- `data/d2-sweep-DONE` — completion marker (present ⇒ nothing to do).
## Offset math
- `data/d2_set.json` holds **3600** non-compliant products (indices `0..3599`).
- **431** already applied+verified on Day-1 (indices `0..430`). Sweep resumes at offset **431**.
- Each run: apply `--offset=$OFFSET --limit=1000`, verify, then advance the offset by **1000**.
| Run | offset | rows applied | indices |
|-----|--------|--------------|---------|
| (Day-1, done) | 0 | 431 | 0..430 |
| Day-2 | 431 | 1000 | 431..1430 |
| Day-3 | 1431 | 1000 | 1431..2430 |
| Day-4 | 2431 | 1000 | 2431..3430 |
| Day-5 | 3431 | 169 | 3431..3599 |
3169 remaining ÷ ≤1000/day = **~4 daily runs**. The last day is a partial (169) because
`limit=1000` caps at the end of the set (`rows = min(1000, total - offset)`).
## Per-run behavior
1. Single-flight **lock** (`data/.d2-sweep.lock`) — a second fire while one is running just exits.
2. If `data/d2-sweep-FAILED` exists → **HALT** (exit 1), do nothing. Manual clear required.
3. If `data/d2-sweep-DONE` exists → nothing to do.
4. If `offset >= 3600` → write DONE, self-disable, ledger completion, exit.
5. `node d2_metafields.mjs --apply --offset=$OFFSET --limit=1000`
— **PG mirror first, then Shopify** (authoritative), per rule 5. Records old values to `data/d2-restore-map.jsonl`.
6. `node verify_d2.mjs $OFFSET 1000` — re-reads via the **Shopify Admin API (authoritative, NOT the CDN)**.
7. **Fail-loud verify gate:** if `NON-compliant != 0` (or verify output is unreadable / `checked=0`)
→ write `data/d2-sweep-FAILED`, **do NOT advance the offset**, exit 1. The offset stays put so a
fixed re-run reprocesses the same slice (writes are idempotent — same values).
8. Append the batch to `~/.claude/yolo-queue/executed-reversible/ledger.jsonl` via `log-exec.mjs`
(`ticket=TK-11331`, `action=d2-daily-batch`, `blast_radius=<rows>`, `undo`, `verify`).
9. Advance the offset in `data/d2-sweep-state.json`.
10. When the advance reaches `>= 3600` → write DONE, ledger completion, **self-disable** the launchd
job (`launchctl bootout gui/$UID/com.steve.tk11331-d2-sweep`).
## Idempotency & safety
- Re-running the same offset re-writes the identical values ("5" / "Sold Per Yard") — safe, no drift.
- On a bad verify the offset does **not** advance, so nothing is skipped.
- `RunAtLoad=false` — a reboot never fires an unattended batch on boot; it waits for the next 04:45.
## Install (Steve — GATED, one approve)
```sh
cp ~/Projects/tk-11331-exec/deploy/com.steve.tk11331-d2-sweep.plist ~/Library/LaunchAgents/
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.steve.tk11331-d2-sweep.plist
```
## Manual kill switch
```sh
launchctl bootout gui/$(id -u)/com.steve.tk11331-d2-sweep
# optional, after DONE: rm ~/Library/LaunchAgents/com.steve.tk11331-d2-sweep.plist
```
## Clear a FAILED halt (after investigating)
```sh
cat ~/Projects/tk-11331-exec/data/d2-sweep-FAILED # read the reason first
rm ~/Projects/tk-11331-exec/data/d2-sweep-FAILED # then the next 04:45 run resumes at the same offset
```
## Rollback
```sh
cd ~/Projects/tk-11331-exec && node d2_rollback.mjs --apply # reverts the full data/d2-restore-map.jsonl
```
## Run one batch manually (test / catch-up, outside launchd)
```sh
bash ~/Projects/tk-11331-exec/scripts/d2_daily_sweep.sh
```
(Still throttled to one ≤1000-row batch per invocation.)