testsAndMisc/NEXT_SESSION_PROMPT.md
Krzysztof kuhy Rudnicki 32a4d9c70c
Some checks are pending
Pre-commit checks / pre-commit (push) Waiting to run
Python tests / test (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Record the /usr/bin bind limit; retarget the brief at the pacman wrapper
`--bind-abs /usr/bin` overlays the directory bwrap needs to exec anything, so
the run dies with `execvp bash: No such file or directory` before the script
starts. A target whose last act replaces a binary there traces fine with every
other path bound and stops at that line with exit 1 on EPERM — usable as a
baseline (it is deterministic across runs), but blind to statements at or
after the failure, which is a constraint on where the seams can go.

Also noted: the absolute-path scanner misses a literal `ln -sf` target,
because it follows variables into `cat >`/`cp`.

The brief now targets install_pacman_wrapper.sh (316) with its baseline
already captured: 16 file hashes, one stubbed call, exit 1 at line 304.
Checked ahead of time that its source.sha256 drift manifest hashes only the
seven copied source files, not the installer, so a split leaves it stable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-17 07:32:37 +02:00

231 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Next session: split `install_pacman_wrapper.sh`
> **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.
## The one job
Split
`linux_configuration/scripts/periodic_background/digital_wellbeing/pacman/install_pacman_wrapper.sh`
(316 lines, over the 250-line cap by 66) into files that are each under the
cap, and **prove by execution** that it still does exactly the same thing.
The harness already exists and has already been run against this exact file —
its baseline is captured and its one blind spot is mapped below. Use it, don't
rebuild it.
## What landed last session (do not redo)
`install_usage_monitoring.sh` (290 lines) is done, committed and pushed as
`26965ba`: a 53-line entry plus five libs in a sibling `lib/`, with an empty
before/after trace diff including all five generated-file hashes. That split is
the worked example — read it before starting this one, the shape transfers.
Two things it discovered that this file inherits:
- The pre-commit hook runs **`shfmt -w` on every staged shell file** and
re-stages it. It rewrites `cat > "x" << 'EOF'` to `cat >"x" <<'EOF'`. If any
test greps a script's source text for a redirection, that rewrite silently
breaks extraction. Run `shfmt -w` on your files yourself, then re-verify, so
the state you tested is the state that gets committed.
- Repo-wide `jscpd` currently reports **2.51% (over the 2% gate)** from the
working tree, but every clone above the HEAD baseline is vendored
`.venv`/`.ci-mirror-venv` site-packages (`tqdm/completion.sh`,
`virtualenv/activate.sh`). Measured at HEAD in a clean worktree it is 1.47%
with 27 clones, and the committed hook run passed. Don't chase it, and don't
"fix" it by touching vendored files.
## The baseline you must reproduce
Captured at commit `26965ba`. **Verified deterministic**: two consecutive runs
at HEAD produced byte-identical traces, so a before/after diff is real proof.
```bash
F=linux_configuration/scripts/periodic_background/digital_wellbeing/pacman/install_pacman_wrapper.sh
rm -rf /tmp/pfx-before # fresh, NOT emptied — the harness refuses a non-empty prefix
meta/scripts/trace_shell_split.sh "$F" --prefix /tmp/pfx-before \
--bind-abs /usr/local/bin \
--bind-abs /usr/local/share/digital_wellbeing \
--bind-abs /usr/local/share/digital_wellbeing/virtualbox \
--bind-abs /var/lib/pacman-wrapper \
--out /tmp/before.txt
```
Expect **exit status 1** (see the blind spot below — this is correct, not a
failure), one stubbed call (`npm install --prefix …`), and 16 files written:
```
abs/usr/local/bin/heavy_job_lock.sh size=7221 sha=3961b14e6a6a8b86
abs/usr/local/bin/makepkg_capped size=2037 sha=3acf1189e93e5771
abs/usr/local/bin/mkpkg size=178 sha=da4a2fa3e8f5b6ab
abs/usr/local/bin/pacman_blocked_keywords.txt size=1042 sha=7bd951741c649b60
abs/usr/local/bin/pacman_greylist.txt size=174 sha=2dc47dd4a25d6aca
abs/usr/local/bin/pacman_lock_lib.sh size=6581 sha=147ea7a163000f82
abs/usr/local/bin/pacman_whitelist.txt size=6079 sha=cfec8efc4a2330c1
abs/usr/local/bin/pacman_wrapper size=29410 sha=548fc2b53ee4cea6
abs/usr/local/bin/words.txt size=1375483 sha=f5c9d52b244f9973
abs/usr/local/share/digital_wellbeing/install_leechblock.sh size=15965 sha=e4ec4b37fd9113a3
abs/usr/local/share/digital_wellbeing/leechblock_defaults.json size=51084 sha=ca068b74282921c6
abs/usr/local/share/digital_wellbeing/package.json size=258 sha=618fa688d2edfa4f
abs/usr/local/share/digital_wellbeing/seed_leechblock_storage.js size=4176 sha=1611cb17124fd388
abs/usr/local/share/digital_wellbeing/virtualbox/enforce_vbox_hosts.sh size=12601 sha=ab039aedc3cd84cd
abs/var/lib/pacman-wrapper/policy.sha256 size=412 sha=0aa8d95524660324
abs/var/lib/pacman-wrapper/source.sha256 size=1374 sha=4c928a499d437070
```
The last stderr line is
`ln: failed to create symbolic link '/usr/bin/pacman': Permission denied`.
Capture the baseline at HEAD **before editing anything**, then diff your split
against it. Unlike last file, a detached worktree is fine here — nothing
interpolates the repo path into a generated artifact (verified: the drift
manifest hashes source files by `readlink -f`, and those hashes are of file
_contents_, not paths).
## The one blind spot — this is the whole design constraint
`--bind-abs /usr/bin` **cannot be used**: it mounts an empty overlay over the
directory the sandbox needs to exec anything, and the run dies with
`bwrap: execvp bash: No such file or directory` before reaching the script.
Measured last session; it is a hard limit of the harness.
So the trace reaches **line 304** (`ln -sf "$WRAPPER_DEST" /usr/bin/pacman`),
hits EPERM, and exits 1. Lines 304 and 306 (the final `echo`) are the only
statements that never execute — everything before them is covered, 16 files
written and 19 stdout lines deep.
**Therefore: keep lines 304–316 in the entry script.** Anything you move into a
lib gets proven by the trace; anything at or after 304 does not, and a broken
split there would diff clean. This is a constraint on where the seams go, not a
reason to skip the file.
Note the absolute-path scanner does **not** list `/usr/bin` when it refuses to
run — it follows variables into `cat >`/`cp` and misses the literal `ln -sf`
target. Don't read its list as complete.
## Hazards specific to THIS file
### 1. It self-sudos at line 9
```bash
if [ "$EUID" -ne 0 ]; then
sudo "$0" "$@"
exit $?
fi
```
Under `--bind-abs` the run is already uid 0 (bwrap `--unshare-user --uid 0`),
so this branch is skipped and the script proceeds for real — that is why the
trace is 16 files deep rather than the three-line `require_root` truncation.
If you move this block, `$0` must still be the entry point.
### 2. `source.sha256` is a drift manifest — but a split does not disturb it
Line ~247 hashes seven **source** files (`WRAPPER_SOURCE`, `LOCK_LIB_SOURCE`,
`BLOCKED_SOURCE`, `GREYLIST_SOURCE`, `MAKEPKG_CAPPED_SOURCE`, `MKPKG_SOURCE`,
`WHITELIST_SOURCE`) plus the installed lock lib. Checked: it does **not** hash
the installer itself, and there is no glob that would pick up a new `lib/`.
So `sha=4c928a499d437070` should stay put across a split — if it moves, you
changed one of those seven copied files, which is a real regression.
`check_and_enable_services.sh` replays this manifest with `sha256sum -c` from
systemd with `cwd=/`, which is why the entries must be absolute
(`readlink -f`). Don't make those paths relative while tidying.
### 3. `chattr +i` / immutability
The script makes policy files immutable and has unlock/relock helpers
(`is_immutable_file`, `unlock_immutable_file_if_needed`,
`relock_files_on_exit`, lines 58–84) wired to a trap. Inside the sandbox these
warn ("Could not make integrity file immutable") and continue — that warning is
in the baseline and is expected. If you move the trap or the helpers, check the
trap still fires from the entry script.
### 4. It writes a _live_ system when run for real
`/usr/bin/pacman` is currently a symlink to `/usr/local/bin/pacman_wrapper`.
The sandbox protects this (the `ln` is denied — that denial is the positive
proof the bind worked), but **never run this script outside the harness** to
"check something".
## Rules that will bite you
- **`SCRIPT_DIR` must resolve symlinks**:
`SCRIPT_DIR="$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")"`. The
`SOURCE_*` vars are built from `dirname "$0"` and the code comments say so
explicitly — if you move that computation into a lib, pass the value in
rather than recomputing it at a different directory depth.
- **Wrapping top-level code in a function changes what `set -e` sees.** A bare
`((x > 0)) && FLAG=true` as a function's last statement becomes its return
value; if false, the script dies. Check the last statement of every function
you create; append `|| true` if it is a bare conditional.
- **No suppressions.** No `# shellcheck disable`, no per-file ignores.
- New `.sh` files need the executable bit, a `#!/bin/bash` shebang, and **no
`set -euo pipefail`** (a sourced lib inherits the caller's strict mode).
Convention: a sibling `lib/` directory; see the `bin/lib/` that landed in
`26965ba`.
- Every commit touching code needs an evidence JSON in
`docs/superpowers/evidence/`; **≥4 staged code files** additionally needs a
fresh `docs/superpowers/contracts/*.json`. Validate both with
`python3 meta/scripts/validate_contract.py` /
`meta/scripts/validate_evidence.py` before committing.
- Markdown needs `npx prettier --write` — prettier is pre-push only, so a file
that passes every per-commit gate can still fail the push.
- Work directly on `main`; commit and push. `git stash` and branch creation are
blocked by hooks.
- `git push` runs `ci-mirror` (clean-venv install + `pre-commit --all-files` +
pytest) and takes minutes. Never edit files while a push is running.
- **Do not wire the file-length pre-commit hook.** It must land last, only once
`bash ~/utils/scripts/check_file_length.sh --all` exits 0, or every push
fails.
## Read these first
1. `docs/shell-split-verification.md` — how a green split is still broken. The
`--prefix` section covers the manifest, the empty-prefix rule, the
`require_root` truncation trap, and the `/usr/bin` limit above.
2. `docs/shell-split-recipes.md` — how to actually make a split.
3. The `26965ba` diff — the worked example of the entry+lib shape.
## Definition of done
- `install_pacman_wrapper.sh` and every file split out of it are **under 250
lines** (`bash ~/utils/scripts/check_file_length.sh --all` no longer lists
any of them).
- `diff /tmp/before.txt /tmp/after.txt` is **empty** — same exit status (1),
same stubbed call, and all 16 content hashes identical.
- Nothing you moved into a lib sits at or after line 304.
- `shellcheck` clean and `shfmt` clean on every touched file; zero
suppressions.
- `/usr/bin/pacman` still symlinks to `/usr/local/bin/pacman_wrapper` and
`pacman --version` still works, after every verification run.
- Committed and pushed, with evidence (and a contract if ≥4 code files).
## If there is time
Take the next target by the same method. Pick by **verifiability**, not by line
count — a big file you can run beats a small one you cannot:
```bash
bash ~/utils/scripts/check_file_length.sh --all
```
Remaining newly-unblocked targets, ascending difficulty: `nvidia_troubleshoot.sh`
(336, needs `--bind-abs /etc/modprobe.d --bind-abs /etc/X11 --bind-abs
/etc/profile`; it embeds `$(date)` in its config so that one hash varies every
run — compare file list and sizes instead), `setup_thorium_startup.sh` (443),
`install_leechblock.sh` (485 — confirm where `INSTALL_ROOT`/`VERSION_DIR` point
BEFORE the first run, it uses `rsync -a --delete`).
**Still out of scope:** `block_compulsive_opening.sh` (705) — `install_all`
copies the running script into `/usr/local/bin`, and an entry+lib shape ships
an entry whose `SCRIPT_DIR` has no `lib/`, breaking three daily-use apps plus
the pacman rewrap hook. Leave it.
Also still blocked on a NAMED blocker, not on line count — do not "just split"
these: `check_and_enable_services.sh` (1337, every `check_*` writes one
`SERVICE_STATUS`), `steam_compatibility.sh` (663), `libre_translate.sh` (488,
~19 globals cross any seam), `enforce_vbox_hosts.sh` (443, every seam falls
inside a heredoc). See `refactor_claude_todo_resume.md`.