diff --git a/.planning/MILESTONES.md b/.planning/MILESTONES.md index 683cdd0..a5f414d 100644 --- a/.planning/MILESTONES.md +++ b/.planning/MILESTONES.md @@ -1,5 +1,27 @@ # Milestones +## v4.0 Win-the-Drop (Shipped: 2026-06-25) + +**Phases completed:** 7 phases, 29 plans + +**Delivered:** ShopPyBot now completes *verified* orders on limited-release drops and survives multi-hour unattended runs without one fault taking down the rest. + +**Key accomplishments:** + +- **Safety gate (P18):** Central monitor-only mode + concrete `place_order_guarded()` on the `RetailerPlugin` ABC routes all 7 bundled plugins' place-order clicks through one guard, closing the confirmed 6-of-7 `test_mode` hole (CI grep + zero-write integration test). `CheckoutConfig` schema foundation for downstream phases. +- **Verified checkout (P19):** URL-first order-confirmation detector (`core/confirmation.py`) plus idempotent `order_id`/`confirmed_at`/`checkout_attempts` columns — `purchased` is written only on a real order number, never a button click. +- **Checkout profile + form-fill (P20):** 9-key CredentialStore shipping/billing profile, BestBuy + Amazon form-fill, CVV via `getpass` at runtime only, AST CI assertion proving no CVV leaks to logs; no full card data persisted. +- **Bounded retry (P21):** Single `RetryPolicy` in `core/retry.py` shared by supervisor restart and cart-retry; per-step `asyncio.timeout()` per DOM stage; DB idempotency re-read before each attempt (no double-buy). +- **Supervisor + relaunch (P22):** Per-coroutine supervision with failure budget absorbs crashes before the TaskGroup boundary; full browser relaunch (teardown→stealth→proxy→login); `sqlite3.OperationalError` read isolation; per-item timeout; cross-platform SIGTERM/SIGINT teardown bridge. +- **Encrypted sessions (P23):** Fernet-encrypted per-platform cookie persistence (`core/session_store.py`) via raw CDP restore that bypasses the nodriver `set_all()` bug; skips re-login/MFA across restarts, no plaintext on disk. +- **Health surface + server safety (P24):** `HealthRegistry`, expanded `BotService.get_status()` (per-plugin liveness/heartbeat), `health_degraded` fan-out alert, `shoppybot status` CLI, headless pygame import-crash guard. + +**Audit:** `.planning/milestones/v4.0-MILESTONE-AUDIT.md` — 17/17 requirements, 7/7 phases verified, 6/6 integration seams clean, suite 755 passed / 2 skipped. Status `tech_debt` (no blockers). + +**Known deferred items at close: 17** (see STATE.md → Deferred Items) — 7 live-environment UAT scenarios + 7 `human_needed` verifications (deferred per autonomous live-UAT policy), 1 todo (Amazon WAF auto-solve), 2 dormant seeds (SEED-001 repo scrub, SEED-002 release-please) — all explicitly Out of Scope for v4.0. + +--- + ## v3.0 v3.0 (Shipped: 2026-06-10) **Phases completed:** 6 phases, 21 plans, 32 tasks diff --git a/.planning/PROJECT.md b/.planning/PROJECT.md index 2168030..09154d3 100644 --- a/.planning/PROJECT.md +++ b/.planning/PROJECT.md @@ -16,26 +16,38 @@ Target user: technically capable individuals who want automated stock monitoring ## Current State -**Shipped v3.0 Resilience + Ecosystem (2026-06-10).** Built on the v2.0 modular core (`BotService` API behind a CLI-default front-end plus an optional FastAPI web UI; runtime-selected `CredentialStore` with no plaintext on disk). v3.0 added anti-detection (JS stealth + proxy rotation with ban detection; opt-in 2captcha reCAPTCHA-v2 solving with balance check and spend cap), the plugin ecosystem (ABC `difficulty`/`requires_proxy`/`requires_captcha` attrs, `shoppybot plugins list`, a GitHub-wiki registry spec, contributor docs), and price monitoring (per-item target price, price history table, price-drop fan-out alerts, `items price-history` CLI). +**Shipped v4.0 Win-the-Drop — Acquisition Core + Reliability (2026-06-25).** Built on the v3.0 resilience/ecosystem layer and the v2.0 modular core (`BotService` API behind a CLI-default front-end plus an optional FastAPI web UI; runtime-selected `CredentialStore` with no plaintext on disk). v4.0 makes the bot complete *verified* orders on limited-release drops and survive multi-hour unattended runs: a central monitor-only gate + `place_order_guarded()` ABC closing the 6-of-7 `test_mode` hole; order-confirmation detection (`purchased` only on a real order number); checkout profile + BestBuy/Amazon form-fill with CVV-at-runtime; one unified `RetryPolicy` with per-step timeouts and idempotent cart-retry; per-coroutine supervisor with browser relaunch, DB read isolation, per-item timeout, and a SIGTERM/SIGINT teardown bridge; Fernet-encrypted session persistence; and a per-plugin health surface (`get_status`, `health_degraded` alert, `shoppybot status`) plus a headless pygame import-crash guard. -v3.0: 6 phases (12-17) / 21 plans, all complete. Full suite: 548 passed, 2 skipped. +v4.0: 7 phases (18-24) / 29 plans, all complete. Full suite: 755 passed, 2 skipped. Audit status `tech_debt` (no blockers; pre-accepted live-UAT debt). -**Deferred (carried):** v2.0 live cross-OS/UI manual checks (keyring restart persistence, masked-TTY setup, web dashboard live render, 0.0.0.0 warning) tracked in STATE.md → Deferred Items; Amazon WAF CAPTCHA auto-solve deferred (degrades to manual pause). +**Deferred (carried):** all live-environment UAT (monitor-only/confirmation/form-fill/relaunch/SIGTERM/session/headless) tracked in STATE.md → Deferred Items as the operator's pre-production live-buy checklist; Amazon WAF CAPTCHA auto-solve (manual-pause fallback); public-release hardening — git-history scrub/squash (SEED-001) + release-please tagging (SEED-002). -**Key constraints (held):** secrets never in config.yml/logs/SQLite plaintext; GUI optional, CLI default; the credential-managing web UI binds to localhost by default. +**Key constraints (held):** secrets never in config.yml/logs/SQLite plaintext; full card number / CVV never persisted to disk or logs (retailer-saved payment + CVV-at-runtime only); GUI optional, CLI default; the credential-managing web UI binds to localhost by default. -## Current Milestone: v4.0 Win-the-Drop (Acquisition Core + Reliability) +## Next Milestone + +**No active milestone.** v4.0 Win-the-Drop shipped 2026-06-25. Start the next cycle with `/gsd:new-milestone` (questioning → research → requirements → roadmap). Phase numbering continues from 24. + +Candidate directions from the v4.0 deferral list: +- **Public-release hardening** — git-history scrub/squash (SEED-001) + release-please version tagging (SEED-002); a dedicated release milestone. +- **Checkout form-fill for the remaining 5 retailers** (v4.0 covers BestBuy + Amazon). +- **Request/API-mode (hybrid) checkout** — faster than DOM but per-site reverse-engineering and an arms race. +- **Outcome analytics** (success rate, time-to-checkout) built on the BUY-04 order records. +- **Richer health/observability on the web dashboard** (beyond the CLI/status payload). + +
+Shipped: v4.0 Win-the-Drop (Acquisition Core + Reliability) — 2026-06-25 **Goal:** Make ShopPyBot complete *verified* orders on limited-release drops, and survive multi-hour unattended runs without one fault taking everything down. -**Target features:** -- Acquisition Core: checkout profile (shipping/billing) + form-fill on 1-2 reliable retailers (BestBuy, Amazon); order-confirmation capture (mark `purchased` only on a real confirmation, not a button click); bounded retry-on-cart with backoff; per-item/per-step checkout time budget; a central monitor-only run mode that also closes the `test_mode` place-order hole (6 of 7 plugins). -- Always-On Reliability: per-coroutine supervision + backoff restart; browser-crash detection + relaunch; encrypted session/cookie persistence; DB read-path error isolation; per-item orchestrator timeout; structured health/heartbeat surface; one unified transient retry/backoff. +**Delivered features:** +- Acquisition Core: checkout profile (shipping/billing) + form-fill on BestBuy + Amazon; order-confirmation capture (mark `purchased` only on a real confirmation, not a button click); bounded retry-on-cart with backoff; per-item/per-step checkout time budget; a central monitor-only run mode that also closes the `test_mode` place-order hole (6 of 7 plugins). +- Always-On Reliability: per-coroutine supervision + backoff restart; browser-crash detection + relaunch; encrypted session/cookie persistence; DB read-path error isolation; per-item orchestrator timeout; structured health/heartbeat surface; one unified transient retry/backoff (`RetryPolicy`). - Opportunistic server-safety: headless pygame import-crash guard; SIGTERM/SIGINT teardown bridge (stop orphaning Chrome). -**Deferred to follow-on:** request/API-mode checkout, virtual-waiting-room/queue survival (Queue-it/PerimeterX/Akamai/DataDome), multi-account/multi-profile parallel attempts, Amazon WAF auto-solve. All XL arms-race or ToS-hostile. +**Constraints held:** v2.0 security posture (no plaintext secrets, CLI default, web optional/localhost); payment via retailer-saved methods + CVV-at-runtime (never persist full card data, PCI); checkout work targets the `nodriver` plugin stack. -**Key constraints:** preserve the v2.0 security posture (no plaintext secrets, CLI default, web optional/localhost); payment via retailer-saved methods + CVV-at-runtime (never persist full card data, PCI); checkout work targets the `nodriver` plugin stack; continues phase numbering from 17. +
## Requirements @@ -75,19 +87,25 @@ v3.0: 6 phases (12-17) / 21 plans, all complete. Full suite: 548 passed, 2 skipp - ✓ Price monitoring: per-item target price, price history table, price-drop fan-out alerts, price-history CLI — v3.0 (Phase 16) - ✓ Stability: v2.0 audit tech-debt resolved + test hardening (548 tests) — v3.0 (Phases 12, 17) -### Active (v4.0 Win-the-Drop — Acquisition Core + Reliability) +### Validated (shipped v4.0 Win-the-Drop — Acquisition Core + Reliability) + +- ✓ Acquisition: checkout profile (shipping/billing) + form-fill (BestBuy, Amazon) — v4.0 (Phase 20) +- ✓ Acquisition: order-confirmation capture / verified purchase — v4.0 (Phase 19) +- ✓ Acquisition: bounded retry-on-cart with backoff (idempotent, no double-buy) — v4.0 (Phase 21) +- ✓ Acquisition: per-item/per-step checkout time budget — v4.0 (Phases 21, 22) +- ✓ Acquisition: central monitor-only run mode + close test_mode place-order hole — v4.0 (Phase 18) +- ✓ Reliability: per-coroutine supervision + backoff restart — v4.0 (Phase 22) +- ✓ Reliability: browser-crash detection + relaunch — v4.0 (Phase 22) +- ✓ Reliability: encrypted session/cookie persistence — v4.0 (Phase 23) +- ✓ Reliability: DB read-path error isolation — v4.0 (Phase 22) +- ✓ Reliability: per-item orchestrator timeout — v4.0 (Phase 22) +- ✓ Reliability: structured health/heartbeat surface — v4.0 (Phase 24) +- ✓ Reliability: unified RetryPolicy (one backoff source) — v4.0 (Phase 21) +- ✓ Server-safety: headless pygame import-crash guard + SIGTERM/SIGINT teardown bridge — v4.0 (Phases 24, 22) + +### Active (next milestone) -- [ ] Acquisition: checkout profile (shipping/billing) + form-fill (BestBuy, Amazon) -- [ ] Acquisition: order-confirmation capture / verified purchase -- [ ] Acquisition: bounded retry-on-cart with backoff (idempotent, no double-buy) -- [ ] Acquisition: per-item/per-step checkout time budget -- [ ] Acquisition: central monitor-only run mode + close test_mode place-order hole -- [ ] Reliability: per-coroutine supervision + backoff restart -- [ ] Reliability: browser-crash detection + relaunch -- [ ] Reliability: encrypted session/cookie persistence -- [ ] Reliability: DB read-path error isolation -- [ ] Reliability: per-item orchestrator timeout -- [ ] Reliability: structured health/heartbeat surface +_None yet — run `/gsd:new-milestone` to scope the next cycle. See "Next Milestone" above for candidate directions._ ### Deferred @@ -118,6 +136,11 @@ v3.0: 6 phases (12-17) / 21 plans, all complete. Full suite: 548 passed, 2 skipp | (v2.0) Dynamic CredentialStore | OS keyring with encrypted-file fallback for headless Ubuntu, env-var last resort; secrets never plaintext on disk | Chosen | | (v2.0) Local web UI via FastAPI | Most portable across Ubuntu/Windows, clean core/UI separation, optional extra; binds localhost by default | Chosen | | (v2.0 reversal) No secrets in SQLite | Storing creds in the local DB is plaintext-on-disk, weaker than env/keyring; rejected in favor of CredentialStore | Chosen | +| (v4.0) Single `place_order_guarded()` gate on the ABC | One enforcement point honors monitor-only/`test_mode` for all 7 plugins; closed the confirmed 6-of-7 hole instead of patching each plugin | ✓ Good | +| (v4.0) `purchased` only on a confirmed order number | A button click is not a purchase; confirmation-URL + order-id capture is the idempotency anchor that prevents double-buy on retry | ✓ Good | +| (v4.0) One unified `RetryPolicy` | Supervisor-restart and cart-retry share one backoff module so the two retry concepts cannot diverge or compound into a runaway loop | ✓ Good | +| (v4.0) Retailer-saved payment + CVV-at-runtime | Never persist full card/PAN (PCI scope); CVV via `getpass`, never logged; AST CI assertion guards against leaks | ✓ Good | +| (v4.0) Live-environment UAT deferred as tracked debt | Live retail checkout is ToS/legal risk in CI; confirmation/form-fill selectors verified by manual UAT, tracked in STATE.md Deferred Items | — Pending (operator live-buy checklist) | ## Evolution @@ -137,4 +160,4 @@ This document evolves at phase transitions and milestone boundaries. 4. Update Context with current state --- -*Last updated: 2026-06-10 — v4.0 Win-the-Drop milestone started* +*Last updated: 2026-06-25 — after v4.0 Win-the-Drop milestone (shipped)* diff --git a/.planning/RETROSPECTIVE.md b/.planning/RETROSPECTIVE.md new file mode 100644 index 0000000..25792de --- /dev/null +++ b/.planning/RETROSPECTIVE.md @@ -0,0 +1,60 @@ +# Project Retrospective + +*A living document updated after each milestone. Lessons feed forward into future planning.* + +## Milestone: v4.0 — Win-the-Drop + +**Shipped:** 2026-06-25 +**Phases:** 7 (18-24) | **Plans:** 29 | **Suite:** 755 passed, 2 skipped + +### What Was Built +- Safety gate: monitor-only mode + `place_order_guarded()` ABC closing the 6-of-7 `test_mode` place-order hole (P18). +- Verified checkout: order-confirmation detection + `order_id`/`confirmed_at`/`checkout_attempts` columns — `purchased` only on a real order number (P19). +- Checkout profile + BestBuy/Amazon form-fill, CVV-at-runtime, no card data persisted (P20). +- Unified `RetryPolicy` + per-step `asyncio.timeout()` + idempotent cart-retry (P21). +- Per-coroutine supervisor + browser relaunch + DB read isolation + per-item timeout + SIGTERM/SIGINT bridge (P22). +- Fernet-encrypted session persistence via raw CDP restore, bypassing the nodriver `set_all()` bug (P23). +- Health surface (`HealthRegistry`, `get_status`, `health_degraded` alert, `shoppybot status`) + headless pygame crash guard (P24). + +### What Worked +- **Foundation phase first.** P18 (safety gate + `CheckoutConfig`) had "do first, all downstream depend on it" — every later phase could be built and tested with live orders suppressed by monitor-only. No phase risked a real purchase during development. +- **Single enforcement points.** One `place_order_guarded()` on the ABC (not per-plugin patches) and one `RetryPolicy` (not per-call-site loops) — both backed by CI guards (grep/AST) that fail the build on regression. +- **Idempotency designed before its consumer.** P19 added the `order_id` anchor; P21's cart-retry read it. Designing the anchor a phase ahead of the retry that needs it avoided a double-buy redesign. +- **Clean integration close.** Integration checker verified all 6 cross-phase seams with 0 blockers; PLUGIN_API_VERSION stayed 2 (all additions additive). + +### What Was Inefficient +- **Live UAT can't run on the dev box.** 17 live-environment items (confirmation/form-fill/relaunch/SIGTERM/session/headless selectors + scenarios) accumulated as deferred debt. Correct per policy, but the real acceptance bar for an acquisition bot is a live drop, which CI/dev can't exercise. +- **STATE.md drifted stale.** It froze mid-Phase-23 (status "verifying", phase table showing 18-21 "Not started") while ROADMAP.md stayed authoritative. At milestone close this forced a desync diagnosis before any action was safe. +- **ROADMAP Phase Details not pruned at v3.0 close.** Archived v3.0 phases 12-17 left full detail in the live ROADMAP, so `roadmap.analyze` false-positived them as incomplete `no_directory` phases — which would have driven an autonomous run to "re-execute" already-shipped work. + +### Patterns Established +- **Foundation-phase-first** for any milestone that touches a dangerous action (place-order): ship the gate before the feature. +- **Confirmed-outcome idempotency**: never treat a UI action as success; capture a retailer-side identifier and gate retries on it. +- **Security invariants as CI guards**: AST assertion (no CVV in `writeLog` args), grep assertion (no `for attempt in range(` outside `core/retry.py`), zero-write integration test under monitor-only. +- **Explicit UAT-debt ledger** in STATE.md Deferred Items — live checks that can't run in CI are tracked, not silently dropped. + +### Key Lessons +1. **Prune ROADMAP "Phase Details" at every milestone close.** Leaving archived-milestone detail in the live ROADMAP desyncs `roadmap.analyze` and can mislead a future autonomous run. (Fixed this close: live ROADMAP now collapses to per-milestone `
` only.) +2. **Keep STATE.md in sync, or treat ROADMAP as the sole source of truth.** A stale STATE.md cost a diagnosis step at close. Prefer reconciling STATE at phase transitions. +3. **Design idempotency anchors one phase ahead of the retry that reads them** — it removes rework and closes the double-buy window by construction. + +### Cost Observations +- Model mix: not tracked this milestone. +- Notable: milestone closed via `/gsd-autonomous` lifecycle tail after the phase work + audit were already complete; the run's main value was catching the ROADMAP/STATE desync before executing anything. + +--- + +## Cross-Milestone Trends + +### Cumulative Quality + +| Milestone | Tests (suite) | Notable | +|-----------|---------------|---------| +| v2.0 | 341 passed | Modular core + CredentialStore (no plaintext on disk) | +| v3.0 | 548 passed, 2 skipped | Anti-detection + ecosystem + price monitoring | +| v4.0 | 755 passed, 2 skipped | Verified checkout + always-on reliability | + +### Top Lessons (Verified Across Milestones) + +1. Prune archived-milestone detail from the live ROADMAP at close — keeps `roadmap.analyze` accurate and ROADMAP constant-size. +2. Single enforcement points (one ABC gate, one RetryPolicy) backed by CI guards beat per-site patches for safety-critical invariants. diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index bca98da..027ead1 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -11,7 +11,7 @@ - ✅ **v1 Open Source Launch** — Phases 1-6 (shipped 2026-06-03) - ✅ **v2.0 Modular Core + Cross-Platform UX** — Phases 7-11 (shipped 2026-06-06) - ✅ **v3.0 Resilience + Ecosystem** — Phases 12-17 (shipped 2026-06-10) -- **v4.0 Win-the-Drop (Acquisition Core + Reliability)** — Phases 18-24 (in progress) +- ✅ **v4.0 Win-the-Drop (Acquisition Core + Reliability)** — Phases 18-24 (shipped 2026-06-25) --- @@ -27,6 +27,8 @@ - [x] Phase 5: Notification System (5/5 plans) — 2026-06-03 - [x] Phase 6: Platform Expansion (5/5 plans) — 2026-06-03 +Full phase detail archived at `.planning/milestones/v1-phases` (see also `milestones/`). +
@@ -46,382 +48,47 @@ Audit: `.planning/milestones/v2.0-MILESTONE-AUDIT.md` (status: passed).
✅ v3.0 Resilience + Ecosystem (Phases 12-17) — SHIPPED 2026-06-10 -- [x] **Phase 12: Stability Foundation** — Close v2.0 deferred cross-OS checks and resolve 4 audit tech-debt items (completed 2026-06-09) -- [x] **Phase 13: Anti-Detection Layer 1 — Fingerprint + Proxy** — Apply JS fingerprint stealth patch and implement proxy rotation with ban detection (completed 2026-06-09) -- [x] **Phase 14: Anti-Detection Layer 2 — CAPTCHA Solving** — Integrate 2captcha opt-in solver with CredentialStore key, startup balance check, async executor wrapping, and spend cap (completed 2026-06-09) -- [x] **Phase 15: Plugin Ecosystem Registry** — Add difficulty/proxy/captcha class attrs to ABC, create GitHub wiki registry table, ship `shoppybot plugins list` command (completed 2026-06-09) -- [x] **Phase 16: Price Monitoring** — Per-item target price, append-only price history table, percentage-drop trigger, fan-out price-drop alerts, price-history CLI (completed 2026-06-10) -- [x] **Phase 17: Test Hardening** — Unit and integration coverage for all v3.0 features (completed 2026-06-10) +- [x] Phase 12: Stability Foundation (4/4 plans) — 2026-06-09 +- [x] Phase 13: Anti-Detection Layer 1 — Fingerprint + Proxy (3/3 plans) — 2026-06-09 +- [x] Phase 14: Anti-Detection Layer 2 — CAPTCHA Solving (3/3 plans) — 2026-06-09 +- [x] Phase 15: Plugin Ecosystem Registry (3/3 plans) — 2026-06-09 +- [x] Phase 16: Price Monitoring (4/4 plans) — 2026-06-10 +- [x] Phase 17: Test Hardening (4/4 plans) — 2026-06-10 -Full phase detail archived in `.planning/milestones/v3.0-ROADMAP.md`. +Full phase detail archived at `.planning/milestones/v3.0-ROADMAP.md`. +Audit: `.planning/milestones/v3.0-MILESTONE-AUDIT.md`.
-### v4.0 Win-the-Drop (Phases 18-24) - -- [x] **Phase 18: Safety Gate + Config Foundation** — Central monitor-only mode, `place_order_guarded()` ABC method closing the 6-of-7 plugin safety hole, and `CheckoutConfig` schema as the foundation every downstream phase depends on (completed 2026-06-11) -- [x] **Phase 19: DB Schema + Confirmation Detection** — Add `order_id`/`confirmed_at`/`checkout_attempts` columns and build `core/confirmation.py` so `purchased` is only written on a real confirmed order number, never on a button click (completed 2026-06-11) -- [x] **Phase 20: Checkout Profile + Form-Fill** — Shipping/billing profile stored in CredentialStore (9 keys, no card data), BestBuy and Amazon form-fill, CVV getpass-only at runtime (completed 2026-06-11) -- [x] **Phase 21: Per-Step Timeouts + Unified Retry + Cart-Retry** — One `RetryPolicy` in `core/retry.py` shared by both supervisor restart and cart-retry; per-step `asyncio.timeout()` per DOM stage; idempotency guard reads DB before every attempt (completed 2026-06-12) -- [x] **Phase 22: Supervisor + Browser Relaunch + Server Safety** — Per-coroutine supervision with failure budget absorbs crashes before the TaskGroup boundary; full relaunch sequence (teardown, proxy, stealth, login); DB read isolation; per-item orchestrator timeout; SIGTERM/SIGINT teardown bridge (completed 2026-06-12) -- [x] **Phase 23: Encrypted Session Persistence** — Fernet-encrypted cookie save/restore via `core/session_store.py` (reuses `EncryptedFileBackend` pattern); raw CDP restore path that bypasses the confirmed `set_all()` bug; replaces Phase 22's no-op stub (completed 2026-06-12) -- [x] **Phase 24: Health Surface + Server Safety** — `core/health.py` HealthRegistry, expanded `BotService.get_status()` with per-plugin liveness/heartbeat, `health_degraded` notification event, updated FastAPI `/status` endpoint, headless pygame crash guard (completed 2026-06-12) - ---- - -## Phase Details - -### Phase 12: Stability Foundation - -**Goal**: The v2.0 deferred debt and audit tech-debt are paid down before new features land, so the test suite is a reliable baseline -**Depends on**: Nothing — do first -**Requirements**: STAB-01, STAB-02 -**Success Criteria** (what must be TRUE): - - 1. All 4 deferred v2.0 cross-OS/UI manual checks (keyring restart survival, masked-TTY passphrase prompt, web dashboard render on Ubuntu, `0.0.0.0` bind warning) are executed and documented pass or fail, with any failures fixed - 2. Each of the 4 v2.0 audit tech-debt items has a targeted regression test that passes in CI - 3. No broad refactors occur: only the specific items in scope are changed - -**Plans**: 4 plans - -Plans: - -- [x] 12-01-PLAN.md — TD-1: re-anchor logger logging_level read to core.paths.config_path() + regression test -- [x] 12-02-PLAN.md — TD-2/TD-3: harden SC1 secret-read guard (rglob) and separator guard (__file__-anchored) -- [x] 12-03-PLAN.md — TD-4 config write-seam regression test + accepted MOD-02 gap doc; MC-4 0.0.0.0 banner assertion -- [x] 12-04-PLAN.md — Execute and document MC-1..MC-4 deferred manual checks in docs/PLATFORMS.md - -### Phase 13: Anti-Detection Layer 1 — Fingerprint + Proxy - -**Goal**: Users can enable proxy rotation and the bot applies a JS fingerprint stealth patch at browser startup, measurably reducing Layer 2 bot signals -**Depends on**: Phase 12 -**Requirements**: ANTI-08, ANTI-04, ANTI-05 -**Success Criteria** (what must be TRUE): - - 1. Bot applies `window.chrome`, `navigator.plugins`, `navigator.languages`, and screen-dimension patches via `core/stealth.py` at every browser startup with no plugin ABC version bump - 2. User can enable proxy rotation via an opt-in `proxy:` config section (disabled by default) listing `scheme://host:port` URLs; bot logs "Proxy rotation: enabled, pool_size=N" at startup - 3. Bot detects ban signals (HTTP 403/429/503, challenge-redirect, block-phrase body) and rotates to the next proxy, retiring a proxy after N consecutive failures for a configurable cooldown period - 4. WebRTC Chrome preferences are set at browser launch to prevent real-IP leaks through the proxy tunnel - 5. Each proxy is scoped to its plugin instance (`self._proxy`) and rotated only at browser restart, not mid-session - -**Plans**: 3 plans - -Plans: - -- [x] 13-01-PLAN.md — core/stealth.py: STEALTH_JS + apply_stealth, ProxyPool (round-robin/retire/cooldown), proxy launch args + WebRTC flag, CDP Fetch auth, ban-signal detector (+ unit tests) -- [x] 13-02-PLAN.md — ProxyConfig schema (opt-in, disabled by default) + documented sample.config.yml proxy section -- [x] 13-03-PLAN.md — Wire stealth + proxy into BotService/orchestrator/registry and all 8 plugins; per-instance scoping, exact startup log, fail-loud on pool exhaustion, ban-detect recording - -**UI hint**: no - -### Phase 14: Anti-Detection Layer 2 — CAPTCHA Solving - -**Goal**: Users who encounter reCAPTCHA v2 or Amazon WAF CAPTCHAs can opt into automated solving via 2captcha with full cost visibility and no credential plaintext exposure -**Depends on**: Phase 13 (fingerprint + proxy layer in place before adding CAPTCHA layer) -**Requirements**: ANTI-06, ANTI-07 -**Success Criteria** (what must be TRUE): - - 1. User can enable CAPTCHA solving via `captcha.enabled: true` in config; the 2captcha API key is stored exclusively in CredentialStore (`TWOCAPTCHA_API_KEY`), never in config.yml - 2. Bot checks 2captcha account balance at startup, logs a WARNING when balance is low, and skips solver use (falling back to manual pause) when balance is zero - 3. CAPTCHA solve calls use `run_in_executor` + `asyncio.timeout(120)` so other plugin poll tasks are not blocked during a solve - 4. A configurable `captcha.max_solves_per_run` limit prevents unbounded API charges; default config disables CAPTCHA solving - -**Plans**: 3 plans - -Plans: - -- [x] 14-01-PLAN.md — Foundation: TWOCAPTCHA_API_KEY in SECRET_KEYS, CaptchaConfig, CaptchaSolver 2captcha v1 client (submit/poll/balance/cap) -- [x] 14-02-PLAN.md — Wiring: solver constructed fresh in async_main + startup balance check + registry.assign_solver (mirrors ProxyPool) -- [x] 14-03-PLAN.md — Plugin solve path: Amazon + BestBuy reCAPTCHA solve under run_in_executor+timeout(120) with manual-pause fallback; WAF deferred - -### Phase 15: Plugin Ecosystem Registry - -**Goal**: Community contributors have a discoverable registry with clear difficulty ratings, and users can inspect loaded plugins locally without a network call -**Depends on**: Phase 12 -**Requirements**: REG-01, REG-02, REG-03, REG-04 -**Success Criteria** (what must be TRUE): - - 1. Plugin authors can declare `difficulty`, `requires_proxy`, and `requires_captcha` as class attributes on any plugin; existing plugins without these attrs continue to load with sensible defaults (non-breaking) - 2. The GitHub wiki registry table contains required fields for each community plugin: name, platform, domain patterns, maintainer, anti-detection difficulty, methods implemented, last-verified date, proxy-required, captcha-required - 3. Running `shoppybot plugins list` displays all locally loaded plugins with their declared domain patterns, difficulty, and proxy/captcha flags without making a network call - 4. CONTRIBUTING.md and the PR template require contributors to supply `difficulty`, `requires_proxy`, and `requires_captcha` for new plugin submissions - -**Plans**: 3 plans - -Plans: - -- [x] 15-01-PLAN.md — REG-02: add difficulty/requires_proxy/requires_captcha class attrs + __init_subclass__ difficulty validation to RetailerPlugin ABC (non-breaking, PLUGIN_API_VERSION stays 2) + test_plugin_base.py assertions -- [x] 15-02-PLAN.md — REG-03: BotService.list_plugins() over registry._all_plugins + core/cli/plugins.py handler + plugins-list subparser with --json + no-network CLI tests -- [x] 15-03-PLAN.md — REG-01/REG-04: docs/PLUGIN_REGISTRY.md 9-field wiki SPEC + CONTRIBUTING.md/PR-template/PLUGIN_DEV.md attr requirements + tests/test_docs.py - -### Phase 16: Price Monitoring - -**Goal**: Users can track per-item prices, receive fan-out alerts when prices drop to target or by a configured percentage, and inspect price history from the CLI -**Depends on**: Phase 12 -**Requirements**: PRICE-01, PRICE-02, PRICE-03, PRICE-04, PRICE-05, PRICE-06 -**Success Criteria** (what must be TRUE): - - 1. User can set `target_price` (absolute) and `price_drop_pct` (percentage) per item in config; NULL/absent means price monitoring is off for that item - 2. Bot records scraped prices in an append-only `price_history` SQLite table each poll cycle via an optional `get_price()` plugin ABC hook (default returns `None`); the DB migration is idempotent on existing installs - 3. Price-drop alerts are dispatched through the existing fan-out notification dispatcher using a distinct `price_drop` notification_type with dedup columns separate from stock alert columns - 4. Price alert payloads include the current price, target price, and percentage from target - 5. Running `shoppybot items price-history ` displays the last N recorded prices for that item - -**Plans**: 4 plans - -Plans: - -- [x] 16-01-PLAN.md — Data layer: idempotent price_history table + 4 items columns + 8 parameterized price _sync functions + ItemConfig target_price/price_drop_pct (PRICE-01/02/05) -- [x] 16-02-PLAN.md — Plugin + notification contract: NotificationEvent price fields, default-None get_price() ABC hook, real Amazon get_price() + text→cents parser, price_drop notifier branches (PRICE-02/04) -- [x] 16-03-PLAN.md — Orchestrator wiring: _check_and_buy price path, both triggers with separate dedup, single price_drop dispatch, startup config seeding + BotService.get_price_history (PRICE-02/03/04/05) -- [x] 16-04-PLAN.md — CLI: shoppybot items price-history leaf with --limit (default 10), $X.XX table, no network (PRICE-06) - -**UI hint**: yes - -### Phase 17: Test Hardening - -**Goal**: Every new v3.0 feature has unit and integration coverage so regressions are caught by CI before they reach users -**Depends on**: Phases 13, 14, 15, 16 (tests validate the implemented features) -**Requirements**: STAB-03 -**Success Criteria** (what must be TRUE): - - 1. Unit tests cover proxy config parsing, ban-signal detection logic, per-instance proxy scoping, and cooldown/retire logic - 2. Unit tests cover CAPTCHA config parsing, balance-check behavior, executor wrapping, and spend-cap enforcement - 3. Unit tests cover price comparison threshold logic, `price_history` DB schema (including idempotent migration against a v2.0 DB fixture), and price-drop dedup separation from stock-alert dedup - 4. Integration tests cover the plugin ABC additions (`difficulty`, `requires_proxy`, `requires_captcha` defaults and overrides) and the `get_price()` hook being called alongside `check_availability` - -**Plans**: 4 plans - -Plans: - -- [x] 17-01-PLAN.md — Proxy coverage: PX-01..PX-06 (config validator, fetch-handler tasks, ProxyPool edges, registry routing/lifecycle isolation) -- [x] 17-02-PLAN.md — CAPTCHA coverage: CP-01..CP-05 (poll/timeout errors, balance gate, solve_amazon_waf submit/poll/decode) -- [x] 17-03-PLAN.md — Price + get_price integration: PR-01..PR-04 + AB-01, AB-02 (v2.0-schema migration fixture, trigger guards, get_price-alongside-check_availability) -- [x] 17-04-PLAN.md — Plugin ABC: AB-03, AB-04 (metadata overrides + _handle_ban ban→proxy-cooldown bridge) - ---- - -### Phase 18: Safety Gate + Config Foundation - -**Goal**: Every plugin routes its final place-order action through an ABC-enforced gate that honors monitor-only mode, so no plugin — current or future — can place a live order when monitoring is active -**Depends on**: Nothing (do first; all downstream v4.0 phases depend on this config foundation) -**Requirements**: BUY-01, BUY-02 -**Success Criteria** (what must be TRUE): - - 1. User can start the bot with `--monitor-only` CLI flag or `debug.monitor_only: true` in config; bot performs stock checks and fires alerts but `auto_buy` is never called for any plugin - 2. All 7 bundled plugins route their place-order DOM click through `place_order_guarded()` on the RetailerPlugin ABC; a CI test with all 7 plugins and `monitor_only=True` asserts zero calls to the purchase write-queue - 3. BestBuy's confirmed `test_mode` gap is closed: `place_order.click()` is not called when `monitor_only=True` or `test_mode=True` on any plugin - 4. `CheckoutConfig` sub-model exists in `AppConfig` with `item_timeout_secs`, `step_timeout_secs`, `max_cart_retries`, `backoff_base`, `backoff_jitter`, and `alert_on_errors` fields so downstream phases can use them without schema changes - -**Plans**: 4 plans - -Plans: - -**Wave 1** - -- [x] 18-01-PLAN.md — DebugConfig.monitor_only + CheckoutConfig model + AppConfig wiring (BUY-01, BUY-02) -- [x] 18-02-PLAN.md — place_order_guarded() concrete method on RetailerPlugin ABC + unit tests (BUY-02) - -**Wave 2** *(blocked on Wave 1 completion)* - -- [x] 18-03-PLAN.md — orchestrator monitor_only gate + --monitor-only CLI flag + CVV short-circuit + ALLOWLIST (BUY-01) -- [x] 18-04-PLAN.md — reroute all 7 plugins through place_order_guarded + test_safety_gate.py (7-plugin zero-write + grep) (BUY-02) - -### Phase 19: DB Schema + Confirmation Detection - -**Goal**: Purchases are only recorded when the bot has verified a real order number from the retailer's confirmation page, never on a button click alone -**Depends on**: Phase 18 (monitor-only gate must be live before any live checkout UAT of confirmation selectors) -**Requirements**: BUY-03, BUY-04 -**Success Criteria** (what must be TRUE): - - 1. The `items` table has `order_id TEXT`, `confirmed_at TEXT`, and `checkout_attempts INTEGER DEFAULT 0` columns added via idempotent `ALTER TABLE` (same pattern as v3.0 price columns); existing DB installs migrate without data loss - 2. After `auto_buy()` returns, the orchestrator calls `detect_order_confirmation(tab, platform)` and only enqueues `("confirmed", link, order_id, ts)` to the write-queue when a non-None order_id is returned; `purchased=1` is set only at that point - 3. When confirmation is not detected, the bot logs a WARNING and falls back to the legacy `purchased` write tag (no silent failure, no double-buy risk from the confirmation path itself) - 4. Each confirmed checkout writes `order_id` and `confirmed_at` to the DB, providing the idempotency anchor for retry (Phase 21) to read before any re-attempt - -**Plans**: 4 plans - -Plans: - -**Wave 1** *(parallel-safe; no file overlap)* - -- [x] 19-01-PLAN.md — models.py: 3 idempotent confirmation columns (order_id/confirmed_at/checkout_attempts DEFAULT 0, NOT incremented) + update_item_confirmed_sync (BUY-04) -- [x] 19-02-PLAN.md — core/confirmation.py NEW: detect_order_confirmation (URL-first/DOM-backup map, ~3s settle, Amazon orderID URL-param parse, CONFIRMED- sentinel) + FakeTab tests (BUY-03) -- [x] 19-03-PLAN.md — core/plugin_base.py: additive sync get_active_tab() ABC default (returns main_tab; PLUGIN_API_VERSION stays 2) (BUY-03) - -**Wave 2** *(blocked on Wave 1)* - -- [x] 19-04-PLAN.md — orchestrator wiring (_try_auto_buy detect + confirmed/legacy fallback outside any timeout; _dispatch_write confirmed branch) + Amazon/BestBuy _last_tab + get_active_tab override + orchestrator tests (BUY-03, BUY-04) - -**Research flag** (RESOLVED via 19-RESEARCH.md): per-retailer confirmation URL patterns (Amazon `/gp/buy/thankyou`, BestBuy `/checkout/r/thank-you`) are HIGH confidence and hardcoded in `core/confirmation.py`; backup DOM selectors (`#confirmedOrderId`, `.thank-you-order-number`) are MEDIUM confidence — live UAT on a test_mode buy is tracked as UAT debt (STATE.md Deferred Items). - -### Phase 20: Checkout Profile + Form-Fill - -**Goal**: Users can configure a shipping/billing profile that the bot fills during BestBuy and Amazon checkout, with payment using the retailer-saved method plus CVV entered at runtime and no full card data persisted anywhere -**Depends on**: Phase 18 (monitor-only gate required before any form-fill can be tested live); Phase 19 (confirmation detection should precede form-fill so a completed form-fill can be confirmed) -**Requirements**: BUY-07 -**Success Criteria** (what must be TRUE): - - 1. User can run `shoppybot setup checkout-profile` to interactively populate 9 address keys (`CHECKOUT_FIRST_NAME`, `CHECKOUT_LAST_NAME`, `CHECKOUT_ADDRESS_LINE1`, `CHECKOUT_ADDRESS_LINE2`, `CHECKOUT_CITY`, `CHECKOUT_STATE`, `CHECKOUT_ZIP`, `CHECKOUT_COUNTRY`, `CHECKOUT_PHONE`) in the CredentialStore; no card number or CVV is stored - 2. BestBuy and Amazon plugins fill the shipping form fields from the `CheckoutProfile` loaded at plugin setup time; CVV is provided via `getpass` at runtime only - 3. A CI grep assertion confirms no `_cvv` value appears in any `writeLog()` call argument on checkout code paths - 4. If a shipping form field selector returns None (DOM drift), the plugin logs a WARNING with the selector name and returns False without submitting an incomplete form - -**Plans**: 4 plans - -Plans: - -**Wave 1** *(parallel-safe; no file overlap)* - -- [x] 20-01-PLAN.md — core/checkout_profile.py: CHECKOUT_PROFILE_KEYS (9, NOT in SECRET_KEYS) + CheckoutProfile model + load_checkout_profile() incomplete detection + tests (BUY-07) -- [x] 20-02-PLAN.md — `setup checkout-profile` CLI: visible-prompt 9 keys, key-NAME-only output, optional ADDRESS_LINE2, no card/CVV stored (BUY-07) -- [x] 20-03-PLAN.md — CVV threading: Amazon __init__ _cvv=None + orchestrator amz injection (mirrors BestBuy 411-414) + run.py needs_cvv includes amazon.com (BUY-07) - -**Wave 2** *(blocked on 20-01 + 20-03)* - -- [x] 20-04-PLAN.md — Form-fill: _checkout_profile load at setup() + _fill_field + BestBuy shipping fill before place_order_guarded (missing-selector WARN+False) + Amazon CVV skip-if-absent + CVV-not-in-logs AST test (BUY-07) - -### Phase 21: Per-Step Timeouts + Unified Retry + Cart-Retry - -**Goal**: Checkout attempts are bounded in time and retries, and all retry/backoff logic flows through one shared `RetryPolicy` so supervisor restarts and cart retries cannot compound into a runaway loop -**Depends on**: Phase 18 (CheckoutConfig fields), Phase 19 (DB `order_id` idempotency anchor must exist before retry reads it) -**Requirements**: BUY-05, BUY-06, REL-08 -**Success Criteria** (what must be TRUE): - - 1. `core/retry.py` contains a single `RetryPolicy` dataclass and `with_retry()` async helper; both supervisor restart (Phase 22) and cart-retry use this module; a CI grep assertion confirms no standalone `for attempt in range(N)` retry loops exist outside `core/retry.py` - 2. Each checkout DOM step (navigate, add-to-cart, checkout-proceed, CVV entry, place-order click, confirmation wait) runs under its own `asyncio.timeout(step_timeout_secs)` context manager; the entire `auto_buy()` method is NOT wrapped in a single outer timeout - 3. A `checkout_stage` variable tracks progress within `auto_buy()`; on `CancelledError` the stage is logged for post-mortem and the item is not immediately re-submitted - 4. Cart-retry reads the DB `order_id` column before each attempt; if a prior attempt already wrote a confirmed order, the retry loop exits immediately without re-submitting - 5. Cart-retry is bounded by `CheckoutConfig.max_cart_retries` (default 3) with exponential backoff; the retry never re-enters the place-order click stage on an already-attempted order - -**Plans**: 4 plans - -Plans: - -**Wave 1** *(parallel-safe; no file overlap)* - -- [x] 21-01-PLAN.md — core/retry.py NEW: RetryPolicy dataclass + pure compute_delay (seedable jitter) + with_retry async helper; tests/test_no_retry_loops.py AST guard (no `for attempt in range(` outside core/retry.py) (REL-08) -- [x] 21-02-PLAN.md — models.py: increment_checkout_attempts_sync (+1 per call) + get_item_order_state_sync (purchased, order_id idempotency anchor); no schema change (BUY-05) -- [x] 21-03-PLAN.md — per-step asyncio.timeout: self._checkout_stage default on RetailerPlugin ABC + 6 Amazon / 8 BestBuy DOM stages each under their own asyncio.timeout(step_timeout_secs); no outer timeout; per-item ceiling deferred to P22 (BUY-06) - -**Wave 2** *(blocked on 21-01 + 21-02 + 21-03)* - -- [x] 21-04-PLAN.md — orchestrator cart-retry: extract _attempt_buy(plugin, link)->(bool, str|None) + with_retry/RetryPolicy from CheckoutConfig; DB re-read + checkout_attempts increment before each attempt; confirmed order_id short-circuit (no double-buy); single enqueue outside loop (BUY-05, REL-08) - -**Research flag** (RESOLVED via 21-CONTEXT.md + 21-RESEARCH.md): the `checkout_attempts` increment strategy is resolved — increment once per retry ATTEMPT, BEFORE each attempt (not per-cart-add, not confirmed-only). max_cart_retries semantics documented: it is the number of RETRIES after the first attempt, so total attempts = 1 + max_cart_retries (max_cart_retries=0 → single attempt, no retry). - -### Phase 22: Supervisor + Browser Relaunch + Server Safety - -**Goal**: A single plugin crash or browser death cannot take down the rest of the bot; plugins restart automatically with backoff and full stealth/proxy/login restoration; the bot shuts down cleanly on SIGTERM -**Depends on**: Phase 18 (CheckoutConfig for alert_on_errors), Phase 21 (RetryPolicy from core/retry.py; supervisor reuses it) -**Requirements**: REL-01, REL-02, REL-03, REL-05, REL-06, SRV-02 -**Success Criteria** (what must be TRUE): - - 1. An unhandled exception in one plugin's `run_plugin` coroutine is absorbed by the supervisor before the `asyncio.TaskGroup` boundary; all other plugin coroutines keep running (verified by a test that crashes one plugin and asserts others continue) - 2. A plugin that exceeds N failures within a time window is parked (no further restart attempts) and the operator receives a notification through the existing dispatcher - 3. On a dead or disconnected Chrome process, the supervisor calls `plugin.relaunch()` which executes the full sequence: teardown, assign_proxy, setup (new browser + stealth + proxy auth), restore_session (no-op stub until Phase 23), login; `apply_stealth` is verified called on the relaunched browser - 4. Transient `sqlite3.OperationalError` (locked DB) on any `run_in_executor` read path in `run_plugin` is caught and isolated: the poll cycle is skipped, not terminated - 5. Each item's check/buy cycle runs under `asyncio.timeout(item_timeout_secs)`; the `write_queue.put()` calls are placed OUTSIDE the timeout context so a timed-out item cannot orphan a pending DB write - 6. SIGTERM and SIGINT trigger cooperative teardown (write-queue flush + browser teardown) via a platform-appropriate signal bridge (`sys.platform` branch handles Windows `NotImplementedError` on `loop.add_signal_handler`) - -**Plans**: 4 plans - -Plans: - -**Wave 1** *(parallel-safe; disjoint file)* - -- [x] 22-01-PLAN.md — core/plugin_base.py: concrete relaunch() (teardown→setup→restore_session→login; stealth re-injected via setup) + restore_session() no-op stub returns False; PLUGIN_API_VERSION stays 2 (REL-03) - -**Wave 2** *(orchestrator.py edits serialize — same-file, no parallel)* - -- [x] 22-02-PLAN.md — run_plugin hardening: cfg param + sqlite3.OperationalError read isolation on items read + per-item asyncio.timeout(item_timeout_secs); write_queue.put stays outside (REL-05, REL-06) - -**Wave 3** *(blocked on 22-01 + 22-02)* - -- [x] 22-03-PLAN.md — supervise() wrapper + _is_browser_dead_exc + failure budget (alert_on_errors / 600s deque) + park notify + browser-dead assign_proxy→relaunch + async_main create_task wiring (REL-01, REL-02) - -**Wave 4** *(blocked on 22-03)* - -- [x] 22-04-PLAN.md — _register_signals (POSIX add_signal_handler / Windows signal.signal fallback) + _flush_write_queue manual drain + async_main signal registration + pre-teardown flush (SRV-02) - -**Research flag** (RESOLVED via 22-RESEARCH.md): the nodriver stealth-persistence question is answered — `add_script_to_evaluate_on_new_document` is a per-session CDP command that is NOT persisted across `Browser.stop()` + `Browser.create()`; every plugin’s setup() re-injects stealth via apply_stealth, so relaunch() calling setup() is sufficient (HIGH confidence, confirmed against installed nodriver 0.50.3 source). - -### Phase 23: Encrypted Session Persistence - -**Goal**: Users can opt into encrypted cookie persistence so the bot skips re-login and MFA across restarts without storing any auth material in plaintext -**Depends on**: Phase 22 (supervisor calls `restore_session` on relaunch; Phase 22 ships a no-op stub that this phase replaces) -**Requirements**: REL-04 -**Success Criteria** (what must be TRUE): - - 1. When `platforms..session_persistence: true`, cookies are saved after successful login via `core/session_store.py` using Fernet encryption (same scrypt KDF as `EncryptedFileBackend`); no plaintext `.json` or `.pickle` file is written to disk - 2. On bot restart or plugin relaunch, cookies are restored via raw CDP `cdp.storage.set_cookies()` (bypassing the confirmed `CookieJar.set_all()` bug); the restore path returns `True` if a session file existed, `False` otherwise (no crash if file is absent) - 3. The `data/sessions/` directory is in `.gitignore`; a CI check confirms no session file pattern matches are committed - 4. The Phase 22 no-op `restore_session` stub in `plugin.relaunch()` is replaced by the live `SessionStore.restore()` call - -**Plans**: TBD -**Research flag**: NEEDS PLAN-PHASE RESEARCH — `nodriver cdp.storage.set_cookies()` exact import path and `CookieParam` constructor signature should be verified against the installed `nodriver==0.50.3` package before implementing the restore path. The workaround is confirmed from issues #1816/#2020 but the exact API shape needs local verification. - -### Phase 24: Health Surface + Server Safety - -**Goal**: Operators can query per-plugin liveness and error state from the CLI or web UI, receive alerts when a plugin degrades, and run the bot headlessly on a server without a pygame import crash -**Depends on**: Phases 22 and 23 (health surface reads from supervisor + session state; SRV-01 is self-contained but grouped here as a low-dependency finish item) -**Requirements**: REL-07, SRV-01 -**Success Criteria** (what must be TRUE): - - 1. `BotService.get_status()` returns a structured dict including `{"running": bool, "uptime_secs": float, "plugins": {name: {"status": str, "last_heartbeat": float, "consecutive_errors": int, "items_checked": int, "orders_confirmed": int}}}` queryable from CLI (`shoppybot status`) and the FastAPI `/status` endpoint - 2. When a plugin's `consecutive_errors` exceeds the configured `alert_on_errors` threshold, a `health_degraded` event is dispatched through the existing notification fan-out channels - 3. On a headless host with no audio device, the sound notifier degrades to a silent no-op at import time rather than crashing; the bot starts and runs normally without pygame - -**Plans**: 5 plans - -Plans: - -**Wave 1** *(parallel-safe; disjoint files)* - -- [x] 24-01-PLAN.md — core/health.py HealthRegistry (per-plugin status/heartbeat/consecutive_errors/items_checked/orders_confirmed) + JSON-safe snapshot + in-memory armed/disarmed dedup + tests (REL-07) -- [x] 24-02-PLAN.md — utils.py pygame headless guard: _initialize_audio()/_AUDIO_AVAILABLE, play_sound no-op when no device, redundant mixer.init removed + tests (SRV-01) +
+✅ v4.0 Win-the-Drop (Phases 18-24) — SHIPPED 2026-06-25 -**Wave 2** *(blocked on 24-01; disjoint files service/orchestrator/cli)* +- [x] Phase 18: Safety Gate + Config Foundation (4/4 plans) — 2026-06-11 +- [x] Phase 19: DB Schema + Confirmation Detection (4/4 plans) — 2026-06-11 +- [x] Phase 20: Checkout Profile + Form-Fill (4/4 plans) — 2026-06-11 +- [x] Phase 21: Per-Step Timeouts + Unified Retry + Cart-Retry (4/4 plans) — 2026-06-12 +- [x] Phase 22: Supervisor + Browser Relaunch + Server Safety (4/4 plans) — 2026-06-12 +- [x] Phase 23: Encrypted Session Persistence (4/4 plans) — 2026-06-12 +- [x] Phase 24: Health Surface + Server Safety (5/5 plans) — 2026-06-12 -- [x] 24-03-PLAN.md — core/service.py get_status() locked shape {running,uptime_secs,plugins{...}} + _start_time uptime + single HealthRegistry ref wired into async_main; /status endpoint unchanged + JSON-serializable test (REL-07) -- [x] 24-04-PLAN.md — core/orchestrator.py wiring: health=None kwarg through async_main/supervise/run_plugin; heartbeat/items_checked/status transitions/orders_confirmed; health_degraded fire-once+re-arm dedup via existing dispatcher, distinct from plugin_parked + tests (REL-07) -- [x] 24-05-PLAN.md — core/cli/status.py shoppybot status subcommand (per-plugin table + --json, no network, in-process-state note) + subparser registration + tests (REL-07) +Full phase detail archived at `.planning/milestones/v4.0-ROADMAP.md`. +Audit: `.planning/milestones/v4.0-MILESTONE-AUDIT.md` (status: tech_debt — pre-accepted live-UAT debt). -**UI hint**: yes +
--- ## Progress -| Phase | Milestone | Plans | Status | Completed | -|-------|-----------|-------|--------|-----------| -| 1. Foundations + Security | v1 | 5/5 | Complete | 2026-06-02 | -| 2. Plugin Migration | v1 | 6/6 | Complete | 2026-06-03 | -| 3. Community Documentation | v1 | 2/2 | Complete | 2026-06-03 | -| 4. Async Orchestrator | v1 | 5/5 | Complete | 2026-06-03 | -| 5. Notification System | v1 | 5/5 | Complete | 2026-06-03 | -| 6. Platform Expansion | v1 | 5/5 | Complete | 2026-06-03 | -| 7. Modular Core Service | v2.0 | 3/3 | Complete | 2026-06-04 | -| 8. Credential Store | v2.0 | 4/4 | Complete | 2026-06-04 | -| 9. CLI Front-End | v2.0 | 4/4 | Complete | 2026-06-04 | -| 10. Optional Web UI | v2.0 | 4/4 | Complete | 2026-06-04 | -| 11. Cross-Platform Verification | v2.0 | 5/5 | Complete | 2026-06-05 | -| 12. Stability Foundation | v3.0 | 4/4 | Complete | 2026-06-09 | -| 13. Anti-Detection Layer 1 — Fingerprint + Proxy | v3.0 | 3/3 | Complete | 2026-06-09 | -| 14. Anti-Detection Layer 2 — CAPTCHA Solving | v3.0 | 3/3 | Complete | 2026-06-09 | -| 15. Plugin Ecosystem Registry | v3.0 | 3/3 | Complete | 2026-06-09 | -| 16. Price Monitoring | v3.0 | 4/4 | Complete | 2026-06-10 | -| 17. Test Hardening | v3.0 | 4/4 | Complete | 2026-06-10 | -| 18. Safety Gate + Config Foundation | v4.0 | 4/4 | Complete | 2026-06-11 | -| 19. DB Schema + Confirmation Detection | v4.0 | 4/4 | Complete | 2026-06-11 | -| 20. Checkout Profile + Form-Fill | v4.0 | 4/4 | Complete | 2026-06-11 | -| 21. Per-Step Timeouts + Unified Retry + Cart-Retry | v4.0 | 4/4 | Complete | 2026-06-12 | -| 22. Supervisor + Browser Relaunch + Server Safety | v4.0 | 4/4 | Complete | 2026-06-12 | -| 23. Encrypted Session Persistence | v4.0 | 4/4 | Complete | 2026-06-12 | -| 24. Health Surface + Server Safety | v4.0 | 5/5 | Complete | 2026-06-12 | +| Milestone | Phases | Plans | Status | Shipped | +|-----------|--------|-------|--------|---------| +| v1 Open Source Launch | 1-6 | 28/28 | ✅ Shipped | 2026-06-03 | +| v2.0 Modular Core + Cross-Platform UX | 7-11 | 20/20 | ✅ Shipped | 2026-06-06 | +| v3.0 Resilience + Ecosystem | 12-17 | 21/21 | ✅ Shipped | 2026-06-10 | +| v4.0 Win-the-Drop | 18-24 | 29/29 | ✅ Shipped | 2026-06-25 | -All 66 v1+v2.0 requirements satisfied. v3.0: 18 requirements mapped across Phases 12-17. v4.0: 17 requirements mapped across Phases 18-24. +All requirements satisfied across v1 (44) + v2.0 (22) + v3.0 (18) + v4.0 (17). Per-milestone requirement detail in `.planning/milestones/v*-REQUIREMENTS.md`. --- -*Last updated: 2026-06-12 — Phase 24 planned (5 plans, 2 waves)* +*Last updated: 2026-06-25 — v4.0 Win-the-Drop shipped; ready for next milestone (`/gsd:new-milestone`).* diff --git a/.planning/STATE.md b/.planning/STATE.md index 2fb9cb6..05bd245 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -2,15 +2,15 @@ gsd_state_version: 1.0 milestone: v4.0 milestone_name: Win-the-Drop -status: verifying -last_updated: "2026-06-12T21:27:47.343Z" -last_activity: 2026-06-12 +status: Awaiting next milestone +last_updated: "2026-06-25T01:32:12.122Z" +last_activity: 2026-06-25 — Milestone v4.0 completed and archived progress: - total_phases: 13 + total_phases: 7 completed_phases: 7 total_plans: 29 completed_plans: 29 - percent: 54 + percent: 100 --- # ShopPyBot — State @@ -28,30 +28,30 @@ progress: ## Current Position -Phase: 24 -Plan: Not started -Status: Phase complete — ready for verification -Last activity: 2026-06-12 +Phase: Milestone v4.0 complete +Plan: — +Status: Awaiting next milestone +Last activity: 2026-06-25 — Milestone v4.0 completed and archived ## Phase Status | Phase | Goal Summary | Status | Reqs | |-------|-------------|--------|------| -| 18 — Safety Gate + Config Foundation | monitor-only mode + place_order_guarded() ABC + CheckoutConfig schema | Not started | BUY-01, BUY-02 | -| 19 — DB Schema + Confirmation Detection | order_id/confirmed_at columns + core/confirmation.py; purchased only on real order | Not started | BUY-03, BUY-04 | -| 20 — Checkout Profile + Form-Fill | 9-key CredentialStore profile; BestBuy + Amazon form-fill; CVV getpass-only | Not started | BUY-07 | -| 21 — Per-Step Timeouts + Unified Retry + Cart-Retry | core/retry.py RetryPolicy; per-step asyncio.timeout; idempotent cart-retry | Not started | BUY-05, BUY-06, REL-08 | +| 18 — Safety Gate + Config Foundation | monitor-only mode + place_order_guarded() ABC + CheckoutConfig schema | Complete | BUY-01, BUY-02 | +| 19 — DB Schema + Confirmation Detection | order_id/confirmed_at columns + core/confirmation.py; purchased only on real order | Complete | BUY-03, BUY-04 | +| 20 — Checkout Profile + Form-Fill | 9-key CredentialStore profile; BestBuy + Amazon form-fill; CVV getpass-only | Complete | BUY-07 | +| 21 — Per-Step Timeouts + Unified Retry + Cart-Retry | core/retry.py RetryPolicy; per-step asyncio.timeout; idempotent cart-retry | Complete | BUY-05, BUY-06, REL-08 | | 22 — Supervisor + Browser Relaunch + Server Safety | per-coroutine supervision; failure budget; full relaunch sequence; DB read isolation; SIGTERM bridge | Complete | REL-01, REL-02, REL-03, REL-05, REL-06, SRV-02 | -| 23 — Encrypted Session Persistence | core/session_store.py Fernet cookies; CDP restore path; replaces Phase 22 stub | In Progress (1/4 plans) | REL-04 | -| 24 — Health Surface + Server Safety | core/health.py HealthRegistry; get_status() expansion; health_degraded alert; pygame headless guard | Not started | REL-07, SRV-01 | +| 23 — Encrypted Session Persistence | core/session_store.py Fernet cookies; CDP restore path; replaces Phase 22 stub | Complete | REL-04 | +| 24 — Health Surface + Server Safety | core/health.py HealthRegistry; get_status() expansion; health_degraded alert; pygame headless guard | Complete | REL-07, SRV-01 | --- ## Performance Metrics -**Plans completed**: 20 of 20 -**Requirements completed**: SRV-02 (plus all prior phases) -**Phases completed**: 5 of 7 +**Plans completed**: 29 of 29 +**Requirements completed**: 17 of 17 (BUY-01..07, REL-01..08, SRV-01, SRV-02) +**Phases completed**: 7 of 7 **Blockers resolved**: 0 --- @@ -93,7 +93,8 @@ Last activity: 2026-06-12 ### Active Todos -- Run `/gsd:plan-phase 18` to begin Phase 18 planning +- v4.0 complete + archived. Run `/gsd:new-milestone` to scope the next cycle (phase numbering continues from 24). +- Operator: work the v4.0 live-UAT checklist (Deferred Items below) before the first production live-buy. ### Blockers @@ -115,6 +116,26 @@ Items acknowledged and deferred at v2.0 milestone close on 2026-06-05. All are l | uat | Phase 01 — 01-UAT.md | partial (0 pending) | | uat | Phase 19 — per-retailer confirmation selectors (Amazon + BestBuy) | UAT required before Phase 19 finalizes selectors | +### Acknowledged at v4.0 milestone close (2026-06-25) — 17 items + +All deferred per the autonomous live-UAT policy; none are code gaps. This is the operator's pre-production live-buy checklist. Source: `gsd-sdk query audit-open` at close. + +| Category | Item | Status | +|----------|------|--------| +| uat | Phase 18 — live `--monitor-only` run fires alerts but places no order (18-HUMAN-UAT.md) | partial (1 pending) | +| uat | Phase 19 — live Amazon/BestBuy confirmation URL + DOM order-number selectors (19-HUMAN-UAT.md) | partial (3 pending) | +| uat | Phase 20 — live BestBuy/Amazon shipping form-fill selectors + CVV entry (20-HUMAN-UAT.md) | partial (4 pending) | +| uat | Phase 21 — per-step timeout clean-abort under a real slow drop (21-HUMAN-UAT.md) | partial (2 pending) | +| uat | Phase 22 — live supervisor restart + browser relaunch + SIGTERM teardown (22-HUMAN-UAT.md) | partial (2 pending) | +| uat | Phase 23 — live cross-restart MFA/login-skip; persisted session accepted (23-HUMAN-UAT.md) | partial (2 pending) | +| uat | Phase 24 — live headless-server run, no audio device / pygame absent (24-HUMAN-UAT.md) | partial (1 pending) | +| verification | Phases 18-24 — VERIFICATION.md status `human_needed` (automated must-haves passed; live checks deferred) | human_needed (7) | +| todo | Amazon WAF CAPTCHA auto-solve wiring (waf-auto-solve-followup.md) | pending (medium); manual-pause fallback in place | +| seed | SEED-001 — public repo history scrub/squash before release | dormant (release milestone) | +| seed | SEED-002 — release-please automatic version tagging | dormant (release milestone) | + +**Tracked HIGH item (from audit):** Phase 21 place-order-stage timeout double-buy edge (placed-but-unconfirmed) — verify live and consider P22-style hardening. + --- | Phase 18 P02 | 267 | 2 tasks | 2 files | | Phase 18 P18-03 | 8m | 2 tasks | 6 files | @@ -335,4 +356,4 @@ Items acknowledged and deferred at v2.0 milestone close on 2026-06-05. All are l ## Operator Next Steps -- Run `/gsd:plan-phase 18` to begin Phase 18 (Safety Gate + Config Foundation) +- Start the next milestone with /gsd:new-milestone diff --git a/.planning/v4.0-MILESTONE-AUDIT.md b/.planning/milestones/v4.0-MILESTONE-AUDIT.md similarity index 100% rename from .planning/v4.0-MILESTONE-AUDIT.md rename to .planning/milestones/v4.0-MILESTONE-AUDIT.md diff --git a/.planning/REQUIREMENTS.md b/.planning/milestones/v4.0-REQUIREMENTS.md similarity index 97% rename from .planning/REQUIREMENTS.md rename to .planning/milestones/v4.0-REQUIREMENTS.md index a495073..e513e58 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/milestones/v4.0-REQUIREMENTS.md @@ -1,3 +1,12 @@ +# Requirements Archive: v4.0 Win-the-Drop + +**Archived:** 2026-06-25 +**Status:** SHIPPED + +For current requirements, see `.planning/REQUIREMENTS.md`. + +--- + # ShopPyBot — Requirements **Current Milestone:** v4.0 Win-the-Drop (Acquisition Core + Reliability) diff --git a/.planning/milestones/v4.0-ROADMAP.md b/.planning/milestones/v4.0-ROADMAP.md new file mode 100644 index 0000000..bca98da --- /dev/null +++ b/.planning/milestones/v4.0-ROADMAP.md @@ -0,0 +1,427 @@ +# ShopPyBot — Roadmap + +## Project + +**Core Value:** A drop-in plugin framework that lets the community add new retail platform integrations by placing a single Python file in `plugins/` — no core changes required. + +--- + +## Milestones + +- ✅ **v1 Open Source Launch** — Phases 1-6 (shipped 2026-06-03) +- ✅ **v2.0 Modular Core + Cross-Platform UX** — Phases 7-11 (shipped 2026-06-06) +- ✅ **v3.0 Resilience + Ecosystem** — Phases 12-17 (shipped 2026-06-10) +- **v4.0 Win-the-Drop (Acquisition Core + Reliability)** — Phases 18-24 (in progress) + +--- + +## Phases + +
+✅ v1 Open Source Launch (Phases 1-6) — SHIPPED 2026-06-03 + +- [x] Phase 1: Foundations + Security (5/5 plans) — 2026-06-02 +- [x] Phase 2: Plugin Migration (6/6 plans) — 2026-06-03 +- [x] Phase 3: Community Documentation (2/2 plans) — 2026-06-03 +- [x] Phase 4: Async Orchestrator (5/5 plans) — 2026-06-03 +- [x] Phase 5: Notification System (5/5 plans) — 2026-06-03 +- [x] Phase 6: Platform Expansion (5/5 plans) — 2026-06-03 + +
+ +
+✅ v2.0 Modular Core + Cross-Platform UX (Phases 7-11) — SHIPPED 2026-06-06 + +- [x] Phase 7: Modular Core Service (3/3 plans) — 2026-06-04 +- [x] Phase 8: Credential Store (4/4 plans) — 2026-06-04 +- [x] Phase 9: CLI Front-End (4/4 plans) — 2026-06-04 +- [x] Phase 10: Optional Web UI (4/4 plans) — 2026-06-04 +- [x] Phase 11: Cross-Platform Verification (5/5 plans) — 2026-06-05 + +Full phase detail archived at `.planning/milestones/v2.0-ROADMAP.md`. +Audit: `.planning/milestones/v2.0-MILESTONE-AUDIT.md` (status: passed). + +
+ +
+✅ v3.0 Resilience + Ecosystem (Phases 12-17) — SHIPPED 2026-06-10 + +- [x] **Phase 12: Stability Foundation** — Close v2.0 deferred cross-OS checks and resolve 4 audit tech-debt items (completed 2026-06-09) +- [x] **Phase 13: Anti-Detection Layer 1 — Fingerprint + Proxy** — Apply JS fingerprint stealth patch and implement proxy rotation with ban detection (completed 2026-06-09) +- [x] **Phase 14: Anti-Detection Layer 2 — CAPTCHA Solving** — Integrate 2captcha opt-in solver with CredentialStore key, startup balance check, async executor wrapping, and spend cap (completed 2026-06-09) +- [x] **Phase 15: Plugin Ecosystem Registry** — Add difficulty/proxy/captcha class attrs to ABC, create GitHub wiki registry table, ship `shoppybot plugins list` command (completed 2026-06-09) +- [x] **Phase 16: Price Monitoring** — Per-item target price, append-only price history table, percentage-drop trigger, fan-out price-drop alerts, price-history CLI (completed 2026-06-10) +- [x] **Phase 17: Test Hardening** — Unit and integration coverage for all v3.0 features (completed 2026-06-10) + +Full phase detail archived in `.planning/milestones/v3.0-ROADMAP.md`. + +
+ +### v4.0 Win-the-Drop (Phases 18-24) + +- [x] **Phase 18: Safety Gate + Config Foundation** — Central monitor-only mode, `place_order_guarded()` ABC method closing the 6-of-7 plugin safety hole, and `CheckoutConfig` schema as the foundation every downstream phase depends on (completed 2026-06-11) +- [x] **Phase 19: DB Schema + Confirmation Detection** — Add `order_id`/`confirmed_at`/`checkout_attempts` columns and build `core/confirmation.py` so `purchased` is only written on a real confirmed order number, never on a button click (completed 2026-06-11) +- [x] **Phase 20: Checkout Profile + Form-Fill** — Shipping/billing profile stored in CredentialStore (9 keys, no card data), BestBuy and Amazon form-fill, CVV getpass-only at runtime (completed 2026-06-11) +- [x] **Phase 21: Per-Step Timeouts + Unified Retry + Cart-Retry** — One `RetryPolicy` in `core/retry.py` shared by both supervisor restart and cart-retry; per-step `asyncio.timeout()` per DOM stage; idempotency guard reads DB before every attempt (completed 2026-06-12) +- [x] **Phase 22: Supervisor + Browser Relaunch + Server Safety** — Per-coroutine supervision with failure budget absorbs crashes before the TaskGroup boundary; full relaunch sequence (teardown, proxy, stealth, login); DB read isolation; per-item orchestrator timeout; SIGTERM/SIGINT teardown bridge (completed 2026-06-12) +- [x] **Phase 23: Encrypted Session Persistence** — Fernet-encrypted cookie save/restore via `core/session_store.py` (reuses `EncryptedFileBackend` pattern); raw CDP restore path that bypasses the confirmed `set_all()` bug; replaces Phase 22's no-op stub (completed 2026-06-12) +- [x] **Phase 24: Health Surface + Server Safety** — `core/health.py` HealthRegistry, expanded `BotService.get_status()` with per-plugin liveness/heartbeat, `health_degraded` notification event, updated FastAPI `/status` endpoint, headless pygame crash guard (completed 2026-06-12) + +--- + +## Phase Details + +### Phase 12: Stability Foundation + +**Goal**: The v2.0 deferred debt and audit tech-debt are paid down before new features land, so the test suite is a reliable baseline +**Depends on**: Nothing — do first +**Requirements**: STAB-01, STAB-02 +**Success Criteria** (what must be TRUE): + + 1. All 4 deferred v2.0 cross-OS/UI manual checks (keyring restart survival, masked-TTY passphrase prompt, web dashboard render on Ubuntu, `0.0.0.0` bind warning) are executed and documented pass or fail, with any failures fixed + 2. Each of the 4 v2.0 audit tech-debt items has a targeted regression test that passes in CI + 3. No broad refactors occur: only the specific items in scope are changed + +**Plans**: 4 plans + +Plans: + +- [x] 12-01-PLAN.md — TD-1: re-anchor logger logging_level read to core.paths.config_path() + regression test +- [x] 12-02-PLAN.md — TD-2/TD-3: harden SC1 secret-read guard (rglob) and separator guard (__file__-anchored) +- [x] 12-03-PLAN.md — TD-4 config write-seam regression test + accepted MOD-02 gap doc; MC-4 0.0.0.0 banner assertion +- [x] 12-04-PLAN.md — Execute and document MC-1..MC-4 deferred manual checks in docs/PLATFORMS.md + +### Phase 13: Anti-Detection Layer 1 — Fingerprint + Proxy + +**Goal**: Users can enable proxy rotation and the bot applies a JS fingerprint stealth patch at browser startup, measurably reducing Layer 2 bot signals +**Depends on**: Phase 12 +**Requirements**: ANTI-08, ANTI-04, ANTI-05 +**Success Criteria** (what must be TRUE): + + 1. Bot applies `window.chrome`, `navigator.plugins`, `navigator.languages`, and screen-dimension patches via `core/stealth.py` at every browser startup with no plugin ABC version bump + 2. User can enable proxy rotation via an opt-in `proxy:` config section (disabled by default) listing `scheme://host:port` URLs; bot logs "Proxy rotation: enabled, pool_size=N" at startup + 3. Bot detects ban signals (HTTP 403/429/503, challenge-redirect, block-phrase body) and rotates to the next proxy, retiring a proxy after N consecutive failures for a configurable cooldown period + 4. WebRTC Chrome preferences are set at browser launch to prevent real-IP leaks through the proxy tunnel + 5. Each proxy is scoped to its plugin instance (`self._proxy`) and rotated only at browser restart, not mid-session + +**Plans**: 3 plans + +Plans: + +- [x] 13-01-PLAN.md — core/stealth.py: STEALTH_JS + apply_stealth, ProxyPool (round-robin/retire/cooldown), proxy launch args + WebRTC flag, CDP Fetch auth, ban-signal detector (+ unit tests) +- [x] 13-02-PLAN.md — ProxyConfig schema (opt-in, disabled by default) + documented sample.config.yml proxy section +- [x] 13-03-PLAN.md — Wire stealth + proxy into BotService/orchestrator/registry and all 8 plugins; per-instance scoping, exact startup log, fail-loud on pool exhaustion, ban-detect recording + +**UI hint**: no + +### Phase 14: Anti-Detection Layer 2 — CAPTCHA Solving + +**Goal**: Users who encounter reCAPTCHA v2 or Amazon WAF CAPTCHAs can opt into automated solving via 2captcha with full cost visibility and no credential plaintext exposure +**Depends on**: Phase 13 (fingerprint + proxy layer in place before adding CAPTCHA layer) +**Requirements**: ANTI-06, ANTI-07 +**Success Criteria** (what must be TRUE): + + 1. User can enable CAPTCHA solving via `captcha.enabled: true` in config; the 2captcha API key is stored exclusively in CredentialStore (`TWOCAPTCHA_API_KEY`), never in config.yml + 2. Bot checks 2captcha account balance at startup, logs a WARNING when balance is low, and skips solver use (falling back to manual pause) when balance is zero + 3. CAPTCHA solve calls use `run_in_executor` + `asyncio.timeout(120)` so other plugin poll tasks are not blocked during a solve + 4. A configurable `captcha.max_solves_per_run` limit prevents unbounded API charges; default config disables CAPTCHA solving + +**Plans**: 3 plans + +Plans: + +- [x] 14-01-PLAN.md — Foundation: TWOCAPTCHA_API_KEY in SECRET_KEYS, CaptchaConfig, CaptchaSolver 2captcha v1 client (submit/poll/balance/cap) +- [x] 14-02-PLAN.md — Wiring: solver constructed fresh in async_main + startup balance check + registry.assign_solver (mirrors ProxyPool) +- [x] 14-03-PLAN.md — Plugin solve path: Amazon + BestBuy reCAPTCHA solve under run_in_executor+timeout(120) with manual-pause fallback; WAF deferred + +### Phase 15: Plugin Ecosystem Registry + +**Goal**: Community contributors have a discoverable registry with clear difficulty ratings, and users can inspect loaded plugins locally without a network call +**Depends on**: Phase 12 +**Requirements**: REG-01, REG-02, REG-03, REG-04 +**Success Criteria** (what must be TRUE): + + 1. Plugin authors can declare `difficulty`, `requires_proxy`, and `requires_captcha` as class attributes on any plugin; existing plugins without these attrs continue to load with sensible defaults (non-breaking) + 2. The GitHub wiki registry table contains required fields for each community plugin: name, platform, domain patterns, maintainer, anti-detection difficulty, methods implemented, last-verified date, proxy-required, captcha-required + 3. Running `shoppybot plugins list` displays all locally loaded plugins with their declared domain patterns, difficulty, and proxy/captcha flags without making a network call + 4. CONTRIBUTING.md and the PR template require contributors to supply `difficulty`, `requires_proxy`, and `requires_captcha` for new plugin submissions + +**Plans**: 3 plans + +Plans: + +- [x] 15-01-PLAN.md — REG-02: add difficulty/requires_proxy/requires_captcha class attrs + __init_subclass__ difficulty validation to RetailerPlugin ABC (non-breaking, PLUGIN_API_VERSION stays 2) + test_plugin_base.py assertions +- [x] 15-02-PLAN.md — REG-03: BotService.list_plugins() over registry._all_plugins + core/cli/plugins.py handler + plugins-list subparser with --json + no-network CLI tests +- [x] 15-03-PLAN.md — REG-01/REG-04: docs/PLUGIN_REGISTRY.md 9-field wiki SPEC + CONTRIBUTING.md/PR-template/PLUGIN_DEV.md attr requirements + tests/test_docs.py + +### Phase 16: Price Monitoring + +**Goal**: Users can track per-item prices, receive fan-out alerts when prices drop to target or by a configured percentage, and inspect price history from the CLI +**Depends on**: Phase 12 +**Requirements**: PRICE-01, PRICE-02, PRICE-03, PRICE-04, PRICE-05, PRICE-06 +**Success Criteria** (what must be TRUE): + + 1. User can set `target_price` (absolute) and `price_drop_pct` (percentage) per item in config; NULL/absent means price monitoring is off for that item + 2. Bot records scraped prices in an append-only `price_history` SQLite table each poll cycle via an optional `get_price()` plugin ABC hook (default returns `None`); the DB migration is idempotent on existing installs + 3. Price-drop alerts are dispatched through the existing fan-out notification dispatcher using a distinct `price_drop` notification_type with dedup columns separate from stock alert columns + 4. Price alert payloads include the current price, target price, and percentage from target + 5. Running `shoppybot items price-history ` displays the last N recorded prices for that item + +**Plans**: 4 plans + +Plans: + +- [x] 16-01-PLAN.md — Data layer: idempotent price_history table + 4 items columns + 8 parameterized price _sync functions + ItemConfig target_price/price_drop_pct (PRICE-01/02/05) +- [x] 16-02-PLAN.md — Plugin + notification contract: NotificationEvent price fields, default-None get_price() ABC hook, real Amazon get_price() + text→cents parser, price_drop notifier branches (PRICE-02/04) +- [x] 16-03-PLAN.md — Orchestrator wiring: _check_and_buy price path, both triggers with separate dedup, single price_drop dispatch, startup config seeding + BotService.get_price_history (PRICE-02/03/04/05) +- [x] 16-04-PLAN.md — CLI: shoppybot items price-history leaf with --limit (default 10), $X.XX table, no network (PRICE-06) + +**UI hint**: yes + +### Phase 17: Test Hardening + +**Goal**: Every new v3.0 feature has unit and integration coverage so regressions are caught by CI before they reach users +**Depends on**: Phases 13, 14, 15, 16 (tests validate the implemented features) +**Requirements**: STAB-03 +**Success Criteria** (what must be TRUE): + + 1. Unit tests cover proxy config parsing, ban-signal detection logic, per-instance proxy scoping, and cooldown/retire logic + 2. Unit tests cover CAPTCHA config parsing, balance-check behavior, executor wrapping, and spend-cap enforcement + 3. Unit tests cover price comparison threshold logic, `price_history` DB schema (including idempotent migration against a v2.0 DB fixture), and price-drop dedup separation from stock-alert dedup + 4. Integration tests cover the plugin ABC additions (`difficulty`, `requires_proxy`, `requires_captcha` defaults and overrides) and the `get_price()` hook being called alongside `check_availability` + +**Plans**: 4 plans + +Plans: + +- [x] 17-01-PLAN.md — Proxy coverage: PX-01..PX-06 (config validator, fetch-handler tasks, ProxyPool edges, registry routing/lifecycle isolation) +- [x] 17-02-PLAN.md — CAPTCHA coverage: CP-01..CP-05 (poll/timeout errors, balance gate, solve_amazon_waf submit/poll/decode) +- [x] 17-03-PLAN.md — Price + get_price integration: PR-01..PR-04 + AB-01, AB-02 (v2.0-schema migration fixture, trigger guards, get_price-alongside-check_availability) +- [x] 17-04-PLAN.md — Plugin ABC: AB-03, AB-04 (metadata overrides + _handle_ban ban→proxy-cooldown bridge) + +--- + +### Phase 18: Safety Gate + Config Foundation + +**Goal**: Every plugin routes its final place-order action through an ABC-enforced gate that honors monitor-only mode, so no plugin — current or future — can place a live order when monitoring is active +**Depends on**: Nothing (do first; all downstream v4.0 phases depend on this config foundation) +**Requirements**: BUY-01, BUY-02 +**Success Criteria** (what must be TRUE): + + 1. User can start the bot with `--monitor-only` CLI flag or `debug.monitor_only: true` in config; bot performs stock checks and fires alerts but `auto_buy` is never called for any plugin + 2. All 7 bundled plugins route their place-order DOM click through `place_order_guarded()` on the RetailerPlugin ABC; a CI test with all 7 plugins and `monitor_only=True` asserts zero calls to the purchase write-queue + 3. BestBuy's confirmed `test_mode` gap is closed: `place_order.click()` is not called when `monitor_only=True` or `test_mode=True` on any plugin + 4. `CheckoutConfig` sub-model exists in `AppConfig` with `item_timeout_secs`, `step_timeout_secs`, `max_cart_retries`, `backoff_base`, `backoff_jitter`, and `alert_on_errors` fields so downstream phases can use them without schema changes + +**Plans**: 4 plans + +Plans: + +**Wave 1** + +- [x] 18-01-PLAN.md — DebugConfig.monitor_only + CheckoutConfig model + AppConfig wiring (BUY-01, BUY-02) +- [x] 18-02-PLAN.md — place_order_guarded() concrete method on RetailerPlugin ABC + unit tests (BUY-02) + +**Wave 2** *(blocked on Wave 1 completion)* + +- [x] 18-03-PLAN.md — orchestrator monitor_only gate + --monitor-only CLI flag + CVV short-circuit + ALLOWLIST (BUY-01) +- [x] 18-04-PLAN.md — reroute all 7 plugins through place_order_guarded + test_safety_gate.py (7-plugin zero-write + grep) (BUY-02) + +### Phase 19: DB Schema + Confirmation Detection + +**Goal**: Purchases are only recorded when the bot has verified a real order number from the retailer's confirmation page, never on a button click alone +**Depends on**: Phase 18 (monitor-only gate must be live before any live checkout UAT of confirmation selectors) +**Requirements**: BUY-03, BUY-04 +**Success Criteria** (what must be TRUE): + + 1. The `items` table has `order_id TEXT`, `confirmed_at TEXT`, and `checkout_attempts INTEGER DEFAULT 0` columns added via idempotent `ALTER TABLE` (same pattern as v3.0 price columns); existing DB installs migrate without data loss + 2. After `auto_buy()` returns, the orchestrator calls `detect_order_confirmation(tab, platform)` and only enqueues `("confirmed", link, order_id, ts)` to the write-queue when a non-None order_id is returned; `purchased=1` is set only at that point + 3. When confirmation is not detected, the bot logs a WARNING and falls back to the legacy `purchased` write tag (no silent failure, no double-buy risk from the confirmation path itself) + 4. Each confirmed checkout writes `order_id` and `confirmed_at` to the DB, providing the idempotency anchor for retry (Phase 21) to read before any re-attempt + +**Plans**: 4 plans + +Plans: + +**Wave 1** *(parallel-safe; no file overlap)* + +- [x] 19-01-PLAN.md — models.py: 3 idempotent confirmation columns (order_id/confirmed_at/checkout_attempts DEFAULT 0, NOT incremented) + update_item_confirmed_sync (BUY-04) +- [x] 19-02-PLAN.md — core/confirmation.py NEW: detect_order_confirmation (URL-first/DOM-backup map, ~3s settle, Amazon orderID URL-param parse, CONFIRMED- sentinel) + FakeTab tests (BUY-03) +- [x] 19-03-PLAN.md — core/plugin_base.py: additive sync get_active_tab() ABC default (returns main_tab; PLUGIN_API_VERSION stays 2) (BUY-03) + +**Wave 2** *(blocked on Wave 1)* + +- [x] 19-04-PLAN.md — orchestrator wiring (_try_auto_buy detect + confirmed/legacy fallback outside any timeout; _dispatch_write confirmed branch) + Amazon/BestBuy _last_tab + get_active_tab override + orchestrator tests (BUY-03, BUY-04) + +**Research flag** (RESOLVED via 19-RESEARCH.md): per-retailer confirmation URL patterns (Amazon `/gp/buy/thankyou`, BestBuy `/checkout/r/thank-you`) are HIGH confidence and hardcoded in `core/confirmation.py`; backup DOM selectors (`#confirmedOrderId`, `.thank-you-order-number`) are MEDIUM confidence — live UAT on a test_mode buy is tracked as UAT debt (STATE.md Deferred Items). + +### Phase 20: Checkout Profile + Form-Fill + +**Goal**: Users can configure a shipping/billing profile that the bot fills during BestBuy and Amazon checkout, with payment using the retailer-saved method plus CVV entered at runtime and no full card data persisted anywhere +**Depends on**: Phase 18 (monitor-only gate required before any form-fill can be tested live); Phase 19 (confirmation detection should precede form-fill so a completed form-fill can be confirmed) +**Requirements**: BUY-07 +**Success Criteria** (what must be TRUE): + + 1. User can run `shoppybot setup checkout-profile` to interactively populate 9 address keys (`CHECKOUT_FIRST_NAME`, `CHECKOUT_LAST_NAME`, `CHECKOUT_ADDRESS_LINE1`, `CHECKOUT_ADDRESS_LINE2`, `CHECKOUT_CITY`, `CHECKOUT_STATE`, `CHECKOUT_ZIP`, `CHECKOUT_COUNTRY`, `CHECKOUT_PHONE`) in the CredentialStore; no card number or CVV is stored + 2. BestBuy and Amazon plugins fill the shipping form fields from the `CheckoutProfile` loaded at plugin setup time; CVV is provided via `getpass` at runtime only + 3. A CI grep assertion confirms no `_cvv` value appears in any `writeLog()` call argument on checkout code paths + 4. If a shipping form field selector returns None (DOM drift), the plugin logs a WARNING with the selector name and returns False without submitting an incomplete form + +**Plans**: 4 plans + +Plans: + +**Wave 1** *(parallel-safe; no file overlap)* + +- [x] 20-01-PLAN.md — core/checkout_profile.py: CHECKOUT_PROFILE_KEYS (9, NOT in SECRET_KEYS) + CheckoutProfile model + load_checkout_profile() incomplete detection + tests (BUY-07) +- [x] 20-02-PLAN.md — `setup checkout-profile` CLI: visible-prompt 9 keys, key-NAME-only output, optional ADDRESS_LINE2, no card/CVV stored (BUY-07) +- [x] 20-03-PLAN.md — CVV threading: Amazon __init__ _cvv=None + orchestrator amz injection (mirrors BestBuy 411-414) + run.py needs_cvv includes amazon.com (BUY-07) + +**Wave 2** *(blocked on 20-01 + 20-03)* + +- [x] 20-04-PLAN.md — Form-fill: _checkout_profile load at setup() + _fill_field + BestBuy shipping fill before place_order_guarded (missing-selector WARN+False) + Amazon CVV skip-if-absent + CVV-not-in-logs AST test (BUY-07) + +### Phase 21: Per-Step Timeouts + Unified Retry + Cart-Retry + +**Goal**: Checkout attempts are bounded in time and retries, and all retry/backoff logic flows through one shared `RetryPolicy` so supervisor restarts and cart retries cannot compound into a runaway loop +**Depends on**: Phase 18 (CheckoutConfig fields), Phase 19 (DB `order_id` idempotency anchor must exist before retry reads it) +**Requirements**: BUY-05, BUY-06, REL-08 +**Success Criteria** (what must be TRUE): + + 1. `core/retry.py` contains a single `RetryPolicy` dataclass and `with_retry()` async helper; both supervisor restart (Phase 22) and cart-retry use this module; a CI grep assertion confirms no standalone `for attempt in range(N)` retry loops exist outside `core/retry.py` + 2. Each checkout DOM step (navigate, add-to-cart, checkout-proceed, CVV entry, place-order click, confirmation wait) runs under its own `asyncio.timeout(step_timeout_secs)` context manager; the entire `auto_buy()` method is NOT wrapped in a single outer timeout + 3. A `checkout_stage` variable tracks progress within `auto_buy()`; on `CancelledError` the stage is logged for post-mortem and the item is not immediately re-submitted + 4. Cart-retry reads the DB `order_id` column before each attempt; if a prior attempt already wrote a confirmed order, the retry loop exits immediately without re-submitting + 5. Cart-retry is bounded by `CheckoutConfig.max_cart_retries` (default 3) with exponential backoff; the retry never re-enters the place-order click stage on an already-attempted order + +**Plans**: 4 plans + +Plans: + +**Wave 1** *(parallel-safe; no file overlap)* + +- [x] 21-01-PLAN.md — core/retry.py NEW: RetryPolicy dataclass + pure compute_delay (seedable jitter) + with_retry async helper; tests/test_no_retry_loops.py AST guard (no `for attempt in range(` outside core/retry.py) (REL-08) +- [x] 21-02-PLAN.md — models.py: increment_checkout_attempts_sync (+1 per call) + get_item_order_state_sync (purchased, order_id idempotency anchor); no schema change (BUY-05) +- [x] 21-03-PLAN.md — per-step asyncio.timeout: self._checkout_stage default on RetailerPlugin ABC + 6 Amazon / 8 BestBuy DOM stages each under their own asyncio.timeout(step_timeout_secs); no outer timeout; per-item ceiling deferred to P22 (BUY-06) + +**Wave 2** *(blocked on 21-01 + 21-02 + 21-03)* + +- [x] 21-04-PLAN.md — orchestrator cart-retry: extract _attempt_buy(plugin, link)->(bool, str|None) + with_retry/RetryPolicy from CheckoutConfig; DB re-read + checkout_attempts increment before each attempt; confirmed order_id short-circuit (no double-buy); single enqueue outside loop (BUY-05, REL-08) + +**Research flag** (RESOLVED via 21-CONTEXT.md + 21-RESEARCH.md): the `checkout_attempts` increment strategy is resolved — increment once per retry ATTEMPT, BEFORE each attempt (not per-cart-add, not confirmed-only). max_cart_retries semantics documented: it is the number of RETRIES after the first attempt, so total attempts = 1 + max_cart_retries (max_cart_retries=0 → single attempt, no retry). + +### Phase 22: Supervisor + Browser Relaunch + Server Safety + +**Goal**: A single plugin crash or browser death cannot take down the rest of the bot; plugins restart automatically with backoff and full stealth/proxy/login restoration; the bot shuts down cleanly on SIGTERM +**Depends on**: Phase 18 (CheckoutConfig for alert_on_errors), Phase 21 (RetryPolicy from core/retry.py; supervisor reuses it) +**Requirements**: REL-01, REL-02, REL-03, REL-05, REL-06, SRV-02 +**Success Criteria** (what must be TRUE): + + 1. An unhandled exception in one plugin's `run_plugin` coroutine is absorbed by the supervisor before the `asyncio.TaskGroup` boundary; all other plugin coroutines keep running (verified by a test that crashes one plugin and asserts others continue) + 2. A plugin that exceeds N failures within a time window is parked (no further restart attempts) and the operator receives a notification through the existing dispatcher + 3. On a dead or disconnected Chrome process, the supervisor calls `plugin.relaunch()` which executes the full sequence: teardown, assign_proxy, setup (new browser + stealth + proxy auth), restore_session (no-op stub until Phase 23), login; `apply_stealth` is verified called on the relaunched browser + 4. Transient `sqlite3.OperationalError` (locked DB) on any `run_in_executor` read path in `run_plugin` is caught and isolated: the poll cycle is skipped, not terminated + 5. Each item's check/buy cycle runs under `asyncio.timeout(item_timeout_secs)`; the `write_queue.put()` calls are placed OUTSIDE the timeout context so a timed-out item cannot orphan a pending DB write + 6. SIGTERM and SIGINT trigger cooperative teardown (write-queue flush + browser teardown) via a platform-appropriate signal bridge (`sys.platform` branch handles Windows `NotImplementedError` on `loop.add_signal_handler`) + +**Plans**: 4 plans + +Plans: + +**Wave 1** *(parallel-safe; disjoint file)* + +- [x] 22-01-PLAN.md — core/plugin_base.py: concrete relaunch() (teardown→setup→restore_session→login; stealth re-injected via setup) + restore_session() no-op stub returns False; PLUGIN_API_VERSION stays 2 (REL-03) + +**Wave 2** *(orchestrator.py edits serialize — same-file, no parallel)* + +- [x] 22-02-PLAN.md — run_plugin hardening: cfg param + sqlite3.OperationalError read isolation on items read + per-item asyncio.timeout(item_timeout_secs); write_queue.put stays outside (REL-05, REL-06) + +**Wave 3** *(blocked on 22-01 + 22-02)* + +- [x] 22-03-PLAN.md — supervise() wrapper + _is_browser_dead_exc + failure budget (alert_on_errors / 600s deque) + park notify + browser-dead assign_proxy→relaunch + async_main create_task wiring (REL-01, REL-02) + +**Wave 4** *(blocked on 22-03)* + +- [x] 22-04-PLAN.md — _register_signals (POSIX add_signal_handler / Windows signal.signal fallback) + _flush_write_queue manual drain + async_main signal registration + pre-teardown flush (SRV-02) + +**Research flag** (RESOLVED via 22-RESEARCH.md): the nodriver stealth-persistence question is answered — `add_script_to_evaluate_on_new_document` is a per-session CDP command that is NOT persisted across `Browser.stop()` + `Browser.create()`; every plugin’s setup() re-injects stealth via apply_stealth, so relaunch() calling setup() is sufficient (HIGH confidence, confirmed against installed nodriver 0.50.3 source). + +### Phase 23: Encrypted Session Persistence + +**Goal**: Users can opt into encrypted cookie persistence so the bot skips re-login and MFA across restarts without storing any auth material in plaintext +**Depends on**: Phase 22 (supervisor calls `restore_session` on relaunch; Phase 22 ships a no-op stub that this phase replaces) +**Requirements**: REL-04 +**Success Criteria** (what must be TRUE): + + 1. When `platforms..session_persistence: true`, cookies are saved after successful login via `core/session_store.py` using Fernet encryption (same scrypt KDF as `EncryptedFileBackend`); no plaintext `.json` or `.pickle` file is written to disk + 2. On bot restart or plugin relaunch, cookies are restored via raw CDP `cdp.storage.set_cookies()` (bypassing the confirmed `CookieJar.set_all()` bug); the restore path returns `True` if a session file existed, `False` otherwise (no crash if file is absent) + 3. The `data/sessions/` directory is in `.gitignore`; a CI check confirms no session file pattern matches are committed + 4. The Phase 22 no-op `restore_session` stub in `plugin.relaunch()` is replaced by the live `SessionStore.restore()` call + +**Plans**: TBD +**Research flag**: NEEDS PLAN-PHASE RESEARCH — `nodriver cdp.storage.set_cookies()` exact import path and `CookieParam` constructor signature should be verified against the installed `nodriver==0.50.3` package before implementing the restore path. The workaround is confirmed from issues #1816/#2020 but the exact API shape needs local verification. + +### Phase 24: Health Surface + Server Safety + +**Goal**: Operators can query per-plugin liveness and error state from the CLI or web UI, receive alerts when a plugin degrades, and run the bot headlessly on a server without a pygame import crash +**Depends on**: Phases 22 and 23 (health surface reads from supervisor + session state; SRV-01 is self-contained but grouped here as a low-dependency finish item) +**Requirements**: REL-07, SRV-01 +**Success Criteria** (what must be TRUE): + + 1. `BotService.get_status()` returns a structured dict including `{"running": bool, "uptime_secs": float, "plugins": {name: {"status": str, "last_heartbeat": float, "consecutive_errors": int, "items_checked": int, "orders_confirmed": int}}}` queryable from CLI (`shoppybot status`) and the FastAPI `/status` endpoint + 2. When a plugin's `consecutive_errors` exceeds the configured `alert_on_errors` threshold, a `health_degraded` event is dispatched through the existing notification fan-out channels + 3. On a headless host with no audio device, the sound notifier degrades to a silent no-op at import time rather than crashing; the bot starts and runs normally without pygame + +**Plans**: 5 plans + +Plans: + +**Wave 1** *(parallel-safe; disjoint files)* + +- [x] 24-01-PLAN.md — core/health.py HealthRegistry (per-plugin status/heartbeat/consecutive_errors/items_checked/orders_confirmed) + JSON-safe snapshot + in-memory armed/disarmed dedup + tests (REL-07) +- [x] 24-02-PLAN.md — utils.py pygame headless guard: _initialize_audio()/_AUDIO_AVAILABLE, play_sound no-op when no device, redundant mixer.init removed + tests (SRV-01) + +**Wave 2** *(blocked on 24-01; disjoint files service/orchestrator/cli)* + +- [x] 24-03-PLAN.md — core/service.py get_status() locked shape {running,uptime_secs,plugins{...}} + _start_time uptime + single HealthRegistry ref wired into async_main; /status endpoint unchanged + JSON-serializable test (REL-07) +- [x] 24-04-PLAN.md — core/orchestrator.py wiring: health=None kwarg through async_main/supervise/run_plugin; heartbeat/items_checked/status transitions/orders_confirmed; health_degraded fire-once+re-arm dedup via existing dispatcher, distinct from plugin_parked + tests (REL-07) +- [x] 24-05-PLAN.md — core/cli/status.py shoppybot status subcommand (per-plugin table + --json, no network, in-process-state note) + subparser registration + tests (REL-07) + +**UI hint**: yes + +--- + +## Progress + +| Phase | Milestone | Plans | Status | Completed | +|-------|-----------|-------|--------|-----------| +| 1. Foundations + Security | v1 | 5/5 | Complete | 2026-06-02 | +| 2. Plugin Migration | v1 | 6/6 | Complete | 2026-06-03 | +| 3. Community Documentation | v1 | 2/2 | Complete | 2026-06-03 | +| 4. Async Orchestrator | v1 | 5/5 | Complete | 2026-06-03 | +| 5. Notification System | v1 | 5/5 | Complete | 2026-06-03 | +| 6. Platform Expansion | v1 | 5/5 | Complete | 2026-06-03 | +| 7. Modular Core Service | v2.0 | 3/3 | Complete | 2026-06-04 | +| 8. Credential Store | v2.0 | 4/4 | Complete | 2026-06-04 | +| 9. CLI Front-End | v2.0 | 4/4 | Complete | 2026-06-04 | +| 10. Optional Web UI | v2.0 | 4/4 | Complete | 2026-06-04 | +| 11. Cross-Platform Verification | v2.0 | 5/5 | Complete | 2026-06-05 | +| 12. Stability Foundation | v3.0 | 4/4 | Complete | 2026-06-09 | +| 13. Anti-Detection Layer 1 — Fingerprint + Proxy | v3.0 | 3/3 | Complete | 2026-06-09 | +| 14. Anti-Detection Layer 2 — CAPTCHA Solving | v3.0 | 3/3 | Complete | 2026-06-09 | +| 15. Plugin Ecosystem Registry | v3.0 | 3/3 | Complete | 2026-06-09 | +| 16. Price Monitoring | v3.0 | 4/4 | Complete | 2026-06-10 | +| 17. Test Hardening | v3.0 | 4/4 | Complete | 2026-06-10 | +| 18. Safety Gate + Config Foundation | v4.0 | 4/4 | Complete | 2026-06-11 | +| 19. DB Schema + Confirmation Detection | v4.0 | 4/4 | Complete | 2026-06-11 | +| 20. Checkout Profile + Form-Fill | v4.0 | 4/4 | Complete | 2026-06-11 | +| 21. Per-Step Timeouts + Unified Retry + Cart-Retry | v4.0 | 4/4 | Complete | 2026-06-12 | +| 22. Supervisor + Browser Relaunch + Server Safety | v4.0 | 4/4 | Complete | 2026-06-12 | +| 23. Encrypted Session Persistence | v4.0 | 4/4 | Complete | 2026-06-12 | +| 24. Health Surface + Server Safety | v4.0 | 5/5 | Complete | 2026-06-12 | + +All 66 v1+v2.0 requirements satisfied. v3.0: 18 requirements mapped across Phases 12-17. v4.0: 17 requirements mapped across Phases 18-24. + +--- + +*Last updated: 2026-06-12 — Phase 24 planned (5 plans, 2 waves)* diff --git a/.planning/phases/18-safety-gate-config-foundation/18-01-PLAN.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-01-PLAN.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-01-PLAN.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-01-PLAN.md diff --git a/.planning/phases/18-safety-gate-config-foundation/18-01-SUMMARY.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-01-SUMMARY.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-01-SUMMARY.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-01-SUMMARY.md diff --git a/.planning/phases/18-safety-gate-config-foundation/18-02-PLAN.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-02-PLAN.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-02-PLAN.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-02-PLAN.md diff --git a/.planning/phases/18-safety-gate-config-foundation/18-02-SUMMARY.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-02-SUMMARY.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-02-SUMMARY.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-02-SUMMARY.md diff --git a/.planning/phases/18-safety-gate-config-foundation/18-03-PLAN.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-03-PLAN.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-03-PLAN.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-03-PLAN.md diff --git a/.planning/phases/18-safety-gate-config-foundation/18-03-SUMMARY.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-03-SUMMARY.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-03-SUMMARY.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-03-SUMMARY.md diff --git a/.planning/phases/18-safety-gate-config-foundation/18-04-PLAN.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-04-PLAN.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-04-PLAN.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-04-PLAN.md diff --git a/.planning/phases/18-safety-gate-config-foundation/18-04-SUMMARY.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-04-SUMMARY.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-04-SUMMARY.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-04-SUMMARY.md diff --git a/.planning/phases/18-safety-gate-config-foundation/18-CONTEXT.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-CONTEXT.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-CONTEXT.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-CONTEXT.md diff --git a/.planning/phases/18-safety-gate-config-foundation/18-HUMAN-UAT.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-HUMAN-UAT.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-HUMAN-UAT.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-HUMAN-UAT.md diff --git a/.planning/phases/18-safety-gate-config-foundation/18-PATTERNS.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-PATTERNS.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-PATTERNS.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-PATTERNS.md diff --git a/.planning/phases/18-safety-gate-config-foundation/18-RESEARCH.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-RESEARCH.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-RESEARCH.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-RESEARCH.md diff --git a/.planning/phases/18-safety-gate-config-foundation/18-REVIEW.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-REVIEW.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-REVIEW.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-REVIEW.md diff --git a/.planning/phases/18-safety-gate-config-foundation/18-VALIDATION.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-VALIDATION.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-VALIDATION.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-VALIDATION.md diff --git a/.planning/phases/18-safety-gate-config-foundation/18-VERIFICATION.md b/.planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-VERIFICATION.md similarity index 100% rename from .planning/phases/18-safety-gate-config-foundation/18-VERIFICATION.md rename to .planning/milestones/v4.0-phases/18-safety-gate-config-foundation/18-VERIFICATION.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-01-PLAN.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-01-PLAN.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-01-PLAN.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-01-PLAN.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-01-SUMMARY.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-01-SUMMARY.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-01-SUMMARY.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-01-SUMMARY.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-02-PLAN.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-02-PLAN.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-02-PLAN.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-02-PLAN.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-02-SUMMARY.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-02-SUMMARY.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-02-SUMMARY.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-02-SUMMARY.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-03-PLAN.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-03-PLAN.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-03-PLAN.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-03-PLAN.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-03-SUMMARY.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-03-SUMMARY.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-03-SUMMARY.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-03-SUMMARY.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-04-PLAN.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-04-PLAN.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-04-PLAN.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-04-PLAN.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-04-SUMMARY.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-04-SUMMARY.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-04-SUMMARY.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-04-SUMMARY.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-CONTEXT.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-CONTEXT.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-CONTEXT.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-CONTEXT.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-HUMAN-UAT.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-HUMAN-UAT.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-HUMAN-UAT.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-HUMAN-UAT.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-PATTERNS.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-PATTERNS.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-PATTERNS.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-PATTERNS.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-RESEARCH.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-RESEARCH.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-RESEARCH.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-RESEARCH.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-REVIEW.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-REVIEW.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-REVIEW.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-REVIEW.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-VALIDATION.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-VALIDATION.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-VALIDATION.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-VALIDATION.md diff --git a/.planning/phases/19-db-schema-confirmation-detection/19-VERIFICATION.md b/.planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-VERIFICATION.md similarity index 100% rename from .planning/phases/19-db-schema-confirmation-detection/19-VERIFICATION.md rename to .planning/milestones/v4.0-phases/19-db-schema-confirmation-detection/19-VERIFICATION.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-01-PLAN.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-01-PLAN.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-01-PLAN.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-01-PLAN.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-01-SUMMARY.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-01-SUMMARY.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-01-SUMMARY.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-01-SUMMARY.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-02-PLAN.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-02-PLAN.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-02-PLAN.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-02-PLAN.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-02-SUMMARY.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-02-SUMMARY.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-02-SUMMARY.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-02-SUMMARY.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-03-PLAN.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-03-PLAN.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-03-PLAN.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-03-PLAN.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-03-SUMMARY.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-03-SUMMARY.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-03-SUMMARY.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-03-SUMMARY.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-04-PLAN.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-04-PLAN.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-04-PLAN.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-04-PLAN.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-04-SUMMARY.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-04-SUMMARY.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-04-SUMMARY.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-04-SUMMARY.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-CONTEXT.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-CONTEXT.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-CONTEXT.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-CONTEXT.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-HUMAN-UAT.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-HUMAN-UAT.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-HUMAN-UAT.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-HUMAN-UAT.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-PATTERNS.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-PATTERNS.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-PATTERNS.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-PATTERNS.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-RESEARCH.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-RESEARCH.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-RESEARCH.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-RESEARCH.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-REVIEW.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-REVIEW.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-REVIEW.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-REVIEW.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-VALIDATION.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-VALIDATION.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-VALIDATION.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-VALIDATION.md diff --git a/.planning/phases/20-checkout-profile-form-fill/20-VERIFICATION.md b/.planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-VERIFICATION.md similarity index 100% rename from .planning/phases/20-checkout-profile-form-fill/20-VERIFICATION.md rename to .planning/milestones/v4.0-phases/20-checkout-profile-form-fill/20-VERIFICATION.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-01-PLAN.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-01-PLAN.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-01-PLAN.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-01-PLAN.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-01-SUMMARY.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-01-SUMMARY.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-01-SUMMARY.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-01-SUMMARY.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-02-PLAN.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-02-PLAN.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-02-PLAN.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-02-PLAN.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-02-SUMMARY.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-02-SUMMARY.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-02-SUMMARY.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-02-SUMMARY.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-03-PLAN.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-03-PLAN.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-03-PLAN.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-03-PLAN.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-03-SUMMARY.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-03-SUMMARY.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-03-SUMMARY.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-03-SUMMARY.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-04-PLAN.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-04-PLAN.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-04-PLAN.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-04-PLAN.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-04-SUMMARY.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-04-SUMMARY.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-04-SUMMARY.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-04-SUMMARY.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-CONTEXT.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-CONTEXT.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-CONTEXT.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-CONTEXT.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-HUMAN-UAT.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-HUMAN-UAT.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-HUMAN-UAT.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-HUMAN-UAT.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-PATTERNS.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-PATTERNS.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-PATTERNS.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-PATTERNS.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-RESEARCH.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-RESEARCH.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-RESEARCH.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-RESEARCH.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-REVIEW.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-REVIEW.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-REVIEW.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-REVIEW.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-VALIDATION.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-VALIDATION.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-VALIDATION.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-VALIDATION.md diff --git a/.planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-VERIFICATION.md b/.planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-VERIFICATION.md similarity index 100% rename from .planning/phases/21-per-step-timeouts-unified-retry-cart-retry/21-VERIFICATION.md rename to .planning/milestones/v4.0-phases/21-per-step-timeouts-unified-retry-cart-retry/21-VERIFICATION.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-01-PLAN.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-01-PLAN.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-01-PLAN.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-01-PLAN.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-01-SUMMARY.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-01-SUMMARY.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-01-SUMMARY.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-01-SUMMARY.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-02-PLAN.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-02-PLAN.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-02-PLAN.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-02-PLAN.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-02-SUMMARY.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-02-SUMMARY.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-02-SUMMARY.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-02-SUMMARY.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-03-PLAN.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-03-PLAN.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-03-PLAN.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-03-PLAN.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-03-SUMMARY.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-03-SUMMARY.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-03-SUMMARY.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-03-SUMMARY.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-04-PLAN.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-04-PLAN.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-04-PLAN.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-04-PLAN.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-04-SUMMARY.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-04-SUMMARY.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-04-SUMMARY.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-04-SUMMARY.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-CONTEXT.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-CONTEXT.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-CONTEXT.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-CONTEXT.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-HUMAN-UAT.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-HUMAN-UAT.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-HUMAN-UAT.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-HUMAN-UAT.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-PATTERNS.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-PATTERNS.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-PATTERNS.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-PATTERNS.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-RESEARCH.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-RESEARCH.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-RESEARCH.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-RESEARCH.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-REVIEW.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-REVIEW.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-REVIEW.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-REVIEW.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-VALIDATION.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-VALIDATION.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-VALIDATION.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-VALIDATION.md diff --git a/.planning/phases/22-supervisor-browser-relaunch-server-safety/22-VERIFICATION.md b/.planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-VERIFICATION.md similarity index 100% rename from .planning/phases/22-supervisor-browser-relaunch-server-safety/22-VERIFICATION.md rename to .planning/milestones/v4.0-phases/22-supervisor-browser-relaunch-server-safety/22-VERIFICATION.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-01-PLAN.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-01-PLAN.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-01-PLAN.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-01-PLAN.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-01-SUMMARY.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-01-SUMMARY.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-01-SUMMARY.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-01-SUMMARY.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-02-PLAN.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-02-PLAN.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-02-PLAN.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-02-PLAN.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-02-SUMMARY.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-02-SUMMARY.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-02-SUMMARY.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-02-SUMMARY.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-03-PLAN.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-03-PLAN.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-03-PLAN.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-03-PLAN.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-03-SUMMARY.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-03-SUMMARY.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-03-SUMMARY.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-03-SUMMARY.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-04-PLAN.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-04-PLAN.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-04-PLAN.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-04-PLAN.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-04-SUMMARY.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-04-SUMMARY.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-04-SUMMARY.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-04-SUMMARY.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-CONTEXT.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-CONTEXT.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-CONTEXT.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-CONTEXT.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-HUMAN-UAT.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-HUMAN-UAT.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-HUMAN-UAT.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-HUMAN-UAT.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-PATTERNS.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-PATTERNS.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-PATTERNS.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-PATTERNS.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-RESEARCH.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-RESEARCH.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-RESEARCH.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-RESEARCH.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-REVIEW-FIX.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-REVIEW-FIX.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-REVIEW-FIX.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-REVIEW-FIX.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-REVIEW.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-REVIEW.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-REVIEW.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-REVIEW.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-VALIDATION.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-VALIDATION.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-VALIDATION.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-VALIDATION.md diff --git a/.planning/phases/23-encrypted-session-persistence/23-VERIFICATION.md b/.planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-VERIFICATION.md similarity index 100% rename from .planning/phases/23-encrypted-session-persistence/23-VERIFICATION.md rename to .planning/milestones/v4.0-phases/23-encrypted-session-persistence/23-VERIFICATION.md diff --git a/.planning/phases/24-health-surface-server-safety/24-01-PLAN.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-01-PLAN.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-01-PLAN.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-01-PLAN.md diff --git a/.planning/phases/24-health-surface-server-safety/24-01-SUMMARY.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-01-SUMMARY.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-01-SUMMARY.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-01-SUMMARY.md diff --git a/.planning/phases/24-health-surface-server-safety/24-02-PLAN.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-02-PLAN.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-02-PLAN.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-02-PLAN.md diff --git a/.planning/phases/24-health-surface-server-safety/24-02-SUMMARY.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-02-SUMMARY.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-02-SUMMARY.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-02-SUMMARY.md diff --git a/.planning/phases/24-health-surface-server-safety/24-03-PLAN.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-03-PLAN.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-03-PLAN.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-03-PLAN.md diff --git a/.planning/phases/24-health-surface-server-safety/24-03-SUMMARY.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-03-SUMMARY.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-03-SUMMARY.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-03-SUMMARY.md diff --git a/.planning/phases/24-health-surface-server-safety/24-04-PLAN.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-04-PLAN.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-04-PLAN.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-04-PLAN.md diff --git a/.planning/phases/24-health-surface-server-safety/24-04-SUMMARY.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-04-SUMMARY.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-04-SUMMARY.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-04-SUMMARY.md diff --git a/.planning/phases/24-health-surface-server-safety/24-05-PLAN.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-05-PLAN.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-05-PLAN.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-05-PLAN.md diff --git a/.planning/phases/24-health-surface-server-safety/24-05-SUMMARY.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-05-SUMMARY.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-05-SUMMARY.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-05-SUMMARY.md diff --git a/.planning/phases/24-health-surface-server-safety/24-CONTEXT.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-CONTEXT.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-CONTEXT.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-CONTEXT.md diff --git a/.planning/phases/24-health-surface-server-safety/24-HUMAN-UAT.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-HUMAN-UAT.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-HUMAN-UAT.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-HUMAN-UAT.md diff --git a/.planning/phases/24-health-surface-server-safety/24-PATTERNS.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-PATTERNS.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-PATTERNS.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-PATTERNS.md diff --git a/.planning/phases/24-health-surface-server-safety/24-RESEARCH.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-RESEARCH.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-RESEARCH.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-RESEARCH.md diff --git a/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-REVIEW-FIX.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-REVIEW-FIX.md new file mode 100644 index 0000000..b4933e2 --- /dev/null +++ b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-REVIEW-FIX.md @@ -0,0 +1,70 @@ +--- +phase: 24-health-surface-server-safety +fixed_at: 2026-06-12T00:00:00Z +review_path: .planning/phases/24-health-surface-server-safety/24-REVIEW.md +iteration: 1 +findings_in_scope: 5 +fixed: 5 +skipped: 0 +status: all_fixed +--- + +# Phase 24: Code Review Fix Report + +**Fixed at:** 2026-06-12 +**Source review:** .planning/phases/24-health-surface-server-safety/24-REVIEW.md +**Iteration:** 1 + +**Summary:** +- Findings in scope: 5 (CR-01, WR-01, WR-02, WR-03, IN-02; IN-01 subsumed by CR-01 rewrite) +- Fixed: 5 +- Skipped: 0 + +## Fixed Issues + +### CR-01 + WR-03: Guard pygame import; wrap play_sound audio errors + +**Files modified:** `utils.py`, `tests/test_utils_audio.py` +**Commit:** 8284735 +**Applied fix:** +- Replaced bare `import pygame` with `try/except (ModuleNotFoundError, ImportError)` block; sets `_PYGAME_AVAILABLE` and `_pygame` alias. +- `_initialize_audio()` short-circuits to False when `_PYGAME_AVAILABLE` is False (before calling mixer.init). +- All `pygame.*` references in the module now use `_pygame.*`. +- `play_sound` wraps `mixer.music.load/play` in `try/except _pygame.error` to swallow runtime audio errors (WR-03). +- Dead `_AUDIO_AVAILABLE: bool = False` pre-declaration (IN-01) eliminated as part of the rewrite. +- Updated `tests/test_utils_audio.py`: patches retargeted to `utils._pygame`, added non-vacuous import-failure simulation test (blocks pygame via `sys.modules["pygame"] = None`, reloads utils, asserts `_AUDIO_AVAILABLE=False` and `play_sound` no-ops), added WR-03 load/play error tests. + +**Suite result:** 755 passed, 2 skipped, 0 failures (baseline 752 passed; 3 new tests added) + +### WR-01: Add get_consecutive_errors reader; use in supervise() crash path + +**Files modified:** `core/health.py`, `core/orchestrator.py` +**Commit:** d11bf2c +**Applied fix:** +- Added `HealthRegistry.get_consecutive_errors(name: str) -> int` that reads the live counter directly via `_ensure` + dict lookup. +- Replaced `snap = health.get_snapshot(); consecutive = snap.get(plugin_name, {}).get("consecutive_errors", 0)` in `supervise()` with `consecutive = health.get_consecutive_errors(plugin_name)`. Removes the full deep-copy allocation on the hot crash path. + +### WR-02: Snapshot loop/task before guard in stop() to close TOCTOU + +**Files modified:** `core/service.py` +**Commit:** 79e5419 +**Applied fix:** +- Moved `loop = self._loop` and `task = self._task` captures to the top of `stop()`, before the `if not self._running or loop is None` guard. Guard and `call_soon_threadsafe` now operate on the same consistent local references even if the daemon thread nulls the instance attributes concurrently. + +### IN-02: Display heartbeat age instead of raw monotonic seconds + +**Files modified:** `core/cli/status.py` +**Commit:** 3007ba1 +**Applied fix:** +- Added `import time` and `now = time.monotonic()` in `_format_status_table`. +- Replaced `f"{rec.get('last_heartbeat', 0.0):.1f}"` with `f"{now - rec['last_heartbeat']:.1f}s ago"` (or `"never"` when heartbeat is 0.0). + +### IN-01: Dead _AUDIO_AVAILABLE pre-declaration + +Subsumed by CR-01 fix. The line 8 `_AUDIO_AVAILABLE: bool = False` pre-declaration was not present in the rewritten utils.py; no separate commit required. + +--- + +_Fixed: 2026-06-12_ +_Fixer: Claude (gsd-code-fixer)_ +_Iteration: 1_ diff --git a/.planning/phases/24-health-surface-server-safety/24-REVIEW.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-REVIEW.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-REVIEW.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-REVIEW.md diff --git a/.planning/phases/24-health-surface-server-safety/24-VALIDATION.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-VALIDATION.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-VALIDATION.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-VALIDATION.md diff --git a/.planning/phases/24-health-surface-server-safety/24-VERIFICATION.md b/.planning/milestones/v4.0-phases/24-health-surface-server-safety/24-VERIFICATION.md similarity index 100% rename from .planning/phases/24-health-surface-server-safety/24-VERIFICATION.md rename to .planning/milestones/v4.0-phases/24-health-surface-server-safety/24-VERIFICATION.md