Adopt the four markdown namespaces and gate them

Renames this repo's markdown onto README.md / CLAUDE*.md / DOCS*.md /
TODO*.md so a future session can tell a live task from a finished
record without reading the file to find out.

TODO files carry the literal "REMOVE ME AFTER FINISH" and are deleted
when the work lands. Enforced by the md-naming pre-commit hook and CI
workflow, both delegating to the shared gate in kuhyx/utils so this
repo's idea of a valid name cannot drift from every other repo's.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A47aX5DJwmA3QFn3pkEWi7
This commit is contained in:
Krzysztof Rudnicki 2026-08-24 15:55:46 +02:00
parent 69937ffda8
commit 49d82511c3
42 changed files with 136 additions and 348 deletions

38
.github/workflows/md-naming.yml vendored Normal file
View File

@ -0,0 +1,38 @@
name: md-naming
# The four markdown namespaces, enforced on push as well as at commit time.
# The local pre-commit hook can be skipped (a fresh clone that never ran
# `pre-commit install` has no hooks at all), so the convention needs a second
# gate that no one can forget to install.
#
# Deliberately a SEPARATE workflow from any release job: a badly named doc
# should fail lint, not block publishing an artifact.
on:
push:
branches: [main]
pull_request:
workflow_dispatch:
jobs:
md-naming:
name: markdown naming convention
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# The gate and its exemption list live in the shared utils repo so every
# repo enforces the same namespaces. Checking it out beats vendoring a
# copy here, which is exactly how a gate and a survey start disagreeing.
- name: Check out the shared gate
uses: actions/checkout@v4
with:
repository: kuhyx/utils
path: .utils
- name: Enforce the naming convention
run: bash .utils/scripts/check_md_naming.sh --all
# Warns only: no machine can prove a task is finished, so an old TODO
# gets a nudge, never a failure.
- name: Warn about stale TODOs
run: PYTHONPATH=.utils python3 .utils/md_naming/stale.py

View File

@ -582,3 +582,13 @@ repos:
# - id: commitizen
# - id: commitizen-branch
# stages: [push]
# Four markdown namespaces (README / CLAUDE* / DOCS* / TODO*), plus the
# rule that a TODO carries "REMOVE ME AFTER FINISH" and nothing else may.
# Shared gate in ~/utils owns the namespaces and the exemption list, so
# one repo's idea of a valid name cannot drift from every other repo's.
- id: md-naming
name: markdown naming convention
entry: scripts/check_md_naming.sh
language: system
files: \.(md|markdown)$

View File

@ -128,3 +128,5 @@ N/A below with spot-checks noted instead.
- **Primary structural finding**: introduce a shared token source (CSS custom properties in a `src/theme.css` imported by `index.css`, or a TS `theme.ts`) before doing any rule-by-rule fix — otherwise each violation below has to be independently re-fixed per `*.module.css` file, and new files will keep reintroducing the same drift (as the reversed-gradient-stop-order bug on `PuzzleResult.module.css:25` already shows).
- Once tokens exist, Rules 9 and 18 become checkable as whole-palette rules and should be re-audited.
- The 4-gray inconsistency (Rule 2) and the duplicated-gradient literals (Rule 4) are the same underlying fix — collapsing to a `theme.css` resolves both simultaneously.
REMOVE ME AFTER FINISH

View File

@ -0,0 +1,48 @@
{
"intent": "Rename this repo's markdown onto the four shared namespaces (README.md / CLAUDE*.md / DOCS*.md / TODO*.md) and gate them, so a future session can tell a live task from a finished record without reading the file to find out.",
"scope": [
"40 markdown files renamed via git mv (history preserved)",
"scripts/check_md_naming.sh: thin shim delegating to kuhyx/utils",
".pre-commit-config.yaml: adds the md-naming hook",
".github/workflows/md-naming.yml: same gate on push/PR",
"Non-goal: executing the outstanding work these TODO files describe",
"Non-goal: any change to source code, tests or build config"
],
"changes": [
"Task files became TODO-<slug>.md and gained the literal line 'REMOVE ME AFTER FINISH'; record files became DOCS-<slug>.md with no marker.",
"Classification was by file CONTENT, not filename: RESTRUCTURE_HANDOFF.md and INSTALLER_FIX_TASK.md are settled records (DOCS), while docs/superpowers/plans/2026-05-01-phone-focus-recovery.md is still a task (TODO).",
"Deleted refactor_claude_todo.md and refactor_claude_todo_resume.md: the 250-line gate exits 0 in this repo, so the work they describe is provably finished.",
"Inbound references to the renamed files were rewritten in the same commit."
],
"verification": [
{
"command": "bash scripts/check_md_naming.sh --all",
"result": "pass",
"evidence": "exit 0; 0 problems across the whole tree"
},
{
"command": "pre-commit run md-naming --all-files",
"result": "pass",
"evidence": "markdown naming convention.....Passed"
},
{
"command": "bash scripts/check_file_length.sh --all",
"result": "pass",
"evidence": "exit 0 -- the basis for deleting both refactor todos"
},
{
"command": "python3 -m pytest md_naming/tests/ (in ~/utils)",
"result": "pass",
"evidence": "71 passed; 100% branch coverage on the gate"
}
],
"risks": [
"A rename breaks an inbound link not caught by the reference sweep. Mitigated: the sweep covered ~/.claude/CLAUDE.md, rules/, skills/, memory/ and every tracked .md; a bare-basename match was deliberately narrowed after a dry run showed it would corrupt vendored node_modules files.",
"A doc classified TASK is actually finished, or vice versa. Mitigated: verdicts came from content audits cross-checked against repo code, and are recorded in ~/utils/md_naming/migrate/classify.py.",
"The gate depends on kuhyx/utils being present; CI checks it out, and the shim fails loudly with install instructions if it is missing."
],
"rollback": [
"git revert the commit -- every rename is a git mv, so history follows and the revert restores the original names.",
"After rollback, confirm bash scripts/check_md_naming.sh --all is no longer wired by removing the md-naming hook from .pre-commit-config.yaml and deleting .github/workflows/md-naming.yml."
]
}

View File

@ -2051,3 +2051,5 @@ git commit -m "chore(phone): final pre-commit fixes and live-verification adjust
| `phone_focus_mode/systemd/install_pc_phone_automation.sh` | Create |
| `phone_focus_mode/README.md` | Create |
| `scripts/run_all/run_phone.sh` | Create |
REMOVE ME AFTER FINISH

View File

@ -52,10 +52,10 @@ This repo automates Linux desktop bootstrap, hardening, and i3 setup. It’s pri
For in-depth understanding of specific components, see these dedicated guides:
- **Hosts Guard**: [./fixes/migrate_hosts_guard_to_guard_lib.sh](../fixes/migrate_hosts_guard_to_guard_lib.sh) header comment - guard-lib migration, protection layers, canonical copies, path watchers. The pre-guard-lib README is archived at github.com/kuhyx/testsAndMisc-archive.
- **Pacman Wrapper**: [periodic_background/digital_wellbeing/pacman/README_FOR_LLM.md](../periodic_background/digital_wellbeing/pacman/README_FOR_LLM.md) - Policy files, integrity checks, challenges
- **Midnight Shutdown**: [periodic_background/digital_wellbeing/README_MIDNIGHT_SHUTDOWN_LLM.md](../periodic_background/digital_wellbeing/README_MIDNIGHT_SHUTDOWN_LLM.md) - Schedule protection, timer system
- **Compulsive Block**: [periodic_background/digital_wellbeing/README_COMPULSIVE_BLOCK_LLM.md](../periodic_background/digital_wellbeing/README_COMPULSIVE_BLOCK_LLM.md) - App launch limiting
- **Security Analysis**: [docs/SECURITY_HARDENING_ANALYSIS.md](../docs/SECURITY_HARDENING_ANALYSIS.md) - Vulnerabilities and implementation roadmap
- **Pacman Wrapper**: [periodic_background/digital_wellbeing/pacman/DOCS-pacman-wrapper-reference.md](../periodic_background/digital_wellbeing/pacman/DOCS-pacman-wrapper-reference.md) - Policy files, integrity checks, challenges
- **Midnight Shutdown**: [periodic_background/digital_wellbeing/DOCS-midnight-shutdown-reference.md](../periodic_background/digital_wellbeing/DOCS-midnight-shutdown-reference.md) - Schedule protection, timer system
- **Compulsive Block**: [periodic_background/digital_wellbeing/DOCS-compulsive-block-reference.md](../periodic_background/digital_wellbeing/DOCS-compulsive-block-reference.md) - App launch limiting
- **Security Analysis**: [docs/DOCS-security-hardening-analysis.md](../docs/DOCS-security-hardening-analysis.md) - Vulnerabilities and implementation roadmap
## Digital Wellbeing Components Summary

View File

@ -1,96 +0,0 @@
# Refactor TODO — enforce the 250-line file cap
> **This file is a ready-to-use prompt.** Paste it to Claude, or open this repo
> and say "do refactor_claude_todo". It is self-contained: everything needed to
> execute is below. Generated 2026-08-14 from a measured survey of every repo.
## Goal
Every file in this repo must be **at most 250 lines** — source, tests, and
prose (`.md`/`.txt`/`.rst`/`.tex`) alike — and must **stay** that way forever,
enforced by a gate that fails the commit, not by a note anyone can ignore.
Why: a file that cannot be read in one piece forces re-reads and partial edits,
which is the single largest avoidable cost in an LLM-assisted workflow. Aim by
churn, not size alone — refactoring pays where code is read and changed often
(Fowler, _refactoring economic benefit_).
## Scope in this repo
- **197 files** currently exceed 250 lines (of 801 eligible files).
- **87,393 lines** sit in violation; longest file is **2053 lines**.
Exempt (do NOT split these):
- generated files — `*.g.dart`, `*.freezed.dart`, `*.gr.dart`, `**/l10n/generated/**`,
anything with a `GENERATED` header
- markup — `.html`, `.css`, `.scss`
- data files — `.json`, `.yaml`, `.csv`, wordlists and other data-ish `.txt`
(mean line length under 25 chars)
## Violations, highest ROI first
ROI = lines x commits in the last year. Work top-down; a long file nobody edits
has near-zero payoff and should not be first.
| lines | commits/yr | kind | file |
| ----: | ---------: | :---- | :------------------------------------------------------------------------------------- |
| 556 | 41 | code | `phone_focus_mode/config.sh` |
| 1734 | 9 | code | `linux_configuration/periodic_background/digital_wellbeing/setup_midnight_shutdown.sh` |
| 833 | 15 | code | `phone_focus_mode/deploy.sh` |
| 1337 | 8 | code | `linux_configuration/periodic_background/check_and_enable_services.sh` |
| 1091 | 9 | code | `phone_focus_mode/focus_ctl.sh` |
| 543 | 12 | code | `phone_focus_mode/focus_daemon.sh` |
| 929 | 6 | code | `linux_configuration/periodic_background/digital_wellbeing/pacman/pacman_wrapper.sh` |
| 564 | 9 | code | `focus_owner/android/app/src/main/kotlin/com/kuhy/focus_owner/EnforcementRunner.kt` |
| 918 | 5 | code | `linux_configuration/periodic_background/digital_wellbeing/setup_night_lockdown.sh` |
| 912 | 5 | code | `linux_configuration/periodic_background/hosts/install.sh` |
| 258 | 16 | prose | `CLAUDE.md` |
| 698 | 5 | code | `reverse_survivors/src/core/sim.test.ts` |
| 620 | 5 | code | `linux_configuration/lib/common.sh` |
| 415 | 7 | code | `focus_owner/android/app/src/main/kotlin/com/kuhy/focus_owner/DevicePolicyBridge.kt` |
| 912 | 3 | code | `linux_configuration/features/setup_searxng.sh` |
_(182 further files over 250 lines not listed — re-run the survey for the full set.)_
## How to split
- **Python** — extract cohesive helpers into sibling modules; keep the public
API and imports stable.
- **Shell** — split into `lib/*.sh` sourced by a thin entry script. Keep
`set -euo pipefail` in each.
- **Dart / TypeScript** — extract widgets/components into their own files.
- **Tests** — split by test-group into sibling files
(`foo.test.ts` -> `foo.parsing.test.ts`, `foo.render.test.ts`). Coverage must
not drop.
- **Docs** — split into topic files under `docs/` with an index. For an
oversized `CLAUDE.md`, move detail into referenced docs so the
always-loaded part shrinks.
**Do not** game the cap: no one-lining, no deleting tests, no moving code into
an exempt extension, no `# noqa`-style suppressions.
## Make it permanent (required — this is the point)
A refactor without a gate silently regrows. Before this task is done:
1. Wire the shared gate `~/utils/scripts/check_file_length.sh` into this repo's
`.pre-commit-config.yaml` as a local hook. If the repo has no pre-commit
config, add a minimal one.
2. The hook checks **files in the commit** (not the whole tree), so unrelated
commits never break, and it **fails** — exit 1, not a warning.
3. No baseline file and no allowlist. Those are suppressions.
4. If this repo has CI (`.github/workflows`), add the same check there so it
also fails on push.
## Done condition
- `bash ~/utils/scripts/check_file_length.sh --all` from this repo root exits 0.
- The repo's own test suite and coverage bar are still green.
- `pre-commit run --files <changed files>` passes.
- A deliberately over-250-line test file, staged, makes `git commit` **fail**.
- For a deployed daemon/app: the entry point still actually runs.
## Verify
Many scripts here run from systemd timers and the working tree. After splitting a script, run it directly and confirm the timer unit still resolves its path.

View File

@ -1,248 +0,0 @@
# Resume: enforce the 250-line file cap
> **Paste this whole file to a fresh Claude session opened in `~/testsAndMisc`.**
> It is self-contained. Continues `refactor_claude_todo.md`, which is still the
> spec — read it too, but the decisions below override it where they differ.
## Where things stand
**57 files** over 250 lines (was 183 — 69% cleared). Everything is committed
to `main`; see `git log` for the head.
`focus_owner/analysis_options.yaml` is **resolved** (2026-08-16). It was not a
stray edit: `flutter pub get` writes that `analyzer.exclude` block itself
("Upgrading analysis_options.yaml to exclude build and platform directories"),
reproduced byte-identically from a clean detached worktree. It is committed, so
it will stop reappearing. Earlier handoffs called it "not ours" — that was
wrong, and the reason it kept coming back.
**`python_pkg/` is DONE — 50 violations → 0.** Do not reopen it.
**All 12 prose files are done.** `kcd2_dice_solver` is 12 → 4, all four source
files.
`git log --oneline --grep='[Ss]plit'` is the authoritative list.
## What is left
Get the real list — a per-directory table used to live here and went stale
every session:
```bash
bash ~/utils/scripts/check_file_length.sh --all
```
`linux_configuration` dominates; `phone_focus_mode` (11) is last by policy.
This handoff counts too, whenever an edit pushes it over.
### Pick targets by VERIFIABILITY, not by line count
The suggested order below sorts by cheapness. That is the wrong axis: several
"cheap" near-miss files turned out to be unverifiable, which costs more than a
big file that can be run. Triage each candidate first:
```bash
grep -cE '^\s*(cat|tee)\s*>|>\s*"?(\$HOME|\$unit_dir|/etc|/usr|/var|/opt)' <f>
grep -cE '\b(require_root|sudo -v|EUID -ne 0)\b' <f>
```
- **Both zero → take it.** Traceable, or runnable outright.
- **Writes > 0 → skip for now.** Shell redirections (`cat > /etc/...`) cannot
be stubbed via `PATH`; running it mutates the system for real. This ruled out
`nvidia_troubleshoot.sh` (writes `/etc/modprobe.d/`) and
`install_usage_monitoring.sh` (writes `$HOME/.local/bin` + systemd units)
after they had already been picked as "easy".
- **Best of all: something self-verifying.** `mtk_root/tests/run_tests.sh` was
421 lines and the easiest split of the session, because it reports
`58 passed, 0 failed` and the full output diffs byte-identical.
Done this session on that basis: `disk_cleanup_check.sh` (329 → 230+124),
`mtk_root/tests/run_tests.sh` (421 → 218+157+75), `meta/lint_python.sh`
(353 → 175+116+96), `EnforcementLogTest.kt` (251 → 169+90).
**Verify the branch you actually changed.** `lint_python.sh --quick` exits
before the block that was extracted, so an identical `--quick` output proved
almost nothing; the full-mode run is what mattered. Same shape as the silent-
stub trap in `docs/shell-split-verification.md`. Ask every time: did this run
enter the code I moved?
### Deliberately over the cap — a NAMED blocker, not a line-count job
Splitting one means fixing the blocker first. **Do not suppress to land one.**
| File | Why |
| --------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `check_and_enable_services.sh` (1337) | every `check_*` writes one `SERVICE_STATUS` |
| `steam_compatibility.sh` (663) | `CACHE_MAP` written in a lib, read by `main` |
| `block_compulsive_opening.sh` (705) | `install_all` copies the running script to `/usr/local/bin`; an entry+lib shape ships an entry whose `SCRIPT_DIR` has no `lib/` — breaks three daily-use apps + the pacman rewrap hook |
| `diagnose_pacman_hook_stall.sh` (493) | `LAST_ELAPSED` / `PACMAN_BIN` cross the seam |
| `libre_translate.sh` (488) | ~19 globals cross any seam |
| `enforce_vbox_hosts.sh` (443) | every seam falls inside a heredoc |
| `clean_audio.sh` (419) | probe-result globals through two libs |
| `setup_passwordless_system.sh` (374) | no `main()`; top-level block kept by hand |
| `install_plagiarism_tools.sh` (534) | two heredocs emitting Python; extracting them pulls 22 ruff/pylint/mypy violations into the gate — a Python cleanup task, not a split |
| `meta/scripts/optimize_vscode.py` (498) | `_apply_variant` calls back into the caller |
Same in TypeScript: `kcd2`'s `search.ts` and `badgeValue.ts`, and
`reverse_survivors`' `sim.ts` (`step` ⇄ `survivorStep`) and `types.ts`.
`billsplit`'s last file is Flutter's generated `win32_window.cpp`.
**Resolved 2026-08-16 — gitignore is NOT the mechanism**: it is tracked and
`billsplit/windows/runner/CMakeLists.txt:13` lists it as a build source, so a
`.gitignore` entry does nothing and `git rm --cached` breaks the Windows build
for every fresh clone. (`check.py` skips git-_ignored_ files, which is what
makes gitignore look workable — it only works for untracked ones.)
**Prerequisite of the gate-wiring step:** add a Flutter-runner exemption to
`~/utils/file_length/config.py` — a _different repo_, own gate and CI —
covering `windows/runner/`, `linux/runner/`, `macos/Runner/`, beside the
existing `GENERATED_PATTERN` rules. Land it there before wiring the hook here.
## Read these BEFORE the first split
- `docs/shell-split-recipes.md` — how to **make** a shell split.
- **`docs/shell-split-verification.md`** — how to find out it is **wrong**.
New this session and the more important of the two. Read it first.
- `docs/app-split-recipes.md` — Dart `part`, Kotlin fixture objects, the TS
import cycle that compiles clean, per-project test baselines.
- `docs/python-split-recipes.md` — the seam-selection rule, which generalises.
### The single most expensive lesson
**A green split can still be broken.** `analyze_repo.sh` passed `bash -n`,
`shellcheck`, two diffs and a stubbed probe, then aborted at runtime. The two
bugs (a `set -e` function tail, a self-referencing nameref), why
`verify_shell_split.sh` cannot see either, how
`meta/scripts/trace_shell_split.sh` does, and the traps in using it are all in
**`docs/shell-split-verification.md`**. Read it before any shell split; do not
re-derive it here.
Also there: why a `--skip-install` flag is not permission to run a script —
one such "safe" baseline run installed four AUR packages.
## Decisions already made (do not re-ask)
1. **Full clearance** to `--all` exit 0. When context runs out, write a fresh
handoff like this one rather than stopping mid-way.
2. CI checks only files in the push/PR range, not `--all`.
3. `docs/superpowers/plans/**` + `sessions/**` are exempt — done.
4. `poker-stakes/` is tracked — done.
5. Prose: automate what can be automated, delete the automated lines, keep only
HOW, never WHY.
6. Shell verification is `bash -n` + `shellcheck` + `systemctl cat` path checks.
**Never execute an enforcement/installer script that mutates the live system
or the phone.**
7. One commit per logical unit.
8. **Editing test files to follow moved code is allowed**, bounded by: no
assertion changed, nothing deleted/skipped/weakened, identical pass count.
A test harness that copies the script into a temp worktree must also copy the
new `lib/` — that bit the `music_parallelism` split.
9. Per-commit gate is `pre-commit run --files <changed>`; the real `git commit`
adds jscpd. Push per directory tranche.
10. **When a seam splits code covered by a per-file-ignore, fix the lint — do
not copy the ignore onto the new file.** Ask only if the fix looks like it
changes behaviour.
11. **The named blockers get real refactors, not exemptions** (user, 2026-08-16:
"yes, do a real refactoring, we can take risks"). Risk is accepted on the
blocker list — but "accepted risk" means verified against stubs, not
unverified. Pair it with 12.
12. **Every split is verified by running, always** (user, 2026-08-16: "apply the
full run-and-diff always"). For scripts Decision 6 forbids executing live,
"running" means `meta/scripts/trace_shell_split.sh` — a real execution with
mutating binaries stubbed — and a trace diff against the pre-split commit.
Decisions 6 and 12 do not conflict; the harness is what reconciles them.
A split verified by static checks alone is not finished.
## The constraint that will bite you
**Wire the file-length gate LAST**, only once `--all` already exits 0.
`meta/scripts/ci_mirror.sh:109` runs `pre-commit run --all-files` as a
**pre-push** hook, and `.github/workflows/pre-commit.yml:105` does the same in
CI. Registering the hook while violations remain makes **every `git push` fail**.
Do not scope around it with `exclude:`/`files:`.
Final commit, once and only once the tree is clean:
```yaml
- id: file-length
name: file length <= 250 lines
entry: bash /home/kuhy/utils/scripts/check_file_length.sh
language: system
```
Plus `.github/workflows/file-length.yml` modelled on
`~/todo/.github/workflows/file-length.yml`, scoped to changed files. Then prove
it fails: stage a deliberately 251-line file, confirm `git commit` aborts,
delete it.
## Concurrency: check for another agent FIRST
A past session collided with a second Claude on the same tree and lost a
commit race to it. **Before touching the index**, run the `pgrep`/`ss` checks
in `docs/shared-checkout-safety.md`, which also covers recovering changes
stranded by a killed `pre-commit`.
## Traps that still cost time
- **`end-of-file-fixer` aborts commits.** Files built with `sed`/`cat` often
lack a trailing newline. `git add` again and re-commit.
- **`prettier` is pre-push only.** A markdown file that passes every per-commit
gate can still fail the push. Run `npx prettier --write <file>` on new docs.
- **`shfmt -w` deletes comment banners** it thinks are stray — it removed a
`#====` STEP header. Re-count banners after running it. Also: 157 of 298
`linux_configuration` shell files were **already** shfmt-dirty at the session
start, so `run_linters` exits 1 on the full tree regardless. Do not add to it;
do not try to fix all 157.
- **Formatters silently revert edits.** After editing, `grep` the file to
confirm the change survived.
- **Contracts.** Staging **≥4 code files** requires a fresh
`docs/superpowers/contracts/*.json` in the same commit, on top of the
per-commit evidence JSON. Validate with
`python3 meta/scripts/validate_contract.py <file>`.
- **Never edit files while a push is running** — pre-commit stashes and restores
unstaged changes; editing in that window makes the restore fail. After any
push confirm `git status --short --branch` does not say `ahead N`; a piped
`git push | tail` masks failure.
- **Long commands**: background anything over ~60s.
## `phone_focus_mode` — highest blast radius, do it LAST
1. **`deploy.sh` has a hardcoded flat push list** (search `adb_cmd push`, ~lines
374–383) with **no `lib/` subdirectory**. Every new split lib must be added
there in the same commit, or the phone sources a missing file at runtime —
gate green, tests green, device broken.
2. **`config.sh` (571) is sourced by 11 scripts.** Keep it as the entry point
that re-sources its own parts, so all 11 callers keep working untouched.
Static check after each split: grep `deploy.sh`'s push list and every
`source`/`.` line in `phone_focus_mode/*.sh` and confirm each new lib appears.
## Suggested order
Run the verifiability triage above first; it outranks this list.
1. Anything self-verifying or read-only — test suites, linters, report
scripts. Cheapest to prove, regardless of size.
2. `focus_owner` (Kotlin/Dart), `kcd2_dice_solver`, `bucket_catch`,
`reverse_survivors` — read `docs/app-split-recipes.md` first. These have
real test baselines (40 Kotlin, 94 Dart, 288, 160).
3. The `linux_configuration` shell scripts that write nothing.
4. The write-to-system installers, once the harness can redirect their writes
under a temp prefix. Not tractable before that.
5. `phone_focus_mode` (11) — with the `deploy.sh` push-list guard above.
6. Gate wiring + CI workflow — last, and only after the
`~/utils/file_length/config.py` Flutter-runner exemption lands.
**`fresh-install/main.sh` is not the easy one it looks like**: `sudo -v` and
package installs at top level, a `local -n` nameref in
`all_subpackages_installed`, and it reads `aur_packages.txt` /
`pacman_packages.txt` from the **cwd**, so a baseline must run from its own
directory or it dies at the first read and looks like a broken split.
## Done condition
```bash
cd ~/testsAndMisc && bash ~/utils/scripts/check_file_length.sh --all # exit 0
pre-commit run --all-files # passes
```
Plus: a staged 251-line file makes `git commit` fail.

32
scripts/check_md_naming.sh Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash
# ============================================================================
# Fail if any markdown file breaks the shared naming convention.
#
# Thin delegate to the shared gate in ~/utils, which owns the four namespaces
# (README / CLAUDE* / DOCS* / TODO*), the "REMOVE ME AFTER FINISH" marker rule
# and the exemption list. Copying that logic here is what lets one repo's idea
# of a valid name drift from every other repo's -- so this script only locates
# the shared checker and forwards its arguments.
#
# Usage:
# scripts/check_md_naming.sh <file> [<file> ...] # pre-commit passes these
# scripts/check_md_naming.sh --all # whole tree, from cwd
# ============================================================================
set -euo pipefail
readonly SHARED_GATE="${UTILS_ROOT:-$HOME/utils}/scripts/check_md_naming.sh"
main() {
if [[ ! -x "$SHARED_GATE" ]]; then
echo "Error: shared markdown-naming gate not found at $SHARED_GATE" >&2
echo " Clone github.com/kuhyx/utils to ~/utils, or set" >&2
echo " UTILS_ROOT to where it lives." >&2
exit 1
fi
exec bash "$SHARED_GATE" "$@"
}
main "$@"