mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-11 18:54:03 +01:00
7e2d78c7db
252 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 7e2d78c7db |
Add the G29 shifter HID-BPF fix (1st/3rd engage reliably)
Live on this host since 2026-09-11 but never committed. The wheel firmware decodes the shifter's hall sensor itself and this unit's top row rests at Y 180-193, below the ~195 engage threshold, so 1st/3rd often did not register. g29_shifter.bpf.c re-decodes those two gates from the raw X/Y bytes with hysteresis (engage >= 160, release <= 140) and ORs the bit in before hid-logitech parses the report; it never clears a bit, so the firmware's working gates cannot regress. fix_g29_shifter.sh builds, attaches and installs it persistently (udev-hid-bpf rule + a systemd safety-net unit pulled in on every re-enumeration); g29_shifter_capture.py records and grades raw captures per gate. Two files were over the 250-line cap: the test file is split at the record/CLI boundary with the report builders moved into a conftest fixture (the naming hook allows only test_*.py in tests/), and the grader's docstrings are tightened. No behaviour change to the installed fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SS8M5wk88nJTXxDWz61KKk |
|||
| ce3c4213b0 |
Arch journal cap: 300M -> 4G, vacuum only past the cap
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
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
300M held about two days on kuhy's desktop: one Steam boot wrote 480k journal lines and the evidence for a curfew failure two weeks earlier was already gone when it was needed (2026-09-13). The three Arch copies of the journal tweak (arch_hardware, arch_perf_fixes, arch_perf_report) move to a 4G cap, and "large"/vacuum now mean past that cap — a 2G journal is deliberate retention, not a finding. Tests follow. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015mLnUeoJ3mUwGRTowTUH6d |
|||
| f15f00f3fe |
Re-point paths for the ~ reorganisation (home-tidy)
Also bumps the 16 meta/requirements pins the freshness gate flagged (anthropic 1.5.0, numpy 2.5.3, pytest-randomly 5, flake8-bugbear 26.9.9, ruff 0.16.7, pyright 1.1.414, selenium 4.49.0, hypothesis 6.168.0, lxml 6.1.3, pipdeptree 4.2.5, the two types-* stubs) and Flutter 3.47.3 in meta/.fvmrc. One test still expected the pre-move ~/mcp-servers path. The pre-commit hook fed .zsh files to shfmt and shellcheck, neither of which parses zsh (SC2148/SC1071), so no zsh file could be committed since the hook was tightened on 2026-08-22; those now get `zsh -n` instead. The hook's own shfmt pass also staged its reformat of 8 shell files. CI_GREEN_SKIP because main is red on exactly the dependency-freshness finding this commit fixes, which is the documented use of that flag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwtowrowDXx6ccmWVPkCaF |
|||
| 0cea82cab5 |
feat: the nftables drift check now runs itself at boot
`verify` gated on the two invariants whose breach cut every bridged container off the internet on 2026-08-29 and again after the 2026-08-30 reboot -- but nothing invoked it, so it reported drift only when a human thought to ask, and the outage it exists for is exactly the one nobody thinks to ask about. nftables-drift.service now runs it after nftables.service, docker.service and network-online.target at every boot, plus a daily Persistent timer. A breach is a failed unit in `systemctl --failed`, not a log line: a self-check that detects a failure has to gate, not warn. Proven in ~/utils/vmbox across two real reboots -- drifted /etc leaves the unit failed with the diff and the missing docker0 accept in its journal, matching /etc leaves it active with no false positive -- then installed and run green on the host. Not yet observed across a host reboot. Also bumps the gatelock pin to v0.7.3, which is what turned main's dependency-freshness job red; CI_GREEN_SKIP is set because this commit is that fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015d48t4qsQLBJnDwDeo2Nom |
|||
| ce33c935e4 |
Run the website's post editor as a loopback unit, no Caddy route
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
Pre-commit checks / pre-commit (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
|
|||
| adf2e402df |
Grade against 3.14 too, not just run on it
`requires-python` and the CI pin already said 3.14, but ruff, mypy and pylint were still grading `meta/` against 3.10 -- three tools judging an interpreter nothing runs any more, and ruff withholding the pyupgrade fixes that only become safe at the newer target. `pin_ci_python.py` rewrites these three knobs. What the stale target was hiding: `datetime.timezone.utc` where `datetime.UTC` is now correct (UP017), and ruff's stable formatter applying PEP 758 -- `except (A, B):` becomes `except A, B:`. That makes the source 3.14-only at the syntax level, which is what `requires-python = ">=3.14"` already promised; kuhy chose it deliberately over holding ruff a release back (2026-08-29). Also drops a comment in `wsg_grabber/models.py` that the bump makes false: it explained why the file used ``str, Enum`` instead of ``StrEnum`` under mypy at 3.10, sitting directly above a ``class Verdict(StrEnum)`` that had already stopped obeying it. ruff, ruff-format, mypy --strict and pylint all clean at the new target. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SfnJmxxsZ534AKk16rC2Gr |
|||
| c5cd63fb80 |
token_audit: measure tool-call batching per API message
Some checks are pending
Counting transcript records reports 1.00 tool calls per message no matter what the model did: the harness writes one JSONL record per tool_use block, all sharing message.id. A hand-rolled per-record count read the real 1.9% batching rate as 0.0%, which nearly shipped as a headline finding. Group by message.id instead, and report it, so the number comes from a tested code path with the window handled by find_transcripts rather than an ad-hoc timestamp. Three snapshot fields give next week's run a baseline. Axes was at the 12-attribute cap, so the counters live in their own Batching dataclass with the two ratios as properties. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NiSSDu4gKbp9GGxTTtXvYW |
|||
| 4c426eae0e |
Make install_core_system.sh work on a fresh machine
Two of the seven modules invoked python_pkg/ paths that stopped existing when screen_locker and steam_backlog_enforcer were extracted into their own repos, so the first thing the installer did on a fresh machine was fail. Nothing installed guard-lib either, which meant the CORE "Midnight shutdown timer" module always died with "guardctl not found on PATH". Every module now resolves through install_module_from_repo, and require_extracted_repo clones a missing sibling instead of only printing instructions -- otherwise "clone it yourself" is not actionable on the bare machine this script exists to set up. The clone drops privileges to the invoking human via sudo -u, because an installer that re-execs under sudo would otherwise leave a root-owned checkout in the user's home; a sandbox cannot catch that, since the guest is root throughout. A clone that fails is still a loud, fatal module failure, never a silent skip. guard-lib is addressed as the "utils" repo: it is a subdirectory of github.com/kuhyx/utils, so asking for "guard-lib" would resolve to ~/guard-lib and derive a clone URL that does not exist. It installs first, before the modules that need guardctl. Separately, the security hardening suite's screen_locker section hardcoded $HOME/testsAndMisc/python_pkg/screen_locker/screen_lock.py. That path went away with the extraction, so all five checks silently SKIPPED -- reporting a clean run for tests that never executed, which is the exact failure mode lib/extracted_repos.sh was written to prevent. It resolves the repo properly now ($HOME is also wrong under sudo), and the assertions are rewritten against the current implementation: the four symbols they grepped for no longer exist anywhere, having been replaced by SPORT_CHOICES, SPORT_TABLE_TENNIS, _validate_table_tennis and MANUAL_WORKOUT_REFLECTION_MIN_CHARS. On this host that takes the suite from 18 passed / 1 failed / 5 skipped to 23 passed / 0 skipped. The task file's "18 passed / 6 skipped out of 17" target was stale -- the suite is 24 checks now -- so it could not be matched literally. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp |
|||
| 6b828d29aa |
Extract digital-wellbeing; the directory-depth cap now enforces
The last unit over the cap is now github.com/kuhyx/digital-wellbeing, cloned at ~/digital-wellbeing, and `--warn` is gone from the directory-depth-cap hook. `check_directory_depth.sh --all` exits 0 with zero violations, down from 46 at the start of the session. That was the point of the campaign. History was filtered across all three paths the unit has lived at (63 commits); the current path alone holds 2. The six suites that test this code lived in linux_configuration/tests/ rather than beside it, so they were filtered separately (24 commits) and merged in as a second root. A path filter of the unit alone would have left them here, testing code that had left the repo -- and their names would still have been in shell-tests.yml's hardcoded arrays, failing CI on missing files. linux_configuration/lib/common.sh is vendored into vendor/ rather than trimmed to the four functions actually used: set_actual_user_vars calls two of the others, so a trim breaks silently. It is in vendor/ and not lib/ because test_music_parallelism.sh copies the unit's own lib/ into a scratch worktree while stubbing the shared lib; in one directory the real common.sh overwrote the stub, and the suite silently measured the wrong library. Running the callers caught what shellcheck and grep both missed: check_and_enable_services.sh died with "DW_REPO: unbound variable" because the definition sat below its first use. It now runs clean and reports ok for every extracted-unit check. Two long-standing live defects fixed as a consequence. The pacman-wrapper drift manifest embeds ABSOLUTE paths and had named deleted scripts/ paths since the flatten, so the check reported a permanent false "stale or tampered"; it now verifies, with unchanged hashes proving the extracted bytes are identical. And the last two stale live references are gone: 8 -> 0. The two pre-commit excludes for setup_midnight_shutdown.sh are dropped rather than moved. lsattr shows nothing in the unit is chattr +i any more, and the fixer hooks leave the file alone when allowed to run. Removing 80 shell files also shrank the jscpd denominator enough that the virtualenv/tqdm clones inside the GENERATED .ci-mirror-venv/ tipped the repo to 2.09% and blocked this commit, which adds no duplication. That directory is the same vendored third-party shell the ignore list already excludes as .venv/, just under another name; jscpd walks the working tree rather than the index, so being untracked did not spare it. Excluded by name: 1.74%. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp |
|||
| f72455662c |
Extract the hosts blocker into its own repo
linux_configuration/periodic_background/hosts/ is now github.com/kuhyx/hosts-blocker, cloned at ~/hosts-blocker. Clears 5 of the 46 directory-depth violations; the remaining 41 are all digital_wellbeing, so --warn stays on the depth hook until that unit moves too. History was filtered across ALL FOUR paths the unit has lived at (hosts/, linux_configuration/hosts/, linux_configuration/scripts/periodic_background/ hosts/, and the current one). Filtering on the current path alone would have preserved 1 commit out of 51 -- the flatten moved it recently, so a naive single --path silently truncates the history to the flatten commit and still looks fine in `git log | head`. No source line changed: all 15 files are byte-identical to the copies removed here. install.sh already resolved its lib/ via BASH_SOURCE. The six callers now resolve the checkout through a new shared helper, linux_configuration/lib/extracted_repos.sh, which reads the invoking user's home from SUDO_USER's passwd entry rather than $HOME -- several of these scripts re-exec under sudo, where $HOME is /root. A missing checkout fails loudly with a clone instruction; it is never a silent skip, which is the failure mode that left screen-locker's self-repair dead for five months after the 2026-05-15 reorg. The live blocker is untouched: /etc/hosts keeps its chattr +i, and the guards load their plugins from /usr/local/share/guard-lib-plugins/ (byte-identical by md5), not from a repo path. Verified: 25 tests pass from ~/hosts-blocker; shellcheck clean on the helper and all six callers; sudo resolution prints /home/kuhy/hosts-blocker; test_setup_dns_blocker.sh passes; report_live_state.sh --check is byte-identical before and after, so no new stale references. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp |
|||
| 7597831915 |
Prune 655 superpowers evidence and contract artifacts
These accumulate roughly one per commit and were the largest single driver of tracked-file growth: 695 artifacts, 487 of them from 2026-08 alone. Kept the 20 most recent of each plus both templates. A count cap rather than a time window, because the growth is bursty - a 7-day window would still have kept 393 files and bounded nothing. Selection is by git commit date, not by filename: the corpus mixes -2026-05, -20260604 and undated slugs, so a filename filter would have mis-sorted ~48 files. Safe to delete: nothing reads the historical corpus. Every reference in meta/scripts is either template.json or validation of files staged in the current commit, and the three hooks still pass. sessions/ is append-only and untouched. Tracked files 1993 -> 1340. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp |
|||
| 0fb3e3eee2 |
Cap extensionless scripts, and split the one that was hiding
check_file_length.sh required a dot in the filename, so every
extensionless executable was silently uncapped. is_source_file() now
reads the first two bytes and accepts a shebang, which catches real
scripts while still skipping data files like i3/config.
That immediately surfaced boot_recovery/boot-repair at 756 lines --
three times the largest capped shell file in the repo, uncapped purely
because its name has no dot in it. Split along its own section markers
into lib/{discovery,repair_fs,repair_kernel,modes}.sh, leaving the entry
script at 218.
The report block stays inline on purpose. It is top-level code with exit
calls rather than function definitions, and sourcing it changed control
flow: 30 of 42 tests failed. That is recorded in the lib headers so the
next reader does not retry it.
No vmbox pass for this one, deliberately. boot-repair is UEFI/ESP tooling
and the guest is BIOS/GRUB, so a sandbox green would be actively
misleading -- the vmbox skill says so itself. The evidence is the fixture
suite, which drives the real script against synthetic root trees: 42
passed before, 42 passed after.
verify_shell_split.sh reports 31 of 32 functions byte-identical. The one
difference is clean_shadow_files, where quoting $ESP_MOUNTPOINT fixed a
genuine SC2086 that only became visible once the function left the
monolith -- shellcheck could no longer see the readonly declaration and
stopped assuming the expansion was safe.
lib/tests/run_all.sh delegates to that same suite rather than standing up
a second, weaker harness: these libraries need the entry script's strict
mode and globals, so testing them in isolation would test a fiction.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp
|
|||
| 08aac8d7d1 |
Flatten i3-configuration and misc/testsAndMisc-bash
i3 config was filed under periodic_background, which it never was. It is now linux_configuration/i3/ (config + installer) and linux_configuration/i3blocks/ (the status scripts), both at depth 2. misc/testsAndMisc-bash/ was a directory named after the repo it already lived in, so its contents moved up to misc/. Its stray .gitignore came along, keeping the media rules working from the new location; its .vscode/tasks.json did not, because it invoked Bash/transcribe.sh, a path this repo dropped long ago. Most of this commit is the references the previous flatten missed. Its regex skipped anything with a variable prefix, which left 16 files -- including check_and_enable_services.sh and install_core_system.sh, the orchestrators -- still naming $CONFIG_DIR/scripts/periodic_background/... It had also rewritten three i3 paths into scripts/i3blocks/, a directory that has never existed; the i3blocks suites caught that by failing. 13 more symlinks broke on the i3 move. Repointed, and cpu_monitor/volume/ disk were run again to confirm the bar still reports. Depth 139 -> 104. Roughly 47 of the remainder sit in digital_wellbeing and hosts, still deferred. The rest encode real structure -- a C project, Python package directories, artgate's routes/styles, systemd unit dirs -- so flattening them further would destroy information rather than taxonomy. Verified: 1798 tests at 100%, both i3blocks suites pass, the shell coverage gate is clean, and the relocated media rules still ignore what they should while test_fw.srt stays tracked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp |
|||
| 4f2b0ccdf7 |
Dissolve linux_configuration/scripts/ and single_use/
Two levels of pure taxonomy carrying no information, and a 2-directory
cap leaves no room for them. Categories now sit directly under
linux_configuration/: features, fixes, utils, misc, fresh-install,
installers, diagnostics, gaming, boot_recovery, periodic_background, lib,
meta, tests. A script's own lib/ and tests/ are exempt from the cap, so
those stay put.
Depth violations 248 -> 139; roughly 40 of the remainder are inside
digital_wellbeing and hosts, deferred until the guard-lib repoint.
The moves are the easy half. What breaks is every path that counted
levels, and three classes of it had to be found by resolving paths rather
than reading diffs:
- 52 relative `source` lines for lib/common.sh and lib/android.sh. The
correct prefix differs per file: 40 were ../../ and 12 were ../../../,
so a blanket replace would have fixed one set and broken the other.
- REPO_ROOT in 7 fixes/lib/tests harnesses and setup_syncyomi_guard.sh,
each walking up four levels from a directory now two levels shallower.
This caused a real regression -- the fixes suite went red with
"log_warn: command not found" -- caught by running it and comparing
against the pre-move baseline worktree.
- conftest.py's sys.path entries, which took the pytest gate down with
14 collection errors, and the `# shellcheck source=` directives, which
took shellcheck down with SC1091.
16 symlinks in $HOME broke, including the entire i3blocks bar. All
repointed, and cpu_monitor/memory/disk/time/volume were run to confirm
they emit real output rather than merely resolving.
Verified: 1798 tests pass at 100% coverage, 8 of 9 lib/tests suites pass
(the ninth needs the coverage jail, identically at baseline), pre-commit
is clean, and the two untracked secret configs are still ignored after
their gitignore paths changed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp
|
|||
| a5209b6878 |
Extract phone_focus_mode together with focus_policy
github.com/kuhyx/phone-focus-mode The two halves go as one repo: focus_policy parses phone_focus_mode's config.sh into the typed policy that focus-owner consumes, and nothing else references it. 77 commits of history, plus the two Python suites from linux_configuration/tests/ and the six mutation fixtures from meta/scripts/fixtures/, all of which named phone_focus_mode paths. Running the suites -- not reading them -- turned up a real defect the extraction would otherwise have inherited. config.sh sources the untracked config_secrets.sh, and ctl_libs_harness.sh never pinned HOME_LAT/HOME_LON, so the distance assertions were checking against whatever real coordinates that file happened to hold. The suite passed on this machine and could not run anywhere else, and a real home location was load-bearing in a pass/fail condition. The harness now pins a pair 4300 m south of the coordinate its own case seeds, and config.sh sources the secrets file only when it exists. 136/136 pass with no secrets file present. Before making the repo public I scanned the whole history for the known home coordinates and for any committed config_secrets.sh: zero hits, and the file was never tracked. The new repo also gained a .gitignore, since none came across in the filter-repo pass. test_magisk_service.sh hangs. It hangs identically in the pre-extraction baseline worktree and appears in no CI list here, so the new repo's CI skips it with that reason rather than pretending it passes. check_python_location.sh loses its phone_focus_mode exception now that the directory is gone; every tracked .py still passes it. The .gitignore rules for config_secrets.sh and the keystore STAY -- the working copy is still on this machine and still holds a real location. Depth 262 -> 248. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp |
|||
| fe8629e104 |
Extract android_guardian; heavy_job_lock goes to its installer
github.com/kuhyx/android-guardian, cloned to ~/android-guardian
periodic_background/utils/ held two unrelated things: the Magisk module
and heavy_job_lock.sh. Only the module was a unit, so the directory is
split rather than moved.
heavy_job_lock.sh goes to digital_wellbeing/, NOT to ~/utils. Measured
rather than assumed: of its consumers only install_pacman_wrapper.sh
reads the repo copy, while pacman_wrapper.sh, makepkg_capped.sh and
meta/scripts/run_with_heavy_lock.sh all read the installed
/usr/local/bin copy. A library belongs with whoever installs it, and
exporting it would have added a cross-repo edge for one caller.
Fixed a bug that predates the extraction: update_android_hosts.sh set
GUARDIAN_MODULE_DIR to "$SCRIPT_DIR/android_guardian", a sibling that
never existed at that path -- packaging could not have worked from this
checkout at all. It now reads ${ANDROID_GUARDIAN_DIR:-~/android-guardian}
and require_guardian_module() gates build_module(), the single funnel
every packaging path goes through, so a missing checkout names the clone
command instead of failing inside a cp four helpers deep.
Verified both directions: with no checkout the guard fires with
instructions; with one, the module builds -- 182331 blocked domains, a
1.1 MB zip. The subsequent adb push failed on device-side storage, which
this commit does not touch.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp
|
|||
| a1f8044a4a |
Extract system-maintenance, the isolated subsystem
github.com/kuhyx/system-maintenance, cloned to ~/system-maintenance First of the four live subsystems, and the one with no cross-unit source edges, so it establishes the pattern for the harder three. Passed in vmbox first -- the installer ran from an extracted-layout copy in the guest and detected its distro without any monorepo path -- then on the host. Its 21 Python and shell test files lived in linux_configuration/tests/, reaching bin/ through a shared conftest. Twenty moved with the code and got their own conftest; the shared one stays, since it serves several script directories that are not going anywhere. test_shutdown_timer_monitor.sh stays here. It is a pair test: it checks that digital_wellbeing's installer template and the monitor it installs still agree, so it belongs to whichever half owns the installer. It now reads the monitor from ~/system-maintenance and SKIPs when that checkout is absent. An intermediate version of that test fell back to /usr/local/bin, which HUNG -- the installed copy is stale and differs from source, so the test was comparing an installer template against a binary it never generated. Falling back to it would have been worse than skipping, so the fallback is gone and the comment says why. Also removed test_hosts_file_monitor.sh from shell-tests.yml's explicit list: it left with the subsystem, and CI would have failed on the missing file rather than quietly skipping it. Verified: 136 tests pass in the new repo, 202 still pass here, the pair test passes against the checkout and skips without it, and both the shell coverage gate and shellcheck are clean. Depth 294 -> 267. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp |
|||
| 6b5fe8e7a7 |
Extract mtk_root, with the libraries that lived outside it
github.com/kuhyx/mtk-root
Unlike the app extractions, mtk_root could not travel alone: its four
libraries and three of its four test suites sat in scripts/lib/, one
directory up from the stages that source them. Taking only the directory
would have orphaned eight files here and left the new repo unable to
source anything, so all twelve moved in one filter-repo pass. The
mtk_root/ + lib/ sibling layout is preserved, so every
`source "$SCRIPT_DIR/../lib/..."` resolves with no call site edited.
Two things the new repo needed that the monorepo provided implicitly:
lib/tests/run_all.sh -- the copy here is shared with the non-mtk suite
and stays. Without a new one, shell-tests.yml's
glob over */lib/tests/run_all.sh would have
stopped covering these suites on a green build.
.shellcheckrc -- external-sources + source-path=SCRIPTDIR, which
is what lets shellcheck run with no severity
filter and still pass on the ../lib/ sources.
mtk_harness.sh stays here despite the name: test_common_datetime.sh
sources it. Removing it broke that suite outright, which running the
suite caught immediately. Its header now describes what it actually
serves; the misleading name is kept on purpose, since renaming would
churn its one consumer for nothing.
Verified: 84 tests pass in the new repo with shellcheck clean, 55 still
pass here, and check_shell_coverage.sh --all exits 0 with the four
mtk_root allowlist entries gone.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp
|
|||
| a0459b99a0 |
Extract reverse_survivors, kcd2_dice_solver and focus_owner
Three self-contained apps move to their own public repos with full history (8, 14 and 30 commits, each matching the monorepo count): github.com/kuhyx/reverse-survivors github.com/kuhyx/kcd2-dice-solver (cloned to ~/kcd2-dice-solver) github.com/kuhyx/focus-owner Each gained a CI workflow. Only reverse_survivors had one here; the other two were never gated by anything, which is part of why they no longer belong in a repo whose CI is shared. What stayed: setup_kcd2_dice_solver.sh, which deploys the solver rather than being part of it. It now builds from ~/kcd2-dice-solver, and the post-commit hook it generates drops its `git diff -- kcd2_dice_solver/` filter -- in a standalone repo every commit is solver code, so the old filter could never match again. focus_owner's two references to python_pkg.focus_policy now name the phone-focus-mode repo, where that generator is headed. Running the live kcd2 unit -- rather than reading it -- found three real breakages that no amount of grepping would have: pnpm ignoring the package.json "pnpm" field, pnpm refusing to purge node_modules without a TTY, and the Docker container still bind-mounting the deleted monorepo path. All three are fixed; the unit exits 0 and https://dice.kuhy.duckdns.org/ serves the freshly built bundle. Depth violations 417 -> 303. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp |
|||
| ec5d3aba34 |
Archive billsplit and everything coupled to it
The coupled case the previous archive commit deferred. billsplit is not just a directory: a coverage package exists only to gate it, app_icons holds a registry entry pointing into it, a CI workflow builds it, and .binary-allowlist / .gitignore carry a paired set of rules for its launcher icons. Removing the directory alone would have failed the 100% coverage gate and left three configs naming a missing path. So all of it goes in one commit: billsplit/ 73 files python_pkg/billsplit_coverage/ 5 files, gated billsplit only .github/workflows/billsplit-ci.yml the app_icons AppIcon(key="billsplit") entry the .binary-allowlist patterns and their .gitignore ! overrides the .prettierignore fixture line billsplit/ and billsplit_coverage/ were extracted in a single filter-repo pass (8 commits, matching the monorepo) and pushed to github.com/kuhyx/testsAndMisc-archive, where the coverage package joins the archive's existing python_pkg/ tree. Presence was confirmed through the GitHub API before anything was deleted here. .binary-allowlist is now empty of patterns -- billsplit's icons were its only entries. The policy header stays and records that no exception is in force, and check_no_binaries.sh still exits 0 on a comment-only file. Verified: 2095 tests pass at 100% coverage, down from 2114 by exactly the 19 billsplit_coverage tests. Depth violations 451 -> 417. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp |
|||
| ea281134b6 |
Archive bucket_catch and poker-stakes to testsAndMisc-archive
Two self-contained apps, neither referenced by any other code, systemd unit or symlink -- which is why they go first in the restructure. Full history preserved via git filter-repo and merged into github.com/kuhyx/testsAndMisc-archive at their original repo-relative paths, matching how C/, CPP/, horatio/ and the rest already sit there. Archived commit counts match the monorepo exactly: bucket_catch 10, poker-stakes 2. poker-stakes-ci.yml goes with the app it builds, so no workflow is left pointing at a missing path. bucket_catch had no workflow. Verified before deleting: file counts match (64 and 65), diff -r against the working tree is clean, and the GitHub API lists both units in the archive. Depth violations drop 556 -> 451. billsplit is deliberately NOT part of this commit: it is coupled to python_pkg/billsplit_coverage and an app_icons registry entry that the 100% coverage gate requires be deleted in the same commit as the app. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PJrqKERAo5QUeEHWS5Asgp |
|||
| 46c8f065e8 |
Wire the shell ratchet into pre-commit, and test the coverage tooling
check_shell_coverage.sh has existed for a while and was never in .pre-commit-config.yaml, so nothing ran it: a new shell library with no suite beside it could land freely. Wiring it was unaffordable while the gate measured a jail-run 100% bar (--all took over 600s). After |
|||
| 5ba2cbd476 |
Dedupe endurain imports against postgres when the API cannot answer
Endurain API keys are upload-only by design: the scope allow-list is
frozenset({"activities:upload"}), enforced in both validate_api_key_scopes
and the create schema, and the activities read route is mounted with
validate_access_token rather than validate_access_token_or_api_key. A key
therefore fails at authentication before any scope check, so the HTTP
dedupe check added in
|
|||
| 8ab15446fd |
Make the push gate check the push, and make its pytest stage real
`git push` took ~130s because ci-mirror re-ran `pre-commit --all-files` plus every lib/tests suite on every push, regardless of what changed. The larger problem found while measuring it: ci_mirror.sh invoked pytest_changed_packages.py with no arguments, and that script returns 0 on empty argv because it is normally a pre-commit hook fed staged paths. The pre-push python stage has therefore been running ZERO tests and reporting success. Behind it sat a genuinely failing test, fixed in |
|||
| 325d5a8baa |
Stop the endurain importer duplicating RunnerUp's own uploads
RunnerUp can now upload to Endurain directly as well as through the WebDAV inbox, and the two paths cannot see each other: the same run landed twice (activities 14 and 15, identical start time, distances differing only by tcx/gpx rounding). The ledger only knows what this importer handled, so it could not catch it. Add a third dedupe check that asks Endurain what it already holds, queried once per run. Endurain exposes no query-by-start-time endpoint, so a page of recent activities is fetched and compared client-side, with a +/-120s tolerance: the file for activity 15 embeds 21:51:04Z while the server stored 21:51:05Z, so exact equality misses the very duplicate that motivated this. A failed or unauthorised query returns None, never an empty list, so it can never be read as "nothing matches" and upload on the strength of it. Export filenames are parsed as LOCAL wall-clock time. RunnerUp_2026-08-14- 00-58-45_Running is 2026-08-13 22:58:45 UTC; reading it as UTC would move a late-evening run onto the wrong day and defeat the comparison. Also fix the adb fallback, which broke the moment a second device attached: phone_attached() returned true if ANY device was in the 'device' state, then every bare adb call failed with "more than one device/emulator" as a warning on a path nobody watches. The serial is now resolved once and threaded into every call as -s. Ambiguity is settled by configuration, never by guessing -- several devices and no ENDURAIN_ADB_SERIAL means the fallback is skipped. The remote check stays inert until the API key is granted activities:read; until then it falls back to the ledger, which is the previous behaviour. 85 tests, 100% branch coverage on all four modules, no suppressions. The oversized test modules are split by concern to stay under the 250-line cap. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbYEnfGnf1988cdFdGebot |
|||
| b399fab56d |
Take the fixes/lib suite from 110s to 37s, same 645 assertions
Three changes, none of which touch what a single test asserts:
thorium_repairs.sh's `sleep 4` becomes `sleep ${THORIUM_STARTUP_WAIT:-4}`
and the harness sets 0.2s. Three cases were each paying four real
seconds to watch a stubbed browser stay up; the stub sleeps 30s, so the
`kill -0` check is unchanged. That file: 17.0s -> 5.6s.
_t_hide was the rest of it. It walks PATH and creates a symlink per
executable -- ~13.5k here, 4.6s a build -- and the suite has ~16
distinct tool sets. Now the full farm is built ONCE and `cp -al`
hard-links it per set, with only the hidden names deleted from the copy.
The cache also moved out of $TEST_TMPDIR, which is per-file, into a
run-scoped dir run_all.sh creates and removes, so it survives between
files.
110s -> 98s (sleep) -> 85s (shared cache) -> 37s (hard-linked farm).
645 OK, 0 FAIL throughout. The cases that only pass when a tool is
genuinely unfindable -- install_pyroveil's missing-dependency arms,
fix_aggressive without sqlite3 -- still pass, which is what proves the
farm is still hiding rather than quietly returning a full PATH.
lib_test_core.sh hit exactly 250 lines, so _t_hide and the farm move to
lib_test_path.sh (194 + 67).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|||
| cb1e8c53c9 |
Test nvidia_config.sh to 100%; the 8-lib campaign is done (85 -> 84)
76/76 lines through run_all.sh at --min 100, exit 0. 45 assertions over
15 cases across all four functions.
XORG_CONF and XORG_CONF_D are plain globals ASSIGNED inside
configure_xorg, so the default had to move to the assignment itself --
exporting a value from the harness alone would have been overwritten on
entry. PROFILE_FILE and USER_HOME take the ordinary `${VAR:-default}`
form. The real /etc/X11/xorg.conf.d/20-nvidia.conf keeps its Jun 2025
mtime and ~/pyroveil does not exist after the suite runs.
configure_xorg's RenderAccel value is asserted to be "true" AND asserted
never to be "false": the lib's own comment records that "false" forces
software rendering and pins Xorg above 30% CPU, so that is a regression
worth a dedicated negative assertion.
install_pyroveil's prompt could not be asserted on its text -- bash
writes a `read -p` prompt straight to the terminal, so it never reaches
captured output. The branches are distinguished by the non-interactive
arm's "Auto-installing" line instead, which is observable.
bt_audio.sh re-measured at 127/127 with the new files present.
That is all eight libs from the original handoff: arch_sysctl (banked as
unmeasurable and quarantined), arch_cpu, arch_hardware, thorium_repairs,
hosts_guard_migrate, hosts_guard_rollback, ubuntu_perf_fixes and
nvidia_config. Allowlist 91 -> 84.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|||
| 558883f281 |
feat(endurain): self-hosted run viewer fed by RunnerUp WebDAV exports
Stands up Endurain behind the existing Caddy edge and imports RunnerUp activities into it. Endurain is a VIEWER: screen-locker's workout gate still reads the phone over adb and is untouched here, because a WebDAV drop folder is spoofable by anything that can write to ~/cloud and must not become a screen-unlock authority. Primary path is phone -> WebDAV (existing dufs) -> ~/cloud/RunnerUp/ -> importer -> Endurain, with an adb pull as fallback when a run has not been uploaded yet. A systemd user timer runs the importer every 15 min. Findings that shaped the implementation, each verified against the running system rather than assumed: - Endurain moved to Codeberg; the GitHub repo is a read-only mirror stale at v0.17.7 against v0.19.0. The image is pulled from Codeberg. - postgres:18 moved PGDATA to /var/lib/postgresql/18/docker. Upstream's compose example still mounts the pre-18 path, which fails as an unexplained "container is unhealthy"; the real cause is only visible in docker logs. - Endurain seeds a hardcoded admin/admin via an Alembic migration with no env override, and endurain.kuhy.duckdns.org already resolved publicly. Publishing is therefore a separate script that refuses to install the Caddy site while those credentials still work. - Endurain performs NO duplicate detection, so the importer owns dedupe on two axes: a content hash, and an activity key that collapses the .gpx/.tcx pair RunnerUp writes for a single run (different bytes, same run). - Ambiguous uploads (5xx/timeout) are never re-queued, since the activity may already have been committed; only definite 4xx rejections are staged for retry. The importer exits non-zero so the timer surfaces the problem. - RunnerUp has no background sync at all, so every upload is a manual tap. - Its WebDAV synchronizer needs HTTPS with a real cert (Android blocks cleartext at targetSdk 36; self-signed is broken upstream) and a URL with no trailing slash, and cannot MKCOL against dufs - so the inbox must pre-exist. The phone gets a dufs credential scoped to /RunnerUp, verified unable to read Keepass or gdrive_backups. Verified: 52 tests at 100% branch coverage; the timer imports 4 real runs and re-runs create zero duplicates; https://endurain.kuhy.duckdns.org serves 200 with a valid cert and the other edge sites are unaffected; screen-locker's suite still passes 1090 tests at 100% coverage. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbYEnfGnf1988cdFdGebot |
|||
| ad55d1b6a1 |
Test ubuntu_perf_fixes.sh to 100%, take it off the allowlist (86 -> 85)
81/81 lines through run_all.sh at --min 100, exit 0, and check_shell_coverage.sh passes with the allowlist line already deleted. 59 assertions over 14 cases across all four functions: the sysctl drop-in with both the "already applied" skip and the documented-default fallback for an unreadable sysctl; both NVIDIA branches, since the helper-service path and the standalone fallback write different units and different undo lines; earlyoom install / already-installed / config-backup; and fix_failed_sssd for none, all five and exactly one failed unit -- that last one matters because the detection loop breaks at the first failure while the masking loop re-checks each unit, so a naive implementation would mask all five. The four write targets are now behind overrides defaulting to the real paths. The add_undo lines deliberately KEEP their literal /etc paths and are asserted to: the undo script runs on the real system, so seaming those would have written an undo that removes nothing. Neither /etc/sysctl.d/99-performance-tuning.conf nor /etc/default/earlyoom exists after the suite runs. bt_audio.sh re-measured at 127/127 with the new files present -- that canary runs after every addition to this suite now, because a test file silently broke it once already. The handoff is rewritten for the one remaining lib. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| 0201d5460a |
Clear both hosts-guard libs; teach the instrument done <redirect> (88 -> 86)
hosts_guard_migrate.sh 81/81 and hosts_guard_rollback.sh 65/65, both 100.00% through run_all.sh at --min 100, and check_shell_coverage.sh passes with each allowlist line already deleted. 82 assertions over 36 cases in three files sharing one harness: all four validate_requirements refusals, plugin installation, the mount-collapse loop in every arm (clears, fails, hits its 20-iteration bound, dry-run), per-instance migration for both the bind-mounted and the plugin flavour, and do_migrate's refusal to retire the legacy pacman hooks when nothing registered -- the arm that exists so a failed migration cannot leave the files with no unlock hook at all. INSTRUMENT FIX: rollback measured 97.01% with lines 69 and 108 uncovered, both `done <redirect>` closing a loop. Reproduced minimally: in a three-iteration loop ending `done >"$1"`, the body reports hits=3 and the `done` line hits=0, because bash attributes the redirected compound statement to the loop's OPENING line. That is the same defect _CLOSING_BRACE_OP already handles for `} |`, so _LOOP_END_OP is its twin. The redirect is required by the pattern: a bare `done` stays in the denominator, so genuinely uncovered loops are still caught. Audited as the instrument notes demand -- every newly excluded line printed with its text -- and it is exactly those two lines across all of fixes/lib. Acceptance pair unchanged and exact: dot_resolver_install.sh 39/39, rpi_nc_ca.sh 73/73. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| 0e9629e110 |
Test thorium_repairs.sh to 100%, lift the gate timeout (89 -> 88)
75/75 lines through run_all.sh at --min 100, exit 0, and
check_shell_coverage.sh passes with the allowlist line already deleted
so the pass cannot come from the exemption.
55 assertions over 23 cases: all five profile repairs plus test_thorium,
including every dry-run arm, the partial singleton-lock case, a corrupt
database and the sqlite3-absent path. The lib needed NO production
change -- it already takes every path from $THORIUM_CONFIG_DIR, so
pointing that one variable at the tmpdir confines it completely.
The gate's per-case timeout moves from the jail's 120s default to 600s.
That default is a hang-catcher, not a performance budget, and this suite
has outgrown it: 76s bare at 25 test files, 93s at 27, and kcov roughly
doubles it. The symptom was a lib FAILING the gate while measuring
perfectly -- bt_audio.sh printed "127/127 lines = 100.00%" and then
"warn: case timed out after 120s", which --fail-on-case-error turned
into a non-zero exit. bt_audio.sh and arch_cpu.sh both pass now.
The cost, recorded in the evidence: a genuine hang takes 10 minutes per
pass to surface instead of 2. test_thorium's `read -r -p` is exactly
that shape, so every case here feeds it stdin explicitly.
This also settles the quarantine in
|
|||
| 88ba911641 |
Test arch_hardware.sh to 100%, fix its dead journal vacuum (90 -> 89)
94/94 lines through run_all.sh at --min 100, exit 0. 42 assertions over 26 cases across all five functions, including both boot-loader flavours and the no-boot-loader path. PRODUCTION BUG, found by writing the tests and confirmed against this host: tweak_journal's regex required a space before the unit, but journalctl prints "305.5M in the file system" with no separator. The size check never matched, so the vacuum was unreachable and a multi-gigabyte journal would never have been trimmed. This is the third instance of the same defect -- arch_perf_report.sh:36,65 carried it too -- so the fix is identical: the separator is now optional. One case asserts the spaced form still matches, making this a widening. The mitigation tweak edits the boot loader, so /boot/loader/entries, /etc/default/grub and grub.cfg are now behind overrides defaulting to the real paths. This machine's boot configuration is never touched. _t_hide gets a cache: it symlinks every executable on PATH (13.5k here) and was rebuilding that farm on every call, which took the suite to 92s and pushed a jail case past its 120s timeout. Now 77s, and arch_cpu.sh re-measures at 57/57 so the shared change cost nothing. The cases are split across two files because a single one was 307 lines and file-length-cap rejected the first commit attempt. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| b4b6908d69 |
Test arch_cpu.sh to 100% and take it off the allowlist (91 -> 90)
57/57 lines, measured through run_all.sh and confirmed a second time at --min 100 so a shortfall would exit non-zero. check_shell_coverage.sh passes with the exemption removed. 28 assertions over 14 cases: no cpufreq at all, every core already tuned, the mixed case where only some cores lag, cpupower absent / unconfigured / already configured, and all three I/O scheduler targets (none for NVMe, mq-deadline for SATA SSD, bfq for spinning disks). The sysfs reads and the two /etc write targets are now behind overrides defaulting to the real paths, so the tests can present hardware this host does not have -- a rotational disk -- without touching anything. One assertion initially passed for the wrong reason: _t_hide keeps $FAKE_BIN on PATH by design, so the harness's own cpupower stub still satisfied has_cmd and the "not installed" branch never ran. Unstubbing before hiding is now explicit, and the case fails if that is lost. This also settles arch_sysctl.sh's diagnosis: it uses this same harness, so the harness is not why it cannot be measured. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| 7c14cfca61 |
Test arch_sysctl.sh and open the optimize_arch_desktop lib family
23 assertions over 8 cases covering both tweak_vm_sysctl and tweak_network_sysctl: the skip path, the write-when-the-drop-in-is- missing path that a naive "nothing to do" check gets wrong, the value-differs path, an unreadable-sysctl path, and the tcp_bbr guard. arch_desktop_harness.sh is the third family harness, supplying the globals and apply_tweak that live in optimize_arch_desktop.sh rather than in any lib. arch_cpu.sh and arch_hardware.sh will reuse it. SYSCTL_DROPIN_DIR defaults to the real /etc/sysctl.d, matching the seam ubuntu_perf_more.sh and arch_perf_fixes.sh already carry; the suite runs un-jailed in ci_mirror.sh and CI, so a bind mount would protect nothing. Both real drop-ins keep their Mar 2 mtimes. _t_run lands in lib_test_core.sh because `out="$(func)"` forks a subshell and kcov never registers a lib whose first execution happens inside one -- measured as 0 lines via $( ) against 54 lines via a direct call. arch_sysctl.sh STAYS on the coverage allowlist: it still reports "kcov instrumented no lines" through the jail, while ubuntu_perf_more.sh measures 32/32 in the same worktree and the same invocation. The tests are real and green; the coverage number is not yet obtainable, and the exemption may only come off once it is. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| fa7b05da74 |
Test arch_perf_probes.sh to 100%: allowlist 93 -> 92
Sixth of the 16 untested fixes/lib libs, and the first with no dead branch to
fix -- the whole job was test-writing. 43 assertions across two files;
93/93 = 100.00% through run_all.sh, twice, the second at --min 100 (exit 0),
then cleared with the real gate confirming and no exemption.
The one genuine gap was check_memory_pressure's high-swap branch (lines
158-159), which fires only when swap is heavily used WHILE RAM is still
available -- a state this host does not happen to be in. The lib reads the
absolute /proc/meminfo, but every read goes through `awk`, so stubbing awk
supplies controlled figures without touching /proc: the stub answers on the
pattern being matched and execs the real awk for anything else. Both
directions are covered, including RAM genuinely exhausted, which must NOT be
flagged because it is not the stutter signature.
Everything else here is branch coverage of probes whose inputs are external
commands: failed systemd units, ACPI error counts, a CPU-hungry Xorg,
governors all-performance vs one-powersave vs unsupported, thermal
throttling excerpts, the TLP/power-profiles-daemon conflict in all three
combinations, and storage health with and without fstrim/smartctl.
lib_test_core.sh gains _t_stub_cat. "Print this fixture file" is by far the
commonest stub shape, and writing it inline forces every call site to embed
a literal ${LIB_TEST_DEV} that must not expand at write time -- SC2016 on
every use. Passing the fixture NAME as data removes the class outright, and
two earlier bluetooth test files are simplified onto it. All 21 test files
and three harnesses now lint clean with no suppression anywhere.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|||
| ffcf99f102 |
Test arch_perf_report.sh to 98.33% and fix its dead journal-size check
Fifth of the 16 untested fixes/lib libs and the first of the arch-performance
group, which gets its own harness here. 38 assertions across two files. The
lib STAYS on the allowlist at 59/60 = 98.33%, measured twice.
The second dead branch this campaign has found, fixed here. Lines 36 and 65
matched on:
([0-9]+\.?[0-9]*)\ (G|M)
a REQUIRED space before the unit. journalctl actually prints "Archived and
active journals take up 305.5M in the file system." with no separator --
verified against the real binary on this machine. So check_journal_size could
never report a large journal and apply_safe_fixes could never vacuum one:
both were unreachable. The space is now optional, and tests cover the spaced
and unspaced forms so neither can rot.
NOTE: this makes a previously-dead DESTRUCTIVE action live. With
--apply-safe-fixes and a journal over a gigabyte, the entry script will now
really run `journalctl --vacuum-size=300M`. That is the flag's documented
purpose and it stays behind the flag, but it has never actually executed
before today.
Line 20, the one uncovered line, is `} >>"$REPORT_FILE"` -- the closing brace
of a redirected group. A minimal probe writes both inner lines to the file
while bash attributes no statement to the brace, and the report assertions
independently prove the group runs, since they read the text it writes. Same
defect (a) class as bt_audio's `} |`.
That form now caps four libs: bt_audio.sh, arch_perf_report.sh, and
arch_perf_probes.sh and nvidia_config.sh which are still untested. So the
continuation_lines() fix is worth landing after all -- as its own commit,
next, so a denominator change can be reverted without taking these tests
with it.
_t_hide and _t_full_path move from bt_harness.sh into lib_test_core.sh: they
are generic PATH plumbing and the arch harness needs them for the
"nvidia-smi is not installed" branch. All twelve existing suites re-run green
after the move.
One trap worth recording: apply_safe_fixes appends to the ACTIONS array, so
it must be called directly rather than inside "$(...)" -- a command
substitution runs it in a subshell where the appends die with it, and the
assertions read an empty array.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|||
| a5b6663c02 |
Test bt_audio.sh to 96.97% and fix its wpctl health check
Fourth of the 16 untested fixes/lib libs, the largest at 214 lines, and the
last of the bluetooth family. 47 assertions across three files. The lib
STAYS on the allowlist: 128/132 = 96.97%, twice with the same line set.
The bug the tests found, fixed here. Line 21 read:
if timeout 3 _run_as_user wpctl status &>/dev/null; then
`timeout` is a binary and `_run_as_user` is a shell function that is never
exported, so this always failed with rc=127 -- "timeout: failed to run
command '_run_as_user'". check_pipewire_health therefore reported PipeWire
hung on EVERY run and restarted a healthy audio stack every time
fix_bluetooth.sh ran. The healthy branch was unreachable, which is why the
test for it could not be written without first noticing. Now
`_run_as_user timeout 3 wpctl status`: same bound, inside the sudo'd command.
Why 96.97% and not 100%. An earlier run of this suite read 99/99 = 100.00%,
and that number was wrong -- it measured the file before pre-commit's
`shfmt -w` reformatted it. shfmt rewrites the `{ ...; } \` + `| bluetoothctl`
one-liners into multi-line blocks ending in `} |`, which raises the
denominator 99 -> 132. Confirmed unavoidable by running shfmt against HEAD's
own copy: it rewrites there too, so reverting the reformat is not stable.
The four uncovered lines (90, 113, 137, 157) are each that `} |` -- the pipe
operator joining a block to bluetoothctl, not a statement. They do execute:
a `{ echo a; echo b; } | consumer` block writes every marker to a file even
though bash's xtrace never reports the left-hand side, because a pipe's LHS
runs in a subshell whose xtrace is not captured. kcov instruments them and
cannot attribute them; defect (a) in docs/kcov-under-report.md, third flavour.
Scope, measured: the continuation-pipe form appears ONLY in bt_adapter.sh
(pre-format) and bt_audio.sh (post-shfmt). None of the remaining twelve
untested libs carries either, so this does not block the campaign and the
detector fix stays off the critical path.
Also worth 90.91% -> 96.97% on its own: the bluetoothctl stubs now drain
stdin before exiting. Real bluetoothctl consumes its whole input; a stub
that read one line and exited closed the pipe early, so the writing block
took SIGPIPE partway through and its later lines never ran.
NOTE for whoever edits this file next: bt_audio.sh is now exactly 250 lines,
at the cap. The next change to it needs a split, not a comment trim.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|||
| 434cb7587e |
Test bt_adapter.sh to 98.55%; line 97 is unreachable by construction
Third of the 16 untested fixes/lib libs, and the first that does NOT come
off the allowlist. 68/69 = 98.55%, up from 45/69. The remaining line is
not a missing test:
PS4='+TRACE:${BASH_SOURCE##*/}:${LINENO} ' bash -x
shows bash attributing the whole multi-line dbus-send assignment in
_services_resolved to line 100, and never emitting line 97 at all. Line 97
is the OPENING line of a backslash-continued statement; kcov instruments
it, bash never attributes a statement to it. That is defect (a) in
docs/kcov-under-report.md -- a wrong denominator -- and no test can move it.
continuation_lines() in shell_coverage_lines.py already excludes such lines,
but only detects them by tracking QUOTE state, so a line ending in an
unquoted backslash slips through. Fixing that is deliberately left out of
this commit: it changes the denominator for every measured lib, and if it
regresses one of the libs now gated with no exemption, reverting a mixed
commit would throw away these tests too.
Scope of the problem, measured rather than guessed -- backslash
continuations are confined to the bluetooth family: bt_audio.sh 10,
bt_adapter.sh 7, bt_pairing.sh 3, pacman_hook_stall_summary.sh 2, and ZERO
in the other 12 remaining libs. So this caps bt_audio.sh next and then stops
mattering.
Also replaces the harness's _t_only_path with _t_hide. _t_only_path dropped
PATH to $FAKE_BIN alone, which hid grep, head and sort as well as the tool
under test -- so the "usbreset is not installed" case died in a cascade of
"command not found" and passed for the wrong reason instead of exercising
the fallback. _t_hide rebuilds PATH from symlinks to every real executable
except the named ones; verified directly that command -v usbreset is then
empty while grep and head still resolve.
bt_report.sh (37/37) and bt_pairing.sh (65/65) re-measured at --min 100,
both exit 0, so neither cleared lib regressed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|||
| 0e9cc0ce95 |
Test bt_pairing.sh to 100%: allowlist 97 -> 96
Second of the 16 untested fixes/lib libs. 65/65 = 100.00% through run_all.sh, twice, the second at --min 100 (exit 0), then cleared from the allowlist and confirmed by the real gate with no exemption. Two findings worth keeping: - udevadm was NOT in the harness's stub list, so `udevadm control --reload-rules` was reaching the REAL binary during the suite. A prepended stub dir cannot hide a tool the host actually has. Caught by asserting on the recorded call rather than by reading the code, which is the argument for asserting on calls at all. - _disable_usb_autosuspend writes /etc/udev/rules.d/50-bluetooth-no- autosuspend.rules. Binding /etc in the jail would have covered it, and was rejected: run_all.sh runs UN-jailed in ci_mirror.sh and in the shell-tests workflow, as its own header says, so a bind mount protects the coverage run and leaves every bare run writing to a real root-owned file. Instead the lib gained a UDEV_RULES_DIR override, defaulting to the real path -- the same env-override seam pacman_hook_stall_harness.sh uses for PACMAN_LOG/CACHE_DIR/PACMAN_LOCK. Proof the suite is safe where it runs un-jailed: after a bare run_all.sh, the real rule file still has its 2026-02-21 mtime (it is a genuine artifact of a production run six months ago) and the probed power/control values are unchanged. The jail cannot give that check. fix_usb_autosuspend globs the real /sys/bus/usb/devices/*/ with no override, so its branches are driven through stubbed lsusb output: ffff:ffff matches nothing on any machine and reaches the not-found arm, while 046d:c077 (control=on) and 046d:0825 (control=auto) are read-only probes, each guarded so the case self-skips with a note if the host state changes rather than silently asserting nothing. Split across two test files to stay under the 250-line cap; run_all.sh globs test_*.sh, so splitting costs nothing. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| 11aea86920 |
Land the token_audit standing-cost checkup (116 tests, 100% branch coverage)
Answers "what is billed on every turn regardless of use" by measurement instead of recall, which is the part earlier audits kept getting wrong. A surface is reported dead only when unused in BOTH windows. One earlier audit's seven-day dead list included six servers that had been used within thirty days, so a single window is not evidence. Shares are weighted with cache-read weight 0.1, so a cached surface is not billed as though it were re-read in full each turn. Verified by running it, not just by tests: the report renders over 44,311 turns, putting rules/ at 4,907 tokens (1.77%), MEMORY.md at 4,312 (1.55%), memories/ at 3,758 (1.35%), skill descriptions at 3,279 (1.18%) and CLAUDE.md at 2,057 (0.74%), with per-MCP 7d/30d call counts behind each verdict. The files were found untracked in the tree from an earlier session; every module is already under the 250-line cap, so nothing needed splitting, and the package was already at 100% branch coverage, so nothing needed new tests. This lands the instrument only -- no surface is unregistered on its say-so. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| 81191f40e6 |
Fix both kcov defects: dwm_config 47.95% -> 98.48%, rpi_nc_install 11.36% -> 96.59%
The instrument, not the tests, was the bottleneck. Coverage is now
(PS4-traced lines | kcov hits) & (kcov line set - continuation lines).
The trace deliberately does NOT supply the denominator. A trace reports only
lines that RAN, so using it for both halves would make every subject 100.00%
and arm a repo-wide gate that checks nothing.
Three measured findings, each of which yields a silently wrong trace:
1. kcov's ptrace and bash's xtrace cannot share a process. Under
SHELLOPTS=xtrace every line kcov recorded as hits="1" comes back hits="0"
-- reproduced A/B on one subject. Hence two passes, two namespaces.
2. SHELLOPTS is readonly inside bash, so `export SHELLOPTS=xtrace` fails and
tracing never turns on; and `set -x` does not reach child processes.
3. Decisive: bash under `unshare --map-root-user` runs PRIVILEGED and
DISCARDS an inherited PS4, falling back to "+ " while still honouring
xtrace. The trace loses its file:line prefix and every subject reads 0%.
Fixed by delivering PS4 and `set -x` through a BASH_ENV file, which also
solves child-process propagation.
A fourth trap, in the report: kcov records filename="dwm_config.sh", a bare
basename. The source path comes from the trace's ${BASH_SOURCE} or a unique
repo search; an ambiguous basename yields no exclusion rather than a guess.
Measured through run_all.sh, nothing down:
dot_resolver_install.sh 39/39 = 100.00% (acceptance, unchanged)
rpi_nc_ca.sh 73/73 = 100.00% (was 72/73 = 98.63%)
dwm_config.sh 65/66 = 98.48% (was 35/73 = 47.95%)
rpi_nc_install.sh 85/88 = 96.59% (was 10/88 = 11.36%)
rpi_nc_ca reaching 100% is the load-bearing check: its missing line 141 was
cross-process contagion, and the trace pass has no kcov in it to contaminate.
dwm_config's remaining line 39 is a `done < <(...)`. Left in the denominator
on purpose -- removing lines inflates coverage, the fail-open direction, so a
second exclusion rule needs per-line justification, not a pattern from one lib.
Both new Python modules hold 100% branch coverage (29 tests).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|||
| 0091608b08 |
Make the gate read jail_args and refuse a suite that dies partway
Two defects found in review, both of which made checks PASS more easily, so neither ever went red. GATE: is_covered() hardcoded its jail flags and never read the jail_args marker that ci_mirror.sh and shell-tests.yml both consume. The features/lib suite needs /usr/local/share and /etc/pki and aborts under set -e without them -- but the coverage number still printed, because kcov had already recorded whatever the dead run reached. The gate would have graded 44 libs on a suite that never finished. It now reads the marker, keeps the historical defaults for suites without one, fails closed on an empty marker, and passes --fail-on-case-error. Negative control: stripping the binds makes the gate exit 1 and restoring them returns exit 0. Before this commit both cases exited 0 -- the gate could not fail on an aborting suite. HASH CACHE: the _t_unstub helper written earlier today for the transcribe harness was needed in features_harness.sh too. Two assertions were vacuous -- test_dwm_config's "verify still reports when dwm is absent", and test_rpi_nc_ca's uname removal, where a cached uname left every later branch running under a faked Darwin. All four `rm -f "$TEST_TMPDIR/bin/` sites in the repo were audited; the other two are sound (one probes with [[ -f ]], the other runs in a subshell). No measured figure moved: dot_resolver_install.sh still 39/39 = 100.00% with real absence enforced, so its off-allowlist clearance holds. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016sxxsDHWgdRhmdmzNMA6XM |
|||
| b91be3099d |
Clear unity_handler.sh: 100%, off the allowlist, 105 -> 104
The first allowlist removal of the campaign this session. unity_handler.sh measures 32/32 = 100.00% through its directory's runner, and check_shell_coverage.sh passes it with the exemption DELETED -- the real gate, not just the measurement. HARNESS BUG found getting there: bash caches executable locations in a hash table, so `command -v foo` keeps succeeding after foo is removed from the stub dir. Every "tool is missing" test was therefore running as a "tool is present" test, asserting the opposite of its own name while looking plausible. Reproduced minimally: after rm, STILL FOUND; after `hash -r`, gone. A _t_unstub helper now removes the stub and clears the table. The two suites committed an hour ago had the same defect and are corrected here -- test_transcribe_pkgmgr.sh goes from 15 to 18 passing assertions, so three of its checks had been vacuous. This is exactly the failure the campaign exists to catch, found in its own test code. `basename` joins the restricted PATH because the SUBJECT calls it; the suite avoids needing `tail` via a parameter expansion instead, since every binary in that PATH is one more thing a stub's shebang can trip over. transcribe_deps.sh (92.31%) and transcribe_pkgmgr.sh (98.00%) re-measured unchanged and stay on the allowlist. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016sxxsDHWgdRhmdmzNMA6XM |
|||
| ac1cf57656 |
Give testsAndMisc-bash/lib a harness; cover transcribe_deps and _pkgmgr
The directory held five allowlisted libs and no tests. It now has a harness, a runner, and suites for two libs: transcribe_deps.sh (the fourth hardest-first target, 48/52 = 92.31%) and transcribe_pkgmgr.sh (49/50 = 98.00%). Both stay on the allowlist. 15 + 18 assertions, 0 failures. No jail is needed here and no jail_args marker is created: every branch turns on `command -v <manager>` and every install goes through sudo, both intercepted by a PATH stub dir. Verified the runner exits 0 when invoked bare, which is how both gates call it. The harness REPLACES PATH rather than prepending. This host really has pacman, and a prepended stub dir cannot HIDE a binary, so every "manager not installed" case would have asserted nothing. `bash` and `env` are in the private coreutils dir for a non-obvious reason: every stub's `#!/usr/bin/env bash` shebang resolves through that restricted PATH, and without them each stub died with "env: 'bash': No such file or directory" while looking exactly like a coverage gap. Assertions are on recorded ARGV rather than on the call happening, because which packages a branch installs is what differs between distros -- pacman gets python-virtualenv where apt gets python3-venv. Three assertions aimed at the libsndfile branch were REMOVED rather than left green-but-vacuous: /usr/lib/libsndfile.so exists on this host, so need_libsndfile is legitimately 0 and lines 19/52/53/55 are unreachable without a jail that hides the library. The limitation is written into the suite instead of asserted around. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016sxxsDHWgdRhmdmzNMA6XM |
|||
| 170f843481 |
Make the jailed lib/tests gate able to fail, without breaking measurement
The jail recorded each case's exit code but reported only timeouts, discarding every other non-zero status. A suite with a failing assertion exited 0 through the push gate -- the same blind spot that let an earlier session ship 2 failing assertions in the nc_php suite. Found by negative control, not by reading: a deliberately failing assertion still exited 0. Propagating that status unconditionally was wrong, and caught before landing: the jail's own usage example passes a `bogus` case that exits non-zero to cover an error path, so a clean 5/5 = 100.00% run started failing. That is the mode the 105-lib campaign runs in. So the behaviour sits behind --fail-on-case-error, default off, passed only by ci_mirror.sh and shell-tests.yml. Verified in all four quadrants: measurement + error case = 0, gate + error case = 1, gate + clean suite = 0, gate + broken assertion = 1. Both gates also pass --min 0: with no --measure the jail was measuring run_all.sh itself against its default --min 100 and failing the push at 54.55%, a coverage verdict neither gate asked for. ci_mirror.sh now prints the jail's output on failure, so a red gate can be diagnosed. The added flag pushed the jail over the 250-line cap, so the in-namespace case runner moved to lib/shell_coverage_case_runner.sh, following the existing lib/shell_coverage_jail_setup.sh split. All four quadrants were re-run after the extraction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016sxxsDHWgdRhmdmzNMA6XM |
|||
| 61ff5eacac |
Cover nc_php.sh (98.82%) and teach the jail to measure foreign platforms
nc_php.sh was the next-hardest library on the allowlist. It targets a Debian Raspberry Pi -- it edits /etc/php/<ver>/apache2/php.ini, drives occ as www-data and installs a crontab -- so none of its write targets exist on this Arch host and the jail had nothing to mirror. - --seed-dir / --seed-file create the paths a foreign-platform subject expects. Verified with a probe: /etc/php/8.2/apache2 exists inside the jail while /etc/php does not exist outside it. - A failed bind is now FATAL, and missing mountpoints are created first. A bind that fails silently is indistinguishable from unreachable code in the report, which is the misdiagnosis this runner exists to prevent. - Mirroring skips unreadable targets (/root as a normal user) rather than aborting under set -e. nc_php.sh: 84/85 = 98.82%, 24 assertions. It STAYS on the allowlist, because 98.82% is below the bar. Line 111 -- the crontab first-run fallback -- hung the suite on both attempts to cover it, so it is left uncovered and documented rather than tried a third time. Two harness hazards found the hard way and written down: a top-level stub function shadows the subject's own function for every later assertion (this silently dropped coverage 83.53% -> 57.65% while the tests still passed), and a stub for a piped-into command must drain stdin or the writer takes SIGPIPE. dot_resolver_install.sh still measures 39/39 = 100.00%. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DbD4nco3EgL1UFoZKmKsLE |
|||
| bca70b67b5 |
Shell coverage: convert the ratchet to a measured 100% bar
The gate was a presence check -- 'a run_all.sh exists beside this lib' -- which an empty suite that asserts nothing satisfies. It now measures. 100% is reachable with no suppressions and no production source changes: run the suite inside a user+mount namespace and the sudo-writes, systemd units and nftables rules execute for real against a throwaway /etc. - meta/scripts/shell_coverage_jail.sh: the runner, promoted out of a session scratchpad. Two fixes it needed on the way: bind targets mirror the real directory tree (an empty bind makes writes fail with ENOENT, which reads as unreachable code), and sudo gets a pass-through stub because real sudo cannot setresuid inside a userns. - It fingerprints git status before and after and fails the run if a write escapes the jail. Verified with a deliberate escapee. - dot_resolver_install.sh: 39/39 = 100.00%, 28 assertions. Picked by scoring every allowlisted lib on root ops, system writes and destructive calls -- it scored highest in the repo. Allowlist 106 -> 105. - jscpd was already over its own threshold at 2.45% and would have rejected every commit staging a .sh file. Excluding vendored .venv/ shell alone gives 1.67%; also excluding lib/tests/ gives 1.47%. The gate is deliberately NOT wired into the hook chain yet. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DbD4nco3EgL1UFoZKmKsLE |
|||
| d42d8ff20d |
Shrink the shell-coverage allowlist 115 -> 106: cover scripts/lib
Gives linux_configuration/scripts/lib a real test suite, which takes all nine of its libraries off meta/shell-coverage-allowlist.txt at once — coverage is per-directory, so this is the highest-ratio first batch. 139 assertions across four suites, all offline: the mtk toolkit is driven through its existing MTK_ROOT_FIXTURE seam, and adb is shimmed onto a narrowed PATH. Nothing reads the wall clock, this host's udev rules, or a real device, because the suite runs in shell-tests.yml and again on every pre-push via ci-mirror — a host-dependent assertion would be a flake that blocks unrelated pushes. The gate only checks that tests/run_all.sh EXISTS, so an empty one would mark all nine libraries covered while testing nothing. Each of the four load-bearing rules was therefore mutation-tested and the suite required to go red: classify_device's && -> ||, discover_partitions' init_boot/boot ordering, mtk_select_device's refusal to guess between two phones, and is_hour_in_range's 10#$ octal guard. That last one initially SURVIVED. Dropping 10#$ makes bash abort with "value too great for base", a non-zero exit indistinguishable from an honest out-of-range result, so the status-only assertion passed either way. It now asserts stderr is empty. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| 1d7271f023 |
Phase 2: enforce the 250-line cap and stop pushing red
Two pre-commit hooks and one pre-push extension, plus the _glyph_art split that had to land first — the cap gate cannot be wired while any file is over it, and the lyricanki note-ascender glyph had taken _glyph_art.py to 253. file-length-cap runs check_file_length.sh --all repo-wide rather than on staged files, so a file that grows past the cap in some other commit is still caught. Proven to block, not assumed: with a deliberate 261-line file staged the hook exits 1 and names it; with the file removed it passes. ci-baseline-green is the user's option (a): refuse to build on a main that is already red. It fails closed on a definitively failing baseline and warns-and- passes on anything it cannot determine — offline, no gh, unauthenticated, no settled runs — each call capped at 5s. Blocking every commit on network reachability buys nothing when (b) is the real per-change guarantee. CI_GREEN_SKIP=1 is the escape for a fix-forward commit. python-tests.yml is excluded on purpose: it is path-filtered, so a shell-only commit never triggers it and its "latest run" would produce false reds. This hook already earned its place. Run against a red main earlier in this session it refused with "Shell tests (failure)", which is how the broken SETUP_SOURCES was found instead of being pushed over. ci_mirror gains the shell-tests workflow's side-effect-free half — every lib/tests/run_all.sh plus the workflow's own test list, the user's option (b). That list is parsed out of shell-tests.yml rather than copied: a hardcoded duplicate drifts the moment someone adds a test, and a mirror that quietly checks less than CI is worse than no mirror because it reports safe. The Arch-container half stays unmirrored — it wants root and an Arch userland. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F3PzGc75gBcdxP3PkHXGcV |
|||
| d72f03ea93 |
setup_midnight_shutdown: split into lib/ — the cap is now zero
1734 -> 109 lines across eight libs plus one payload file. This was the last file over the 250-line cap; check_file_length.sh --all now reports "all checked files are within 250 lines". This file needed a different proof from the others. Its product is scripts generated into /usr/local/bin via heredocs, so "did the functions move verbatim" is not the question that matters — "does it still emit the same bytes" is. Every heredoc body was hashed before the split: 11 of 12 are byte-identical afterwards, and the twelfth (the 224-line config-unlock body) now lives in lib/payloads/shutdown-config-unlock.sh.in at hash 1a5c67cfd0244101, exactly matching what it replaced. No new bodies appeared. The "~39 top-level functions" this file is documented as having are not real. log(), now_epoch(), cmd_add() and friends sit at column 0 because they are inside the generated scripts. The brace-matching extractor keeps each heredoc with its owning generator, so they moved as part of it. That is also why verify_shell_split.sh reports a bare "-log": it cannot tell a heredoc-internal definition from a top-level one. log() is not lost — it is line 17 of the payload, which is where it always ran from. $_MS_PAYLOAD_DIR is defined in the same file as its use, directly above it, following the _NL_PAYLOAD_DIR lesson from earlier in this campaign. --help is byte-identical, shellcheck is clean with no suppression added. `setup` itself is not executed — it installs systemd units, makes the config immutable and arms a shutdown timer. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F3PzGc75gBcdxP3PkHXGcV |