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
This commit is contained in:
@@ -16,7 +16,7 @@ CRITICAL: real guidance lives in DOC/. When you encounter a reference below, use
|
||||
|
||||
- **Tool model:** `bin/pos-<category>-<command>`, or **category-less** `bin/pos-<cmd>` 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: <cat> <cmd> — <desc>` 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 <bin> || 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/<tool>.env` (chmod 600, env-var precedence). Mask tokens in `config` output.
|
||||
|
||||
@@ -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/<tool>.env` (600)
|
||||
6. Update docs: `DOC/POS.md` (section table + detail — hand-written); `DOC/HOWTO.md` index row + a section in `DOC/howto/<category>.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 <full command> && bin/pos <category> --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 <full command> && bin/pos <category> --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
|
||||
|
||||
|
||||
+53
-1
@@ -184,9 +184,10 @@ bin/pos help <full command> # confirm dispatch works
|
||||
bin/pos <category> --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/<category>/<name>.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: <cat> <cmd> — <desc>`); 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-<entry>` 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_<name>()` 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
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user