# Next session: three explicit asks, then keep splitting > **Paste this whole file into a fresh Claude session opened in `~/testsAndMisc`.** > It is self-contained. Do not go looking for the previous session's context. Over-cap: **37 → 24** (20 shell, 2 kotlin, 1 dart, 1 markdown — this file). 17 commits pushed. **Python is done**: every `.py` is under the cap at 100% line+branch coverage where the gate measures it. The user has given three direct instructions. Do them in this order, each as its own commit. They are already decided — do **not** re-litigate them. --- ## 1. Fix the `_VENDOR_KW` Intel bug `python_pkg/vscode_optimizer/_hardware.py:22`: ```python _VENDOR_KW = {"nvidia": "NVIDIA", "amd": "AMD", "ati": "AMD", "intel": "Intel"} ``` `_detect_gpu` does `for kw, vendor in _VENDOR_KW.items(): if kw in low:`. Every real `lspci` display line contains **"VGA comp*ati*ble controller"**, so `"ati"` matches unconditionally and `"intel"` is unreachable. **Intel iGPUs are reported as AMD**, which switches on the GPU-accelerated terminal and AMD-only Electron flags. This machine is unaffected only because `"nvidia"` is checked first. **Do:** - Fix it so vendor detection keys on the device description rather than the whole line — the text after the last `:` is already extracted into `hw.gpu_model` one line above. Match against that, not `low`. - Delete the `strict=True` xfail `test_detect_gpu_should_recognise_an_intel_igpu` in `python_pkg/vscode_optimizer/tests/test_gpu_disk.py` and make it a normal passing test. **The xfail is `strict=True`, so a fix without removing the marker fails the suite** — that is deliberate. - The parametrised `test_detect_gpu_maps_each_vendor_keyword` currently _characterises_ the bug: two of its cases assert `"AMD"` for an Intel line and for an unknown vendor. Update both to the correct expectations. - Verify with a real run: `python3 -m python_pkg.vscode_optimizer --dry-run` must still detect this machine's RTX 3090 as NVIDIA and propose the same **18 changes**. Then confirm the repo gate: bare `pytest` (memory-capped) at 100%. ## 2. Split the `phone_focus_mode` scripts anyway The user has accepted the deployment risk. **This does not mean skip the copy lists** — it means proceed with the splits, doing them correctly. `deploy.sh` has **two** hardcoded per-file lists and never deploys `lib/`: - an `adb_cmd push "$DEPLOY_DIR/" "/data/local/tmp/focus_stage/"` (~line 381) - an `adb_root "cp /data/local/tmp/focus_stage/ $REMOTE_DIR/"` (~line 497) **Every new sibling must be added to BOTH, in the same commit.** Missing the push leaves the copy pointing at a file that was never staged. Use a **sibling file** beside the entry script, never a `lib/` member — these scripts source `"$SCRIPT_DIR/.sh"` and `lib/` does not exist on the phone. `tether_enforcer.sh` was done last session and is the worked example — read that commit before starting. Targets, cheapest first, with what can verify each: | file | lines | verifiable by | | -------------------- | ----- | ------------------------------------------------------------------------------------ | | `dns_enforcer.sh` | 325 | `lib/tests/test_dns_enforcer.sh` — **runnable** | | `phone_backup.sh` | 333 | no test; not in deploy.sh's lists at all | | `curfew_enforcer.sh` | 367 | no test | | `hosts_enforcer.sh` | 421 | no test | | `lib/monitor.sh` | 449 | `lib/tests/test_monitor.sh` — **runnable**; already a lib, not deployed by deploy.sh | | `focus_daemon.sh` | 543 | no test | | `config.sh` | 571 | **`python_pkg/focus_policy/loader.py` parses it** — see below | | `deploy.sh` | 835 | not deployed itself; entry+`lib/` IS safe here | | `focus_ctl.sh` | 1091 | no test | Start with `dns_enforcer.sh` and `lib/monitor.sh`: both have real test scripts, so you can prove the split the way `tether_enforcer` was proved (run the test before and after; the count must match exactly). **`config.sh` is special.** `python_pkg/focus_policy/loader.py` is a mini-interpreter that parses it as the live source of truth. Splitting it breaks that loader unless the parser follows. `focus_policy` has 100% tests that will catch it — run them. Plan the two changes together, or do `config.sh` last. ## 3. Restructure the three parked files Last session parked these because a verbatim function move cannot get them under the cap. The user has authorised restructuring. **Restructuring means the verbatim harness no longer applies, so each one needs a test or a real run instead** — say plainly which you used. - **`install_plagiarism_tools.sh` (534, only 3 tiny functions).** ~500 lines of top-level code. Wrap coherent stages in functions, then move those. This is the most mechanical of the three; do it first. - **`libre_translate.sh` (488, 18 funcs).** After the clean seams are taken, `parse_args` (111 lines), `write_env_file`, `detect_container_user` and `main` all assign **and** read the same configuration globals. Four attempts were spent relocating them; that does not work. Restructure `parse_args` — e.g. split the case body into per-flag helpers, or move the whole config-globals cluster into one lib that owns both writes and reads. - **`diagnose_pacman_hook_stall.sh` (493, 15 funcs).** Same shape: `run_one` writes `LAST_ELAPSED`, `main` reads it. Also emits **SC2153** (`PACMAN_BIN` vs `PACMAN_PID`) once split — that is a real shellcheck finding to resolve, not to suppress. --- ## Tooling — use it, don't rebuild it **`meta/scripts/extract_shell_functions.py`** — moves functions into a library. Walks brace-by-brace and lifts only function blocks. **Never slice by line range**: these scripts interleave top-level commands between function definitions, and a range slice sweeps those into the library where they run at source time and out of order. It refuses to guess when there is no `set -e` anchor (as in the `#!/system/bin/sh` phone scripts) — place the source line by hand there, after the existing `. "$SCRIPT_DIR/config.sh"`. ```bash python3 meta/scripts/extract_shell_functions.py