mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-12 15:30:57 +01:00
The five Python splits this session were made safe by a throwaway ast identity check. There is no ast for bash, so meta/scripts/verify_shell_split.sh does the same job with shfmt -mn (minify: strips comments and indentation, keeps logic), hashing each top-level function and naming the one that differs. shfmt is already a REQUIRED_TOOLS binary, so this adds no dependency. Validated in all three directions before trusting it: an unchanged copy is IDENTICAL, a copy with blank lines inserted is IDENTICAL, and a one-character logic change ($1 -> $2) is caught and named. The first mutation attempt used sed 's/exit 1/exit 2/' on a file with no 'exit 1' and reported IDENTICAL - the harness was fine, the test was vacuous. That trap is now documented, because a normalizer that silently passes everything is worse than none. Also triaged all 29 over-cap shell scripts for deployed copies, which the trace harness cannot see. The finding that changes the plan: deploy.sh copies a hardcoded per-file list into /data/local/tmp/focus_mode and never deploys lib/, so an entry+lib seam would break focus mode on the phone at its first source - for 8 of the 29, not the 2 the brief named. Those scripts source only config.sh, which is on the copy list, so a sibling-file seam works provided deploy.sh's list grows in the same commit. 16 scripts have no deployed copy and are safe to split conventionally. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
137 lines
7.0 KiB
Markdown
137 lines
7.0 KiB
Markdown
# Shell split verification: how a green split is still broken
|
|
|
|
Split out of `docs/shell-split-recipes.md` when that file hit the same 250-line
|
|
cap it exists to serve. That file covers **how to make** a split; this one
|
|
covers **how to find out it is wrong**. Read both before the next
|
|
`linux_configuration` or `phone_focus_mode` split.
|
|
|
|
Every section below cost a failed run, a revert, or — once — a live system
|
|
mutation.
|
|
|
|
### A `--skip-install` flag is not evidence the script is safe to run
|
|
|
|
`shell_check.sh` gates only `install_if_missing`'s pacman branch on
|
|
`SKIP_INSTALL`. The AUR branch inside `install_linters` runs regardless, so a
|
|
backgrounded "baseline" run installed four packages before it was killed
|
|
mid-`makepkg`. Decision 6 was violated by a flag that looked like permission.
|
|
|
|
Before running any script to capture a baseline, grep the flag's variable and
|
|
confirm it gates **every** mutation path, not just the first one:
|
|
|
|
```bash
|
|
grep -n 'SKIP_INSTALL\|DRY_RUN' <script> # then read each hit
|
|
```
|
|
|
|
`--list-only` was the genuinely safe path here, because it returns before
|
|
`install_linters` is reached at all.
|
|
|
|
### Check whether the target is DEPLOYED as a single file — the trace cannot
|
|
|
|
Before designing seams, find out whether some other script copies the target
|
|
somewhere and runs it from there. An entry+`lib/` shape ships an entry whose
|
|
deployed `SCRIPT_DIR` has no `lib/`, so it dies on its first `source` under
|
|
`set -e` — while your trace stays green, because the trace runs the repo copy
|
|
where `lib/` exists.
|
|
|
|
```bash
|
|
grep -rn '<target-basename>' linux_configuration/ --include='*.sh' \
|
|
| grep -v '<the target itself>:'
|
|
```
|
|
|
|
Confirmed blocked on this, not theoretically: `install_leechblock.sh` (the
|
|
pacman installer `cp`s it to `/usr/local/share/digital_wellbeing/` and
|
|
`pacman_wrapper.sh:831` prefers that copy on **every pacman invocation**) and
|
|
`block_compulsive_opening.sh` (`install_all` copies itself to
|
|
`/usr/local/bin`). A target invoked as `$CONFIG_DIR/scripts/...` is fine.
|
|
|
|
### A stubbed run cannot reach a bug in a seam that passes state
|
|
|
|
`meta/scripts/verify_shell_split.sh` and the hand-rolled `declare -F` probe
|
|
both **source the libs and never call the functions**. That is enough to prove
|
|
every `source` line resolves, and nothing more. The `analyze_repo.sh` split
|
|
passed `bash -n`, `shellcheck`, a function-set diff, a line-set diff and the
|
|
stubbed probe — and still shipped a script that aborted immediately after
|
|
language detection. Two separate bugs, neither reachable without executing:
|
|
|
|
- **`set -e` and the function tail.** Eight `((LANG_FILES[x] > 0)) && HAS_Y=true`
|
|
lines were top-level statements, where a false `&&` merely leaves `$?`
|
|
non-zero. Wrapped into a function they become its _return value_, and the
|
|
last one (java, false for most repos) killed the script. Any time you wrap
|
|
former top-level code in a function, check what its **last statement** is;
|
|
append `|| true` if it is a bare conditional.
|
|
- **A self-referencing nameref.** `local -n LANG_CODE_FILES="$1"` pointing at a
|
|
global of the same name still moves data on bash 5.3, but warns
|
|
`circular name reference` on _every access_ — straight into the script's
|
|
output. Name the local something else (`_code_files`).
|
|
|
|
So: when a seam passes state, run the thing. Stub only the parts that mutate
|
|
the system, and diff the generated artifacts against a detached worktree at the
|
|
pre-split commit. That comparison is what proved the fix — same file set, every
|
|
text artifact byte-identical.
|
|
|
|
## The harness and the fixtures
|
|
|
|
Everything about _running_ the check — `trace_shell_split.sh`, the `--prefix`
|
|
mode for scripts whose job is placing files, the regression fixtures, the
|
|
entry-point and `shfmt` traps — moved to
|
|
**`docs/shell-split-harness.md`** when this file hit the same 250-line cap it
|
|
exists to serve. This file stays the _why_; that one is the _how_.
|
|
|
|
### Prove the move was verbatim: `meta/scripts/verify_shell_split.sh`
|
|
|
|
The Python splits in this repo are verified with a throwaway `ast` identity
|
|
check. There is no `ast` for bash, so the shell equivalent normalises through
|
|
`shfmt -mn` (minify: strips comments and indentation, keeps logic) and compares
|
|
a hash per top-level function:
|
|
|
|
```bash
|
|
bash meta/scripts/verify_shell_split.sh HEAD <old-path> <new-path>...
|
|
# IDENTICAL: all N top-level functions moved verbatim
|
|
```
|
|
|
|
It reports which function changed, so a diff points at the seam rather than the
|
|
whole file. Run it after every split, and again after `shfmt`/pre-commit
|
|
autofixes — reordering an import or reflowing a line is exactly what it is
|
|
designed to see through.
|
|
|
|
**Verify the harness before trusting it.** A normalizer that silently passes
|
|
everything is worse than none. The three checks it must survive:
|
|
|
|
| input | expected |
|
|
| ------------------------------------------------ | --------------------------------------- |
|
|
| unchanged copy of the file | `IDENTICAL` |
|
|
| comments/blank lines/indentation changed | `IDENTICAL` |
|
|
| one character of logic changed (`"$1"` → `"$2"`) | `DIFFERENCE FOUND`, naming the function |
|
|
|
|
The first attempt at that mutation test used `sed 's/exit 1/exit 2/'` on a file
|
|
containing no `exit 1`. It reported `IDENTICAL` — not because the harness was
|
|
broken, but because the mutation never applied. Confirm the mutation actually
|
|
changed the file (`diff` it) before concluding anything about the harness.
|
|
|
|
### Deployment triage of the over-cap scripts (2026-08-17)
|
|
|
|
Run before designing any seam, per the section above. `deploy.sh` copies a
|
|
**hardcoded per-file list** into `/data/local/tmp/focus_mode` and never deploys
|
|
`lib/`:
|
|
|
|
- **`phone_focus_mode/*` (8 over-cap scripts)** — deployed individually.
|
|
A new sibling must be added to `deploy.sh`'s copy list **in the same commit**,
|
|
or focus mode dies on the phone at its first `source`. These scripts source
|
|
only `config.sh`, which is on that list, so a sibling-file seam is workable —
|
|
an entry+`lib/` seam is not.
|
|
- **`install_leechblock.sh`, `block_compulsive_opening.sh`** — copied to
|
|
`/usr/local/…` and preferred there by `pacman_wrapper.sh:831` on every pacman
|
|
invocation. Teach the installer to deploy the directory first, in its own
|
|
commit. Highest blast radius; do these last.
|
|
- **`hosts/install.sh`, `setup_hosts_guard.sh`, `lib/monitor.sh`** — install
|
|
helper scripts to `/usr/local/sbin` or `/usr/local/bin`; check whether the
|
|
file being split is itself the deployed one.
|
|
- **The other 16** (`fresh-install/main.sh`, `nvidia_troubleshoot.sh`,
|
|
`setup_passwordless_system.sh`, `clean_audio.sh`, `enforce_vbox_hosts.sh`,
|
|
`migrate_hosts_guard_to_guard_lib.sh`, `libre_translate.sh`,
|
|
`diagnose_pacman_hook_stall.sh`, `install_plagiarism_tools.sh`,
|
|
`steam_compatibility.sh`, `deploy.sh`, `setup_night_lockdown.sh`,
|
|
`pacman_wrapper.sh`, `generate_study_materials.sh`,
|
|
`check_and_enable_services.sh`, `setup_midnight_shutdown.sh`) — no deployed
|
|
copy found, so an entry+`lib/` shape is safe.
|