diff --git a/docs/restructure-live-state.md b/docs/restructure-live-state.md new file mode 100644 index 00000000..b108c48f --- /dev/null +++ b/docs/restructure-live-state.md @@ -0,0 +1,104 @@ +# Live state pointing into testsAndMisc + +Generated by `meta/scripts/report_live_state.sh` at 2026-08-23T16:56:34+02:00. +Baseline commit: f4535b6b + +This is the checklist for the repo restructure. Every entry below names +an absolute path inside this repo; when that path moves, the referring +file must be repointed in the same step. Re-run the script with +`--check` after each move to prove nothing was left behind. + +``` +## systemd units + /etc/systemd/system/dns-blocklist-refresh.service + 7:ExecStart=/home/kuhy/testsAndMisc/linux_configuration/scripts/single_use/features/setup_dns_blocker.sh refresh + /etc/systemd/system/media-organizer.service + 10:ExecStart=/home/kuhy/testsAndMisc/linux_configuration/scripts/single_use/utils/organize_downloads.sh + /home/kuhy/.config/systemd/user/endurain-import.service + 3:Documentation=file:///home/kuhy/testsAndMisc/endurain/README.md + 11:WorkingDirectory=/home/kuhy/testsAndMisc + 21:ExecStart=/usr/bin/bash -c 'ENDURAIN_API_KEY="$(cat /home/kuhy/testsAndMisc/endurain/.api_key)" /home/kuhy/testsAndMisc/.venv/bin/python -m python_pkg.endurain_import' + /home/kuhy/.config/systemd/user/kcd2-dice-build.service + 7:WorkingDirectory=/home/kuhy/testsAndMisc/kcd2_dice_solver + 8:ExecStart=/home/kuhy/testsAndMisc/kcd2_dice_solver/deploy_build.sh + /home/kuhy/.config/systemd/user/morning-routine.service + 13:Environment=PYTHONPATH=%h/testsAndMisc + 16:WorkingDirectory=%h/testsAndMisc + /home/kuhy/.config/systemd/user/syncyomi-guard.service + 8:WorkingDirectory=/home/kuhy/testsAndMisc + 9:Environment=PYTHONPATH=/home/kuhy/testsAndMisc + +## /etc references + /etc/guard-lib/targets/nsswitch.conf + 4:PLUGIN="/home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/hosts/guard/plugins/nsswitch-plugin.sh" + /etc/guard-lib/targets/resolved.conf + 4:PLUGIN="/home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/hosts/guard/plugins/resolved-plugin.sh" + +## symlinks into the repo + /home/kuhy/.config/i3blocks/claude_usage.sh -> /home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/i3-configuration/i3blocks/claude_usage.sh + /home/kuhy/.config/i3blocks/config -> /home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/i3-configuration/i3blocks/config + /home/kuhy/.config/i3blocks/cpu_monitor.sh -> /home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/i3-configuration/i3blocks/cpu_monitor.sh + /home/kuhy/.config/i3blocks/disk.sh -> /home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/i3-configuration/i3blocks/disk.sh + /home/kuhy/.config/i3blocks/ethernet.sh -> /home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/i3-configuration/i3blocks/ethernet.sh + /home/kuhy/.config/i3blocks/gpu_monitor.sh -> /home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/i3-configuration/i3blocks/gpu_monitor.sh + /home/kuhy/.config/i3blocks/memory.sh -> /home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/i3-configuration/i3blocks/memory.sh + /home/kuhy/.config/i3blocks/motherboard_temp.sh -> /home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/i3-configuration/i3blocks/motherboard_temp.sh + /home/kuhy/.config/i3blocks/pc_startup_status.sh -> /home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/i3-configuration/i3blocks/pc_startup_status.sh + /home/kuhy/.config/i3blocks/persist_common.sh -> /home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/i3-configuration/i3blocks/persist_common.sh + /home/kuhy/.config/i3blocks/shutdown_countdown.sh -> /home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/i3-configuration/i3blocks/shutdown_countdown.sh + /home/kuhy/.config/i3blocks/time.sh -> /home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/i3-configuration/i3blocks/time.sh + /home/kuhy/.config/i3blocks/volume.sh -> /home/kuhy/testsAndMisc/linux_configuration/scripts/periodic_background/i3-configuration/i3blocks/volume.sh + /home/kuhy/.local/bin/keepass-open -> /home/kuhy/testsAndMisc/linux_configuration/scripts/single_use/features/keepass-open.sh + /home/kuhy/.local/bin/line-speed-probe.sh -> /home/kuhy/testsAndMisc/linux_configuration/scripts/diagnostics/line-speed-probe.sh + /home/kuhy/.local/bin/steam-download-duty.sh -> /home/kuhy/testsAndMisc/linux_configuration/scripts/diagnostics/steam-download-duty.sh + /home/kuhy/.oh-my-zsh/custom/calc-live.zsh -> /home/kuhy/testsAndMisc/linux_configuration/zsh/calc-live.zsh + /home/kuhy/.oh-my-zsh/custom/claude-tty-fix.zsh -> /home/kuhy/testsAndMisc/linux_configuration/zsh/claude-tty-fix.zsh + /home/kuhy/.oh-my-zsh/custom/token-savers.zsh -> /home/kuhy/testsAndMisc/linux_configuration/zsh/token-savers.zsh +``` + +## Depth-cap dry run (step 0) + +`meta/scripts/check_directory_depth.sh --all` at baseline: + +| Bucket | Files | +|---|---| +| Total violations | 556 | +| Disappear with the archives/extractions | 349 | +| **Remain — the step D worklist** | **207** | + +Step D worklist by directory: + +``` + 36 linux_configuration/scripts/single_use/utils + 24 linux_configuration/scripts/single_use/features + 18 linux_configuration/scripts/single_use/misc/testsAndMisc-bash + 17 linux_configuration/scripts/single_use/fixes + 14 linux_configuration/scripts/single_use/fresh-install + 13 linux_configuration/scripts/periodic_background/i3-configuration/i3blocks + 10 linux_configuration/scripts/single_use/misc/testsAndMisc-bash/tools + 9 linux_configuration/C/atop_agg + 7 python_pkg/artgate/routes + 6 meta/scripts/fixtures/mutations + 6 meta/scripts/fixtures + 6 linux_configuration/scripts/single_use + 6 linux_configuration/docs/security-hardening + 5 linux_configuration/dwm/bin + 3 python_pkg/artgate/styles + 3 linux_configuration/scripts/diagnostics + 3 linux_configuration/scripts/boot_recovery + 2 linux_configuration/scripts/single_use/features/systemd + 2 linux_configuration/scripts/periodic_background/i3-configuration + 2 linux_configuration/scripts/periodic_background + 2 linux_configuration/scripts/gaming + 2 linux_configuration/scripts/boot_recovery/hooks + 2 linux_configuration/dwm/patches + 1 meta/scripts/run_all + 1 linux_configuration/zsh/scratchpad + 1 linux_configuration/scripts/single_use/utils/data + 1 linux_configuration/scripts/single_use/misc/testsAndMisc-bash/.vscode + 1 linux_configuration/scripts/single_use/feedback-appimage + 1 linux_configuration/scripts/periodic_background/i3-configuration/i3 + 1 linux_configuration/scripts/meta + 1 linux_configuration/.github/workflows + 1 linux_configuration/docs/pacman-wrapper +``` diff --git a/docs/superpowers/evidence/restructure-step0-safety-net-2026-08-23.json b/docs/superpowers/evidence/restructure-step0-safety-net-2026-08-23.json new file mode 100644 index 00000000..decbf435 --- /dev/null +++ b/docs/superpowers/evidence/restructure-step0-safety-net-2026-08-23.json @@ -0,0 +1,51 @@ +{ + "intent": "Establish the safety net for the testsAndMisc restructure: a script that inventories every live reference into the repo, and the directory-depth gate written early as a dry run so the flattening work can be sized before any file moves.", + "scope": [ + "meta/scripts/report_live_state.sh (new)", + "meta/scripts/check_directory_depth.sh (new, NOT yet wired into pre-commit)", + "docs/restructure-live-state.md (new)", + "Non-goal: no archives, extractions or moves in this commit", + "Non-goal: the depth gate is deliberately not enforced yet - the tree does not conform" + ], + "changes": [ + "report_live_state.sh enumerates systemd units, /etc config and $HOME symlinks that name an absolute path inside the repo; --check exits 1 while any remain, making it the post-move gate.", + "check_directory_depth.sh enforces a 2-directory cap, modelled on check_file_length.sh (--all/per-file, EXCLUDED_PREFIXES). Exempts third_party/, docs/superpowers/, .github/skills/ by prefix and any lib/ or tests/ path segment, matched as whole segments so libvirt/ and lib.sh are unaffected.", + "docs/restructure-live-state.md records the baseline inventory (6 systemd units, 2 /etc guard-lib targets, 19 symlinks) plus the depth dry-run sizing." + ], + "verification": [ + { + "command": "./meta/scripts/report_live_state.sh", + "result": "pass", + "evidence": "Reported 6 systemd units (media-organizer, dns-blocklist-refresh, endurain-import, kcd2-dice-build, morning-routine, syncyomi-guard), 2 /etc/guard-lib/targets/*.conf plugin paths, and 19 symlinks under ~/.config/i3blocks (13), ~/.local/bin (3), ~/.oh-my-zsh/custom (3)." + }, + { + "command": "./meta/scripts/report_live_state.sh --check; echo $?", + "result": "pass", + "evidence": "Exit code 1 with 'live references into /home/kuhy/testsAndMisc remain', which is correct at baseline since nothing has moved yet." + }, + { + "command": "14 hand-built path cases through check_directory_depth.sh", + "result": "pass", + "evidence": "a/b/c.sh=0, a/b/c/d.sh=1, boot_recovery/tests/lib/x.sh=0, focus_policy/tests/test_model.py=0, meta/scripts/lib/trace_stubs.sh=0, meta/scripts/fixtures/mutations/monitor.json=1, third_party+docs/superpowers+.github/skills prefixes=0. False-positive guards: a/b/libvirt/c.sh=1 and a/b/c/lib.sh=1 both correctly NOT exempt." + }, + { + "command": "./meta/scripts/check_directory_depth.sh --all", + "result": "fail (expected - dry run only, gate not wired)", + "evidence": "556 violations at baseline. 349 disappear with the planned archives/extractions; 207 remain as the step D worklist, dominated by linux_configuration/scripts/single_use (~130)." + }, + { + "command": "pre-commit run --files meta/scripts/check_directory_depth.sh meta/scripts/report_live_state.sh docs/restructure-live-state.md", + "result": "pass", + "evidence": "All hooks passed, including shellcheck, the 250-line file cap, and the shell-coverage ratchet." + } + ], + "risks": [ + "report_live_state.sh reads /etc without sudo, so a root-only-readable reference would be missed; every reference found so far is world-readable.", + "The symlink scan is depth-limited to 4 under $HOME, which covers all known locations but would miss a deeper one.", + "check_directory_depth.sh is intentionally not wired into pre-commit yet; wiring it now would block every unrelated commit, exactly as the file-length-cap comment warns." + ], + "rollback": [ + "git revert the commit; all three files are new and nothing sources them yet.", + "Confirm pre-commit run --all-files still passes, since no existing hook or workflow references these scripts." + ] +} diff --git a/meta/scripts/check_directory_depth.sh b/meta/scripts/check_directory_depth.sh new file mode 100755 index 00000000..53b67ce1 --- /dev/null +++ b/meta/scripts/check_directory_depth.sh @@ -0,0 +1,152 @@ +#!/usr/bin/env bash + +# ============================================================================ +# Enforce the repo-wide directory-depth cap. +# +# A tracked file may sit at most MAX_DEPTH directories below the repo root. +# The cap exists so that this repo stays a home for small, findable scripts: +# once a subtree needs its own taxonomy several levels deep, it has outgrown +# "misc" and belongs in its own repository. +# +# Exempt, by path prefix: +# third_party/ vendored - its shape is upstream's business +# docs/superpowers/ hook-generated bookkeeping, not hand-placed +# .github/skills/ layout fixed by the agent harness +# +# Exempt, by path segment: any file under a `lib/` or `tests/` directory. +# Those two names are structural, not taxonomic - a library split out to +# satisfy the 250-line cap, and the test suite that the shell-coverage +# ratchet requires to sit beside it. Capping them would force one gate to +# fight another. +# +# check_directory_depth.sh --all # every tracked file +# check_directory_depth.sh ... # only the named files +# +# Exits 0 when everything is within the cap, 1 when anything is over. +# ============================================================================ + +set -euo pipefail + +SCRIPT_NAME="$(basename "$0")" +readonly SCRIPT_NAME +readonly MAX_DEPTH=2 + +readonly EXCLUDED_PREFIXES=( + third_party/ + docs/superpowers/ + .github/skills/ +) + +# Directory names that never count as taxonomy. Matched as whole path +# segments, so a file called `lib.sh` or a directory `libvirt/` is unaffected. +readonly EXEMPT_SEGMENTS=( + lib + tests +) + +usage() { + echo "Usage: $SCRIPT_NAME [--all | ...]" + echo "Options:" + echo " --all Check every tracked file" + echo " -h, --help Show this help" + echo + echo "Files must sit at most $MAX_DEPTH directories below the repo root." + exit 0 +} + +# True when the path sits under a tree we deliberately do not police. +is_excluded() { + local path="$1" prefix + for prefix in "${EXCLUDED_PREFIXES[@]}"; do + [[ "$path" == "$prefix"* ]] && return 0 + done + return 1 +} + +# True when any DIRECTORY component of the path is an exempt segment. The +# final component is the filename and is deliberately not considered. +has_exempt_segment() { + local path="$1" dir="${1%/*}" + # No slash means the file is at the root: no directory components at all. + [[ "$path" == */* ]] || return 1 + local IFS=/ + local -a parts + read -r -a parts <<<"$dir" + local part segment + for part in "${parts[@]}"; do + for segment in "${EXEMPT_SEGMENTS[@]}"; do + [[ "$part" == "$segment" ]] && return 0 + done + done + return 1 +} + +# Number of directories above the file: a/b/c.sh -> 2. +depth_of() { + local path="$1" + [[ "$path" == */* ]] || { + echo 0 + return + } + local dir="${path%/*}" + echo $(($(tr -cd '/' <<<"$dir" | wc -c) + 1)) +} + +# Print "\t" for each in-scope file that exceeds the cap. +report_violations() { + local path depth + for path in "$@"; do + [[ -n "$path" ]] || continue + is_excluded "$path" && continue + has_exempt_segment "$path" && continue + depth="$(depth_of "$path")" + if ((depth > MAX_DEPTH)); then + printf '%s\t%s\n' "$depth" "$path" + fi + done +} + +main() { + local -a targets=() + + if [[ $# -eq 0 ]]; then + echo "Error: pass --all or one or more file paths" >&2 + exit 1 + fi + + if [[ "$1" == "--all" ]]; then + mapfile -t targets < <(git ls-files) + else + targets=("$@") + fi + + local violations + violations="$(report_violations "${targets[@]}" | sort -rn)" + + if [[ -z "$violations" ]]; then + echo "check_directory_depth: all checked paths are within $MAX_DEPTH directories" + return 0 + fi + + local count + count="$(printf '%s\n' "$violations" | wc -l)" + + echo "check_directory_depth: $count path(s) over the $MAX_DEPTH-directory cap:" >&2 + printf '%s\n' "$violations" | while IFS=$'\t' read -r depth path; do + printf ' %3d %s (+%d)\n' "$depth" "$path" "$((depth - MAX_DEPTH))" >&2 + done + return 1 +} + +while [[ $# -gt 0 ]]; do + case "$1" in + -h | --help) + usage + ;; + *) + break + ;; + esac +done + +main "$@" diff --git a/meta/scripts/report_live_state.sh b/meta/scripts/report_live_state.sh new file mode 100755 index 00000000..5b6ab93b --- /dev/null +++ b/meta/scripts/report_live_state.sh @@ -0,0 +1,119 @@ +#!/usr/bin/env bash + +# ============================================================================ +# Report every place on this machine that points into this repo. +# +# The restructure moves whole subtrees out of testsAndMisc. Anything that +# names a path inside the repo - a systemd unit, an /etc config, a symlink on +# PATH - keeps pointing at the old location and breaks silently once the +# target moves. This script is the checklist: run it before a move to record +# what must be repointed, and after a move to prove nothing was missed. +# +# report_live_state.sh # human-readable report +# report_live_state.sh --check # exit 1 if anything still points here +# +# --check is the post-move gate. A clean run means no live reference remains. +# ============================================================================ + +set -euo pipefail + +SCRIPT_NAME="$(basename "$0")" +readonly SCRIPT_NAME +readonly REPO="${REPO:-$HOME/testsAndMisc}" + +CHECK_ONLY=0 + +usage() { + echo "Usage: $SCRIPT_NAME [--check]" + echo "Options:" + echo " --check Exit 1 if any live reference into the repo remains" + echo " -h, --help Show this help" + exit 0 +} + +# Units whose ExecStart/WorkingDirectory/Environment names a repo path. +# Root units need no privilege to read; user units live under $HOME. +report_systemd() { + echo "## systemd units" + local found=0 unit + while IFS= read -r unit; do + [[ -n "$unit" ]] || continue + found=1 + printf ' %s\n' "$unit" + grep -n "$REPO\|%h/testsAndMisc" "$unit" | sed 's/^/ /' + done < <(grep -rl "$REPO\|%h/testsAndMisc" \ + /etc/systemd/system "$HOME/.config/systemd/user" 2>/dev/null | sort) + ((found)) || echo " (none)" + return "$found" +} + +# Non-unit config under /etc that hardcodes a repo path (guard-lib targets, +# sudoers drop-ins, pacman hooks). +report_etc() { + echo "## /etc references" + local found=0 path + while IFS= read -r path; do + [[ -n "$path" ]] || continue + case "$path" in */systemd/*) continue ;; esac + found=1 + printf ' %s\n' "$path" + grep -n "$REPO" "$path" 2>/dev/null | sed 's/^/ /' + done < <(grep -rl "$REPO" /etc 2>/dev/null | sort) + ((found)) || echo " (none)" + return "$found" +} + +# Symlinks anywhere in $HOME resolving into the repo: i3blocks, ~/.local/bin, +# oh-my-zsh custom. Depth 4 covers every known location without walking the +# whole home directory. +report_symlinks() { + echo "## symlinks into the repo" + local found=0 link target + while IFS= read -r link; do + [[ -n "$link" ]] || continue + found=1 + target="$(readlink "$link")" + printf ' %s -> %s\n' "$link" "$target" + done < <(find "$HOME" -maxdepth 4 -type l -lname "*testsAndMisc*" \ + -not -path "$REPO/*" 2>/dev/null | sort) + ((found)) || echo " (none)" + return "$found" +} + +main() { + local any=0 + report_systemd || any=1 + echo + report_etc || any=1 + echo + report_symlinks || any=1 + + if ((CHECK_ONLY)); then + if ((any)); then + echo + echo "$SCRIPT_NAME: live references into $REPO remain (see above)" >&2 + return 1 + fi + echo + echo "$SCRIPT_NAME: no live reference points into $REPO" + fi + return 0 +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --check) + CHECK_ONLY=1 + shift + ;; + -h | --help) + usage + ;; + *) + echo "Unknown option: $1" >&2 + exit 1 + ;; + esac +done + +main