From 23ec322ef11f49fce36ba3bf025fbfb923927485 Mon Sep 17 00:00:00 2001 From: he Date: Fri, 14 Aug 2026 15:17:02 -0400 Subject: [PATCH] docs: document the convention lint gate (make lint) in AGENTS/DEV/AGENT_Context - DEV.md: new 'Convention Lint Gate' section enumerating all check classes (shebang/strict-mode, exec bits, POS headers, -h|--help after deps guards, INTERACTIVE_CMDS, POS.md coverage, plugins/apps/units/wrappers/secrets/env seams) + 'refine never weaken' rule; test step and definition of done now include make lint (0 FAIL / 0 WARN) - AGENTS.md: make gen -> make check -> make lint is the definition of done; both gates described, lint not in the pre-commit hook - AGENT_Context_Project.md: How-to-modify step 7 updated - MAINTENANCE.md: next-session brief uses make lint; gate commit marked DONE --- AGENTS.md | 2 +- DOC/AGENT_Context_Project.md | 2 +- DOC/DEV.md | 54 +++++++++++++++++++++++++++++++++++- MAINTENANCE.md | 4 +-- 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 8c05680..76ae871 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,7 +16,7 @@ CRITICAL: real guidance lives in DOC/. When you encounter a reference below, use - **Tool model:** `bin/pos--`, or **category-less** `bin/pos-` for dispatcher/dev-level commands that fit no category (`pos-config`, `pos-tree`) — they dispatch like any tool and show with an empty category in the generated tables. `bin/pos` dispatches by longest-prefix arg matching. New tools are auto-discovered but must be executable (`100755`) and carry a `# POS: ` header right after the shebang; `make gen` only uses the text after the first `— ` (the leading words are convention-only), so keep the one-line description concise. `# POS_FLAGS:` / `# POS_SUBCMDS:` / `# POS_CONFIG:` headers feed tab-completion and the `pos config` scope registry. A missing `# POS:` header hard-fails `make gen`. Legacy `bin/wr-*`, `mp3`, `mp4`, `vbox`, `ssh-load-all` are thin forwarders to `pos` — keep them that way. - **Categories:** `ai`, `communication`, `docker`, `entertainment`, `media`, `network`, `share` (usb, nfs, smb), `ssh`, `system`, plus category-less `config`/`tree`. `pos tree` (bin/pos-tree) is the authoritative structure — it derives the hierarchy from `bin/pos-*` filenames + `# POS:`/`# POS_SUBCMDS:` headers. -- **Generated code:** blocks between `GEN:START`/`GEN:END` markers in `DOC/AGENT_Context_Project.md` (tree, dispatch, selfcontained, filetable, docmap) and `completions/pos.bash` (flags, subcmds, config scopes) are `make gen` output — never hand-edit them. After touching `bin/pos-*`, run `make gen` then `make check` (bash -n + exec-bit check + doc-sync gate + dispatch smoke; definition of done). Hand-maintained, not gen-checked: `DOC/POS.md`, the line-count rows above the filetable marker in `DOC/AGENT_Context_Project.md` (the non-`pos-*` files — `install.sh`, `preinstall.sh`, `postinstall.sh`, `lib/*`, `features/*`; bump a row's count only when that file's length changes), `bin/pos` usage() EXAMPLES, root README. There is no CI — `make check` (`scripts/check-sync.sh`) is the only gate. +- **Generated code:** blocks between `GEN:START`/`GEN:END` markers in `DOC/AGENT_Context_Project.md` (tree, dispatch, selfcontained, filetable, docmap) and `completions/pos.bash` (flags, subcmds, config scopes) are `make gen` output — never hand-edit them. After touching `bin/pos-*`, run `make gen`, then `make check`, then `make lint` (definition of done: check green + lint ends `0 FAIL, 0 WARN`). `make check` (`scripts/check-sync.sh`) is the self-consistency gate — bash -n + exec-bit check + doc-sync + dispatch smoke; `make lint` (`scripts/lint-conventions.sh`) is the convention gate — it enforces every rule in this file (shebang/strict-mode, exec bits, `# POS:` headers, `-h|--help` present and after deps guards, stdin-readers in `INTERACTIVE_CMDS`, POS.md coverage, plugin/app/unit/wrapper/secrets/env-seam classes — see `DOC/DEV.md → Convention Lint Gate`). Hand-maintained, not gen-checked: `DOC/POS.md`, the line-count rows above the filetable marker in `DOC/AGENT_Context_Project.md` (the non-`pos-*` files — `install.sh`, `preinstall.sh`, `postinstall.sh`, `lib/*`, `features/*`; bump a row's count only when that file's length changes), `bin/pos` usage() EXAMPLES, root README. There is no CI — `make check` + `make lint` are the only gates (lint isn't in the pre-commit hook; run it explicitly). - **Stdin gotcha:** any tool that reads stdin must be added to `INTERACTIVE_CMDS` in `bin/pos` — otherwise the logging `tee` pipe hangs on (or swallows) the prompt. - **Deps:** apt packages → `PACKAGES` array in `preinstall.sh`; non-apt/manual installers (e.g. `usbsrv`) → `command -v || err "…"` guard inside the tool, never in PACKAGES. - **Secrets:** never commit keys/tokens. `config/authorized_keys` and `config/rclone.conf` are gitignored; runtime tool config is `~/.config/linux_post_install/.env` (chmod 600, env-var precedence). Mask tokens in `config` output. diff --git a/DOC/AGENT_Context_Project.md b/DOC/AGENT_Context_Project.md index 2070b59..b4e067b 100644 --- a/DOC/AGENT_Context_Project.md +++ b/DOC/AGENT_Context_Project.md @@ -530,7 +530,7 @@ System-wide flag store at `/usr/local/share/linux_post_install/flags/`: 4. Add system deps to `PACKAGES` array in `preinstall.sh` (if needed); non-apt/manual installers → `command -v` guard in the tool instead 5. Add config logic to `postinstall.sh` (if needed, with `.gitignore` for secrets); runtime tool config → `~/.config/linux_post_install/.env` (600) 6. Update docs: `DOC/POS.md` (section table + detail — hand-written); `DOC/HOWTO.md` index row + a section in `DOC/howto/.md` (recipes/troubleshooting); `DOC/AGENT_Context_Project.md` generated sections (bin tree, dispatch table, self-contained list, line-count table) and completion flags update via `make gen` — never hand-edit between `GEN:START`/`GEN:END` markers, but hand-add a row to the "Common Tasks for Agents" table; `AGENTS.md` Quick facts if a structural fact changed; root `README.md` only if the category list changes; move the task to `AGENT_TODO.md` Done (dated) in the same commit -7. Test: `make gen && make check` — `make check` (bash -n + doc/code sync + smoke) is the definition of done; also `bin/pos help && bin/pos --help`. For tools needing root/systemd/absent deps, behaviour-test via env-override paths + stub PATH (see DEV.md "Testing tools that need root / systemd / missing deps") +7. Test: `make gen && make check && make lint` — `make check` (bash -n + doc/code sync + smoke) and `make lint` (0 FAIL / 0 WARN, `scripts/lint-conventions.sh`) together are the definition of done; also `bin/pos help && bin/pos --help`. For tools needing root/systemd/absent deps, behaviour-test via env-override paths + stub PATH (see DEV.md "Testing tools that need root / systemd / missing deps") ### Testing diff --git a/DOC/DEV.md b/DOC/DEV.md index 1142858..9c3b726 100644 --- a/DOC/DEV.md +++ b/DOC/DEV.md @@ -184,9 +184,10 @@ bin/pos help # confirm dispatch works bin/pos --help # confirm category listing includes the new tool (first tool in a new category) make gen # regenerate doc tables + completion flags make check # full self-consistency gate (syntax, exec bits, doc/code sync, smoke) +make lint # convention gate (scripts/lint-conventions.sh) — must end 0 FAIL, 0 WARN ``` -`make check` is the definition of done — the same check runs as a pre-commit hook once you've run `make hook`. +`make check` + `make lint` (0 FAIL / 0 WARN) are the definition of done. `make check` is also run as a pre-commit hook once you've run `make hook`; `make lint` is not part of the hook — run it yourself. ### Testing tools that need root / systemd / missing deps @@ -304,6 +305,57 @@ Place it in `apps//.sh`. It auto-appears in the picker — no re --- +## Convention Lint Gate + +`scripts/lint-conventions.sh` is the automated convention gate — it encodes the +rules in this document so drift is caught by the machine, not the next audit. +Run it with `make lint` (or `./scripts/lint-conventions.sh`). FAIL = definite +violation (fix it before committing), WARN = manual review needed. Exit code is +non-zero when any FAIL exists. + +Check classes (all heuristic-based; heredocs, `${...}` brace-counting, `while` +loop stdin, `/dev/tty` reads and `command -v` fallbacks are excluded): + +- **Shebang / strict mode** (FAIL) — every shell file (`bin/*`, `install.sh`, + `preinstall.sh`, `postinstall.sh`, `features/*`, `apps/*`, `templates/*`, + `scripts/*`) starts with `#!/usr/bin/env bash` and has `set -euo pipefail` + (libs are sourced, so they're exempt). +- **Exec bits** (FAIL) — `bin/pos-*` and `entertainment/*.sh` committed as + `100755` (`chmod +x`). +- **`# POS:` header** (FAIL) — every `bin/pos-*` carries it with the em-dash + separator (`# POS: `); a header past line ~6 is a WARN. +- **`-h|--help`** (FAIL) — every `bin/pos-*` handles it via `case`. +- **Deps guards before help** (FAIL) — the first real guard (`command -v X … + || err`, `if ! command -v X …`, multi-line `\` continuation) must sit before + the `-h|--help` dispatch, so help errors on a box missing the dependency. + Graceful-degradation probes (`if command -v X; then …`) are not guards. +- **Top-level `local`** (WARN) — `local` at brace-depth 0 outside a function is + invalid bash. +- **stdin ⇄ `INTERACTIVE_CMDS`** (FAIL) — a tool that reads stdin must be in + `INTERACTIVE_CMDS` in `bin/pos` (else the logging tee swallows/hangs the + prompt); every entry must also have a matching `bin/pos-` tool. +- **DOC/POS.md coverage** (WARN) — each `bin/pos-*` referenced in `DOC/POS.md`. +- **Entertainment plugins** (FAIL) — must carry `# POS_PLUGIN:` and must NOT + source `lib/common.sh` (stdout is the message). +- **Apps** (FAIL) — each `apps/*` script has `uninstall_()` and an + `uninstall` dispatch case. +- **Systemd units** (WARN) — `TimeoutStopSec=` and `[Install] WantedBy=`. +- **Legacy wrappers** (FAIL/WARN) — `bin/wr-*`, `mp3`, `mp4`, `vbox`, + `ssh-load-all` must forward to `pos` (FAIL if not); >12 lines or a `case` + statement is a WARN (thin forwarder only). +- **Secrets** (WARN) — literal `…TOKEN=/…SECRET=/…KEY=…` assignments are + flagged for manual review (env guards, config reads and runtime generation + are excluded). +- **Env seams** (WARN) — writes to `/etc/`, `$HOME`, `/usr/local` are flagged + unless guarded (`command -v` or `|| echo`), i.e. the write needs a + `VAR="${VAR:-path}"` test seam. + +If a rule is genuinely wrong for a new case (as happened with +graceful-degradation probes in system-health), refine the heuristic — never +weaken it — and note the change in `MAINTENANCE.md`'s lint section. + +--- + ## Best Practices ### Alerting diff --git a/MAINTENANCE.md b/MAINTENANCE.md index 8d57b73..1f1c3f4 100644 --- a/MAINTENANCE.md +++ b/MAINTENANCE.md @@ -297,8 +297,8 @@ Fix (2026-08-14): the tools were documented by command name but not by filename 1. Fix P0 → P1 → P2 in order (HIGH first: M-002..M-006, then M-007, then the rest). 2. M-006 needs a product decision first: restore `--send`/`--markdown` (notify path) or strip the docs. -3. Re-run `make gen && make check && scripts/lint-conventions.sh` + smoke each changed tool. -4. Commit `scripts/lint-conventions.sh` + add the `make lint` target (this was on the deliverable list but is not yet wired into the Makefile). +3. Re-run `make gen && make check && make lint` + smoke each changed tool. +4. ✅ DONE — `scripts/lint-conventions.sh` committed and `make lint` wired into the Makefile (5ef38dc). 5. Move audit tasks to AGENT_TODO Done (dated) on completion. ## Checked & clean