## Implementation Plan: Remaining Automation Resistance Gaps ### Objective Add the three remaining enforcement layers that are **not yet implemented** in the live codebase, based on the investigation findings that Tier 1 (rate limit per minute, 5‑second cooldown, CONFIRM_REQUIRED for 3 actions) is already in place. --- ### 1. Add `CONFIRM_REQUIRED` to All Remaining DevII Game Actions **File:** `services/devii/actions/catalog/game.py` - Locate the 9 action dictionaries that currently **lack** the `"CONFIRM_REQUIRED": True` key. These are: `game_water`, `game_steal`, `game_daily`, `game_buy_plot`, `game_upgrade_ci`, `game_perk`, `game_prestige`, `game_legacy`, `game_quest_claim`. - For **each** of these 9, add the line `"CONFIRM_REQUIRED": True` inside the dictionary (after the `"description"` key). - Do **not** touch the 3 actions that already have it (`game_plant`, `game_harvest`, `game_fertilize`). **Verification:** After change, all 12 game action definitions contain `"CONFIRM_REQUIRED": True`. No syntax errors. The DevII dispatcher (`dispatcher.py`) will now require human confirmation before every game action invoked through DevII. --- ### 2. Make Rate Limit Worker‑Safe (Shared Store) **Files to modify:** - `routers/game/index.py` – `_respond_action()` (rate limit check) - `routers/game/farm.py` – water / steal handlers (rate limit check) - `database/schema.py` – add new table `rate_limit_log` **Schema change:** Add a new table `rate_limit_log` with columns: - `id` (Integer, primary key) - `user_uid` (String, indexed) - `action` (String, nullable – optional, can be `None` for game‑wide) - `timestamp` (DateTime, default=`datetime.utcnow`) **Implementation in index.py and farm.py:** Replace the existing module‑level `_user_action_times` dict with a database query: - Before game logic, run: ```python one_minute_ago = datetime.utcnow() - timedelta(seconds=60) count = db.query(RateLimitLog).filter( RateLimitLog.user_uid == user_uid, RateLimitLog.timestamp >= one_minute_ago ).count() if count >= 30: raise HTTPException(status_code=429, detail="Rate limit exceeded.") # Log this action db.add(RateLimitLog(user_uid=user_uid, timestamp=datetime.utcnow())) db.commit() ``` - Use the existing `db` session (injected via dependency). - In farm.py, use the same pattern for water and steal handlers (separate counters if desired, or combine into one counter per user for all game POSTs – combine is simpler and captures the same behaviour). **Housekeeping:** Optionally add a periodic cleanup of logs older than 5 minutes (or inline prune during query – for simplicity, rely on DB size small; can add cleanup later). **Impact:** Rate limit now works across multiple workers because the counter lives in the shared database. --- ### 3. Add Unit Tests for Automation‑Resistance Mechanics **Test files to modify** (or create new): - `tests/unit/services/devii/actions/test_game.py` (new) – test that all game action definitions have `CONFIRM_REQUIRED`. - `tests/e2e/game/test_rate_limit.py` (new) – send 31 fast requests and verify HTTP 429. - `tests/e2e/game/test_harvest_plant_cooldown.py` (new) – harvest then immediately plant same plot and verify error. **Specific tests:** 1. **`test_all_game_actions_have_confirm_required`** – import `services/devii/actions/catalog/game.py`, iterate over all action dicts, assert `"CONFIRM_REQUIRED"` in each and that the value is `True`. 2. **`test_rate_limit_blocks_excess_requests`** – using test client, send 31 POSTs to `/game/action` (or equivalent) as fast as possible, verify 30 succeed and the 31st returns 429. 3. **`test_harvest_plant_cooldown_blocks_rapid_replant`** – create a plot, plant, harvest, immediately plant again on same plot; assert error (e.g., 400 or custom exception). Separate plot should work fine. 4. **`test_rate_limit_is_worker_safe`** – (optional) simulate two concurrent workers by using two separate db sessions; assert shared counter works. **Note:** The existing test suite uses a test database. Ensure tests use `pytest` fixtures that provide a clean db and test client. --- ### 4. Verification (Definition of Done) - [ ] **All 12 DevII game actions have `CONFIRM_REQUIRED`** – checked by a new unit test (test 1 above). - [ ] **Rate limit enforced across workers** – DB table `rate_limit_log` exists and query works; sending 31 requests in one minute returns HTTP 429 on the 31st. - [ ] **Harvest→plant cooldown still works** (existing implementation) – new e2e test confirms. - [ ] **No regression in existing tests** – all tests in `make test-unit` pass. - [ ] **Lint passes** – `ruff check .` exits with code 0. - [ ] **New tests pass** – the three new test cases above pass. - [ ] **Database migration applied** – `rate_limit_log` table exists in test and production schema. The existing `cooldown_until` column is unchanged. - [ ] **Code review** – only the specified files are modified (`services/devii/actions/catalog/game.py`, `routers/game/index.py`, `routers/game/farm.py`, `database/schema.py`, plus new test files). No changes to economy, frontend, or templates. **Test commands to execute:** ```bash pip install -e '.[dev]' -q make test-unit pip install -q ruff ruff check . ``` All commands must exit with code 0. If PEP 668 errors occur, run inside a virtual environment (e.g., `python3 -m venv .venv && source .venv/bin/activate` then retry).