Files
Your Name e969234ca5
gates / consistency-and-conventions (push) Successful in 1m28s
feat: command registry, alias wrapper scripts, config-ui readability
- lib/registry.sh: shared query API over POS_* headers (reg_scan, reg_list,
  reg_lookup, reg_tools_in, reg_each, reg_config_scopes/keys). Replaces
  per-consumer sed/grep header parsing.

- bin/pos-tree + bin/pos _pos_category_help(): migrated to registry API.
  Category help now shows [deps: ...] annotations. Tree output preserved.

- New optional headers # POS_DEPS: and # POS_EXAMPLES: in tool metadata.
  Added to pos-network-download (aria2c jq curl), pos-media-sync (lsblk jq),
  pos-system-backup (tar), pos-docker-ps (docker) as initial adopters.

- scripts/gen-docs.sh: extended tools array with deps/examples fields;
  conditional column rendering in gen_dispatch; deps annotation in gen_tree.
  Fixed URL-unsafe // joiner (→ middle dot ·) and \x1f caption delimiter
  collision in config-ui.

- bin/pos-ai-alias: rewrote activation from bash aliases (source-time-frozen)
  to executable wrapper scripts at ~/.local/bin. Staleness eliminated:
  edits apply on next invocation with no shell reload. _alias_sync()
  reconciliation on every subcommand, marker-guarded lifecycle, collision
  refusal, legacy .sh retirement. Fixed dup-table bug (option 4 no-op).

- lib/config-ui.sh: @caption/@[KEY=alt] conditional captions, *providers=<tag>
  tagged wildcards, uniform typography tier (bold/cyan/dim), honest prompt.
  Active provider keys bold, inactive dimmed with reason. Backward-compatible.

- bin/pos-system-uninstall: marker-scan for wrapper script cleanup.

- Docs synced: AGENTS.md (new headers + registry), DOC/SCRIPTS.md (registry
  section + lib list), DOC/POS.md (alias wrapper activation), MAINTENANCE.md
  (M-024). Lint fixed: pos-ai-alias registered in INTERACTIVE_CMDS.

Gates: make gen && make check && make lint = 0 FAIL, 0 WARN
2026-08-27 02:30:27 -04:00

324 lines
28 KiB
Markdown

# MAINTENANCE — Convention Audit
> Working report for the post-3-week-convention-drift audit. **Created first, updated
> incrementally as findings are confirmed** — never let findings live only in agent
> context. Keep the structure below; append findings as they land.
>
> Do **not** commit this file — it is a working artifact for the fix session.
> Definition of done for the fix session: every P0/P1 resolved (or explicitly
> marked won't-fix), `make gen && make check` green, `scripts/lint-conventions.sh`
> green, smoke tests pass.
## Phase 0 — authority & conflict resolution
Order of authority when docs disagree:
1. **Templates** (`templates/pos-tool.sh`, `feature.sh`, `app.sh`) — the codified *current* convention for new files. If an old tool deviates from its template, that's drift (unless the tool is intentionally category-less/nested).
2. **DEV.md** — full convention detail, checklists, best practices (env seams, systemd units, alerting, managed blocks).
3. **AGENTS.md Quick facts** — operational facts (make gen/check gate, header format, INTERACTIVE_CMDS gotcha, deps-guard-before-help). If it conflicts with DEV.md, DEV.md wins on *detail*, AGENTS.md wins on *process*.
4. **Code (`# POS:` headers, runtime behavior)** — ground truth for what a tool *does* and for all `GEN:`-derived docs (make gen regenerates from code).
5. **POS.md / HOWTO / README / SCRIPTS / SYSTEMD / APPS** — hand-written references; drift against code = doc bug (fix the doc) unless the doc describes a feature the code never shipped (phantom feature — fix the doc too).
6. **AGENT_Context_Project.md** — generated blocks follow code; hand-maintained rows (§14 Common Tasks, non-`pos-*` line-count rows, "How to modify" table) must match reality.
**Classification rule:** runtime behavior = what the tool *does*; conventions = what it *should* do. Findings are classified bug (behavior wrong), convention-violation (behavior right but against current convention), doc-drift (reference wrong), standardization (two tools same thing differently).
## Baseline
- `make gen && make check`: green (doc-sync, exec bits, syntax, dispatch smoke all pass).
- `scripts/lint-conventions.sh` (new, this audit): automated convention gate — see results below.
- AGENT_TODO: `Now` empty (Tier 1 shipped). Audit itself is the current work.
- Scope order (user-confirmed): bin/ + lib/ first, then install scripts + systemd, then features/entertainment/apps, then docs. All areas audited; this is report priority.
- Pickiness: everything flagged, tagged severity + confidence. P3 = intentional/legacy no-action list.
- Deliverables: `MAINTENANCE.md` (uncommitted) + `scripts/lint-conventions.sh` (to be committed as the reusable gate; `make lint` target still needs wiring in the Makefile).
## Checklist (convention matrix — from AGENTS.md / AGENT_Context §11/§12 / DEV.md)
- [ ] Shebang `#!/usr/bin/env bash` + `set -euo pipefail` (libs exempt — sourced)
- [ ] `# POS:` header right after shebang, ` — ` em-dash format; `# POS_FLAGS:` / `# POS_SUBCMDS:` / `# POS_CONFIG:` as needed
- [ ] Dep-guards (`command -v … || err`) **before** `-h|--help`
- [ ] stdin readers in `INTERACTIVE_CMDS` (bin/pos); no stale entries
- [ ] No `local` outside functions
- [ ] `run`/`spawn` respect `$DRY_RUN`; writes idempotent
- [ ] common.sh sourced via `$(dirname "$0")/../lib/…` fallback chain (standalone from /usr/local/bin)
- [ ] Entertainment plugins: no common.sh, `# POS_PLUGIN:`/`# POS_KEYS:`
- [ ] Apps: `uninstall_<name>()` + `uninstall` dispatch
- [ ] systemd units: `TimeoutStopSec=5s`, `[Install]`, `KillMode=` on daemons
- [ ] Env seams: every system path write has `VAR="${VAR:-path}"` guard
- [ ] No hardcoded secrets; chmod 600 on creds/tokens
- [ ] Docs: POS.md / README / HOWTO / AGENT_Context hand-maintained spots match behavior
## Lint results (`scripts/lint-conventions.sh`)
_Automated gate — FAIL = definite violation, WARN = manual review needed. All 18 FAIL/WARN classes below are **verified real** against source (heuristics were iterated until zero false positives; heredocs, `${...}` brace-counting, `while`-loop stdin, `/dev/tty` reads, `command -v` fallbacks and env-guard secret patterns are all excluded)._
### FAIL — 12 (maps to tickets M-001, M-003..M-015) — ALL RESOLVED in the fix session (see ticket statuses)
- `features/autostart.sh` — missing `set -euo pipefail` (→ M-001)
- `bin/pos-system-firewall` — missing `-h|--help` handling (→ M-015)
- `bin/pos-docker-compose` — reads stdin (read `-rp` :204, `confirm` :210) not in `INTERACTIVE_CMDS` (→ M-002)
- `bin/pos-docker-vbox` — reads stdin (`confirm` :105) not in `INTERACTIVE_CMDS` (→ M-003)
- `bin/pos-network-hotspot` — reads stdin (`read -rp` :53) not in `INTERACTIVE_CMDS` (→ M-004)
- `bin/pos-docker-health` (:21/:24), `bin/pos-docker-ps` (:17/:20), `bin/pos-media-mp3` (:38/:58), `bin/pos-media-mp4` (:43/:71), `bin/pos-network-scan` (:28/:66), `bin/pos-share-usb-server` (:191/:199), `bin/pos-system-health` (:39/:119) — `-h|--help` dispatched before deps guards (→ M-008..M-014)
### WARN — 6 (manual review done, all real docs-coverage gaps → M-023) — ALL RESOLVED
- `bin/pos-config`, `bin/pos-tree`, `bin/pos-entertainment-config`, `-enable`, `-disable`, `-status` — not referenced anywhere in DOC/POS.md
_Fix-session note: lint now reports 0 FAIL / 0 WARN. Two precision fixes were made to the lint itself (documented, not weakenings): `first_guard_line()` only treats real deps guards (`command -v … ||`, `if ! command -v`, `command -v … \`) as guards so graceful-degradation probes (system-health) don't trip the guard-before-help rule; `first_line()` skips comment lines so a comment containing `-h|--help` isn't mistaken for the dispatch._
_Heuristics that passed clean (reviewed, no findings): no secrets committed (all `TOKEN/SECRET/RPC_SECRET` assignments are env guards / config reads / runtime generation), all libs' system writes carry `command -v` fallbacks, legacy wrappers are thin, no plugin sources common.sh, systemd units carry `TimeoutStopSec=`/`WantedBy=`._
## Findings
Findings are tickets. Every ticket gets a unique `M-###` ID; the fix session works
through them in order (P0/HIGH first). Keep this exact field set:
```
### M-000
Status: OPEN
Severity: HIGH | MEDIUM | LOW
Category: bug | convention | dead-code | docs | UX | standardization | security
Files: <path:line …>
Evidence: <what was observed, quote the actual code/output>
Expected: <what the convention/runtime says should happen>
Recommended fix: <one-line, actionable>
Verification: <how the fix session proves it fixed>
```
### P0 — bugs / security
### M-002
Status: VERIFIED
Severity: HIGH
Category: bug
Files: bin/pos-docker-compose:204,210; bin/pos:259
Evidence: `pos docker compose config`/startup runs `read -rp "Enter your Tailscale auth key…"` (:204) and `confirm "Edit .env before starting?"` (:210) — both read stdin. `bin/pos` logging tee would swallow/hang these prompts because `docker-compose` is not in `INTERACTIVE_CMDS` (line 259). Same class of bug as the just-fixed smb-client prompt swallow.
Expected: every tool that reads stdin is in `INTERACTIVE_CMDS`.
Recommended fix: add `docker-compose` to `INTERACTIVE_CMDS`.
Verification: `pos docker compose config` via `pos` (logged) still prompts.
Fix (2026-08-14): added `docker-compose` to `INTERACTIVE_CMDS` in bin/pos:259. Verified: lint stdin-reader FAIL cleared for all three tools (M-002..M-004).
### M-003
Status: VERIFIED
Severity: HIGH
Category: bug
Files: bin/pos-docker-vbox:105; bin/pos:259
Evidence: `pos docker vbox start` calls `confirm "Enter now?"` (stdin read via lib/common.sh `confirm()``read -rp`) at line 105. Not in `INTERACTIVE_CMDS` → prompt swallow/hang under the logging pipe.
Expected: every stdin reader in `INTERACTIVE_CMDS`.
Recommended fix: add `docker-vbox` to `INTERACTIVE_CMDS`.
Verification: `pos docker vbox start` prompts correctly through the dispatcher.
Fix (2026-08-14): added `docker-vbox` to `INTERACTIVE_CMDS` in bin/pos:259; lint stdin FAIL cleared.
### M-004
Status: VERIFIED
Severity: HIGH
Category: bug
Files: bin/pos-network-hotspot:53; bin/pos:259
Evidence: `read -rp "Run in background? [y/N]: " bg` at line 53 (non-`--foreground` path). Not in `INTERACTIVE_CMDS` → prompt swallow/hang.
Expected: every stdin reader in `INTERACTIVE_CMDS`.
Recommended fix: add `network-hotspot` to `INTERACTIVE_CMDS`.
Verification: `pos network hotspot start <dev> <iface>` prompts through the dispatcher.
Fix (2026-08-14): added `network-hotspot` to `INTERACTIVE_CMDS` in bin/pos:259; lint stdin FAIL cleared.
### M-005
Status: VERIFIED
Severity: HIGH
Category: bug
Files: install.sh:38,84-99
Evidence: `install.sh --steps` documents format "`1,3,4 or 1-3`" (line 38) but the filter is `[[ ",$STEPS_SPEC," != *",$phase_num,"* ]]` (line 86) — only comma-separated matches. `--steps 1-3` matches nothing → **zero phases run**, silently.
Expected: both documented syntaxes work (comma list and `N-M`/`N-M,K` range).
Recommended fix: expand the range spec into the explicit phase set before filtering (e.g. `1-3``1,2,3`).
Verification: `install.sh --dry-run --steps 1-3` runs phases 1,2,3; `--steps 1,3` runs 1,3.
Fix (2026-08-14): added `normalize_steps_spec()` to install.sh (placed after variable init, before arg loop) — expands `1-3``1,2,3`, validates single/range/list syntax, errors on garbage and end<start. Verified dry-run: `--steps 1-3`→1,2,3; `--steps 3`→3; `--steps 2-2`→2; `--steps 1,3`→1,3; `--steps 1-2-3` and `--steps 3-1` error.
### M-006
Status: VERIFIED
Severity: HIGH
Category: docs
Files: DOC/POS.md:217,220,298,324; DOC/howto/communication.md:83,91,102,200,317; DOC/howto/system.md:18,19,54,78; DOC/HOWTO.md:74; (code: bin/pos-system-health, bin/pos-system-backup)
Evidence: `pos system health [--send] [--markdown]` and `pos system backup --send` are documented in 5 docs, but commit fe7708f deleted the flag handling from `bin/pos-system-health` (82 lines removed) — the tools now hit `err "Unknown option '--send'"`. Not a flag our tools accept → documented feature would fail at runtime.
Expected: docs match code. Either restore `--send`/`--markdown` (notify-path) or strip every reference and rework the howto examples that use it (`@quiet pos system health --send` maps).
Recommended fix: decide feature vs docs: restore the notify send flags in system-health/backup (they were deleted alongside the old pos-health units) OR remove all `--send`/`--markdown` references. Update HOWTO examples accordingly.
Verification: no `--send`/`--markdown` mention in docs that isn't in code (or flags work).
Fix (2026-08-14): decision — do NOT restore the flags. fe7708f deliberately made health a console-only reporter ("health itself never sends notifications. Sending is the wrapper's job"); backup never had a `--send` flag; the scheduler's `NOTIFY=always` already delivers full output. Docs corrected instead: DOC/POS.md:217,220,298,324; DOC/howto/communication.md:83,91,102,200,317; DOC/howto/system.md (17-19, schedule example, env sample); DOC/HOWTO.md:74. Grep-verified: no stale `system health --send`/`--markdown` refs remain (only legit `--markdown` for matrix sender / entertainment send).
### M-007
Status: VERIFIED
Severity: MEDIUM
Category: bug
Files: lib/notify.sh:57,73,79,83
Evidence: `warn "…" 2>/dev/null || echo "…"` — when sourced standalone (notify.sh is designed to be sourceable without common.sh, line 3) and `warn` is absent, the `|| echo` fallback writes the message to **stdout**. notify.sh is meant to be a silent helper (per-file note line 3: "so it can be sourced by tools that define their own log/warn/err"); stdout here contaminates wrappers/plugins (e.g. entertainment send).
Expected: notify.sh must never write to stdout.
Recommended fix: route fallbacks to stderr (`warn() { printf … >&2; }` or `>&2` on the echoes).
Verification: `lib/notify.sh` sourced alone prints nothing to stdout on a failed send.
Fix (2026-08-14): only line 57 actually leaked stdout (`|| echo "…"`); lines 73/79/83 already end in `|| true` (silent). Routed line 57's fallback to stderr (`>…` on the echo). Verified: standalone `source lib/notify.sh; notify_send ""` → stdout empty, message on stderr.
### P1 — clear convention violations
### M-001
Status: VERIFIED
Severity: LOW
Category: convention
Files: features/autostart.sh
Evidence: Lines 1-2: shebang then `LOG="${HOME:-/root}/.autostart.log"` directly — no `set -euo pipefail`. Template `templates/feature.sh:2` requires it; sibling `features/usb-automount.sh:2` has it.
Expected: `set -euo pipefail` as line 2 (all scripts, libs exempt).
Recommended fix: insert `set -euo pipefail` after the shebang (script already defensively uses `|| true`).
Verification: `./scripts/lint-conventions.sh` clean; `bash -n features/autostart.sh`.
Fix (2026-08-14): rewrote autostart.sh with the full feature-template preamble (`set -euo pipefail`, flags.sh load, usage()/`-h|--help` — folded into M-017). Verified: `bash -n` clean; `-h` prints usage; a real run appends the 3 log lines; lint clean.
### M-008..M-014 — `-h|--help` before deps guards (standardization)
Status: VERIFIED
Severity: MEDIUM
Category: standardization
Files: bin/pos-docker-health:21/24, bin/pos-docker-ps:17/20, bin/pos-media-mp3:38/58, bin/pos-media-mp4:43/71, bin/pos-network-scan:28/66, bin/pos-share-usb-server:191/199, bin/pos-system-health:39/119
Evidence: AGENTS.md/DEV.md convention — `command -v` deps guards sit **before** the `-h|--help` dispatch so help also errors on a box missing the dependency. These 7 tools dispatch help first. For docker-health/docker-ps the guard is only ~3 lines late (near-miss). For system-health there is **no top-level guard at all** (all `command -v` are per-check runtime probes at 119/166/182) — it degrades gracefully instead.
Expected: uniform guard-before-help, or an explicitly documented exception for graceful-degradation tools.
Recommended fix: move guards above help in the 6 hard-dep tools; for system-health either add a minimal guard (docker/fail2ban/systemctl are optional by design) or document it as the sanctioned no-guard pattern in DEV.md.
Verification: `make check` green; `./scripts/lint-conventions.sh` shows no dep-guard FAILs; each tool's `--help` still works without deps installed.
Fix (2026-08-14): 6 hard-dep tools now guard before help — docker-health, docker-ps (converted to documented `command -v X || err` form), network-scan (moved up, kept its echo/exit style), share-usb-server (moved up), media-mp3/mp4 (guards moved before help with a `--dry-run` pre-scan preserving the documented dry-run-without-deps behavior; duplicate `DRY_RUN=0` and post-loop guard blocks removed). system-health documented as the sanctioned no-guard pattern in DEV.md (graceful degradation). Lint refined: `first_guard_line()` only matches real guards (`command -v … ||`, `if ! command -v`, `command -v … \`) and `first_line()` skips comment lines. Verified: lint shows no dep-guard FAILs; restricted-PATH tests — `pos media mp3 -h` errors without deps, `--dry-run` still previews; `pos share usb-server -h` errors (usbsrv absent); mp4 mutual-exclusion checks intact.
### M-015
Status: VERIFIED
Severity: MEDIUM
Category: convention
Files: bin/pos-system-firewall
Evidence: `pos system firewall` has no `usage()` and no `-h|--help` case at all — first thing is a root check, then `read -rp "Execute this command?…"`. Violates the universal tool contract (templates/pos-tool.sh). Lint FAIL confirms.
Expected: `-h|--help` shows a usage synopsis.
Recommended fix: add `usage()` + `-h|--help` case (per templates/pos-tool.sh), keeping the root/deps checks ahead of it.
Verification: `pos system firewall --help` prints usage without prompting.
Fix (2026-08-14): added `usage()` + `-h|--help` case after the root check and notify.sh source (root check stays first — tool is root-only by design). Verified as root: `sudo pos-system-firewall -h` prints usage, exit 0.
### M-016
Status: VERIFIED
Severity: MEDIUM
Category: convention
Files: preinstall.sh:28-41 (PACKAGES); bin/pos-media-mp3:59, bin/pos-media-mp4:72
Evidence: both media tools `command -v ffmpeg || err …` — ffmpeg is an apt package, so per DEV.md "apt packages → PACKAGES array in preinstall.sh" it belongs there. It's absent (only yt-dlp is handled, via manual curl).
Expected: ffmpeg in `PACKAGES`; tools keep the guard as a safety net for non-install.sh installs.
Recommended fix: add `ffmpeg` to PACKAGES.
Verification: `install.sh` installs ffmpeg; mp3/mp4 guards remain as fallback.
Fix (2026-08-14): added `ffmpeg` to the PACKAGES array in preinstall.sh. `bash -n` clean; mp3/mp4 `command -v ffmpeg || err` guards untouched.
### M-017
Status: VERIFIED
Severity: LOW
Category: convention
Files: features/autostart.sh
Evidence: template `templates/feature.sh:24-26` requires sourcing `lib/flags.sh` (`# POS_FLAGS:` support), and the tool contract requires `-h|--help`. autostart.sh has neither — it's a bare boot script. Also missing `set -euo pipefail` (M-001).
Expected: feature template contract (set -euo pipefail, flags.sh load, -h|--help).
Recommended fix: add the template preamble (flags.sh load + usage/-h); no functional change to boot logic.
Verification: `bash -n` clean; `-h` prints usage; lint clean.
Fix (2026-08-14): full template preamble added (flags.sh load via the repo/usr-local fallback chain, FEATURE_NAME, usage() with Flag/Log lines, `-h|--help` case). Boot logic unchanged. Verified: `bash -n`, `-h` usage, live run logs 3 lines, lint clean.
### M-018
Status: VERIFIED
Severity: LOW
Category: convention
Files: features/usb-automount.sh
Evidence: same as M-017 — feature template `flags.sh` load (templates/feature.sh:24-26) absent. (Has `set -euo pipefail`, so only flags.sh + usage are missing.)
Recommended fix: add flags.sh load + usage/-h per template.
Verification: lint clean; `-h` works.
Fix (2026-08-14): added the flags.sh load (repo/usr-local fallback) right after `set -euo pipefail`. usage()/`-h|--help` already present. Verified: `bash -n`, lint clean.
### M-019
Status: VERIFIED
Severity: LOW
Category: standardization
Files: apps/media/scrcpy.sh
Evidence: 0644 (`-rw-rw-r--`) while every other `apps/*/*.sh` is 0755. Consistent exec-bit convention violated.
Recommended fix: `chmod +x apps/media/scrcpy.sh`.
Verification: `ls -l apps/media/scrcpy.sh` shows 0755; `make check` exec-bit gate green.
Fix (2026-08-14): `chmod +x apps/media/scrcpy.sh` → 0755. `make check` exec-bit gate will confirm at final run.
### M-020
Status: VERIFIED
Severity: LOW
Category: standardization
Files: bin/pos-docker-compose:8,9
Evidence: `SCALE_DIR="/usr/local/share/linux_post_install/scale-tail/services"` and `CONFIG_ENV="${HOME}/.config/linux_post_install/compose.env"` hardcoded — no `${VAR:-…}` env seam, unlike the documented `SERVICES_BASE` default (/srv). DEV.md seam rule says every system path write has a `VAR="${VAR:-path}"` guard.
Expected: `SCALE_DIR="${SCALE_DIR:-/usr/local/share/…}"` etc.
Recommended fix: add `:-` seams for SCALE_DIR and CONFIG_ENV.
Verification: env override respected in a dry-run.
Fix (2026-08-14): added `:-` seams for both. Verified: `CONFIG_ENV=/tmp/... config` prints the override; `SCALE_DIR=/tmp/... ls` errors citing the override.
Follow-on found while verifying: `pos docker compose config` crashed with `DIM: unbound variable` (common.sh color block had no DIM; pre-existing on HEAD). Fixed by adding `DIM=$(tput dim)` / empty-fallback to the common.sh color block. `config` now prints correctly.
### P2 — doc drift / consistency / style
### M-021
Status: VERIFIED
Severity: LOW
Category: standardization
Files: lib/notify.sh:27, lib/entertainment-lib.sh:5, lib/config-ui.sh:22, bin/pos-network-download:16, bin/pos-communication-{matrix,telegram}-{listener,sender}
Evidence: `CONFIG_DIR` redefined per-file with inconsistent semantics: notify.sh is XDG-aware (`${XDG_CONFIG_HOME:-$HOME/.config}`), the rest use plain `$HOME/.config/linux_post_install`, network-download uses the `${CONFIG_DIR:-…}` seam form. Same global name, three conventions.
Expected: one definition (in lib/common.sh) + seam form everywhere.
Recommended fix: define `CONFIG_DIR` once in common.sh; drop per-file definitions (keep the `:-` seam in network-download).
Verification: all tools still resolve config after refactor.
Fix (2026-08-14): canonical `CONFIG_DIR="${CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/linux_post_install}"` defined in common.sh; dropped the duplicate in entertainment-lib.sh (loads common.sh) and network-download (loads common.sh). Standalone-sourced files (notify.sh, config-ui.sh, the matrix/telegram listener/sender — none source common.sh) keep an identical guarded seam line, mirroring the "no shared lib? inline fallbacks" convention; noted in common.sh. Verified: notify.sh standalone resolves default/XDG/CONFIG_DIR overrides correctly; pos-config (common.sh + config-ui) works; all touched files bash -n clean.
### M-022
Status: VERIFIED
Severity: LOW
Category: standardization
Files: lib/entertainment-lib.sh (plugin_dir/plugin_exists/plugin_keys/plugin_marker), lib/entertainment-plugin-lib.sh (plugin_config_file/plugin_err/plugin_have/plugin_http_json/plugin_load_config/plugin_require)
Evidence: two libs share the `plugin_*` prefix for unrelated jobs (marker/registry helpers vs plugin runtime helpers). Collision-prone and unclear at call sites.
Expected: distinct prefixes per concern.
Recommended fix: rename the plugin-lib helpers (e.g. `plt_*`/`plugin_rt_*`) or the marker helpers; keep one prefix per lib.
Verification: grep for `plugin_` shows no cross-lib ambiguity; `make gen && make check` green.
Fix (2026-08-14): kept `plugin_*` for the documented plugin-authoring API (`plugin_load_config`/`plugin_have`/`plugin_require`/`plugin_http_json`/`plugin_err`/`plugin_config_file` — referenced in DOC/DEV.md and DOC/POS.md; user plugins depend on it). Renamed the internal registry helpers in lib/entertainment-lib.sh to `ent_plugin_dir`/`ent_plugin_marker`/`ent_plugin_exists`/`ent_plugin_keys` and updated callers (pos-entertainment-{status,enable,config,send}, lib/config-ui.sh). Verified: no bare `plugin_` registry helpers remain; plugin runtime helpers untouched; entertainment tools + pos-config smoke-tested; bash -n clean.
### M-023
Status: VERIFIED
Severity: LOW
Category: docs
Files: DOC/POS.md; bin/pos-config, bin/pos-tree, bin/pos-entertainment-{config,enable,disable,status}
Evidence: 6 shipped tools are absent from the POS.md command reference (31 `bin/pos-*` refs exist but not these). Lint WARN confirms.
Expected: every tool documented in POS.md.
Recommended fix: add rows to the POS.md command table (and cross-check HOWTO for the entertainment group).
Verification: lint WARNs gone; `grep` shows each tool in POS.md.
Fix (2026-08-14): the tools were documented by command name but not by filename (the lint references basenames). Added `**File:** bin/pos-config` (config section), `**File:** bin/pos-tree` (tree section), and a file list on the entertainment section header covering `bin/pos-entertainment-{config,enable,disable,status}`. Verified: lint 0 WARN. HOWTO already covers the entertainment group via `pos entertainment *` command forms.
### M-024
Status: VERIFIED
Severity: LOW
Category: docs
Files: AGENTS.md:17-18; DOC/SCRIPTS.md (Phase-2 lib list, TOC, new lib section)
Evidence: the command-registry feature landed (`lib/registry.sh`, 199 lines; optional `# POS_DEPS:`/`# POS_EXAMPLES:` headers already codified in `templates/pos-tool.sh:13-14` and `DOC/DEV.md:126-134`) but three docs kept describing the old reality: AGENTS.md Quick facts enumerated only `POS_FLAGS`/`SUBCMDS`/`CONFIG` with no mention of the shared query API; DOC/SCRIPTS.md's Phase-2 lib list omitted `registry.sh` and had no section for it (its TOC also lacked five pre-existing lib sections).
Expected: docs describe what IS — code + `# POS:` headers are ground truth (Phase 0 rule 4).
Recommended fix: sync the three drifted docs to implemented reality; no code/template/completion changes.
Verification: `make gen` produces zero diff beyond pre-existing work; `make check` green; `make lint` 0 FAIL / 0 WARN; `grep -n "POS_DEPS"` hits AGENTS.md, DEV.md, SCRIPTS.md.
Fix (2026-08-26): template `templates/pos-tool.sh` now documents the optional `# POS_DEPS:`/`# POS_EXAMPLES:` headers (pre-existing); `lib/registry.sh` added as the shared query API over all `POS_*` headers (`reg_scan` + `reg_list`/`reg_lookup`/…) — AGENTS.md Tool-model + Categories bullets updated, DOC/SCRIPTS.md got the lib-list row (install.sh:143 order), a per-lib reference section, and a completed TOC. Consumers were already migrated (`bin/pos-tree`, `bin/pos` `_pos_category_help()`); lint unchanged (0 FAIL / 0 WARN).
### P3 — intentional / legacy (no action)
- install.sh:123,135,155,185 — installer writes to /usr/local/bin are its purpose; no seam needed (lint excludes install scripts).
- network-download RPC_SECRET at :150 — generated at runtime (`/dev/urandom`), not a committed secret.
- system-health graceful degradation — candidate for the sanctioned no-guard pattern (see M-014); decide in fix session whether it becomes a P1 or a P3. Commit fe7708f's removal of the old `pos-health.{service,timer}` units is correct (superseded by scheduler); the only leftover is the stale docs (M-006).
## Semantic deep-dive notes (the detective pass)
- `lib/notify.sh` — stdout-leak via `warn … || echo` fallbacks (M-007); rest of helper logic (platform routing, silent-fail contract) correct.
- `lib/common.sh``confirm()` reads stdin via `read -rp` → it makes any caller an INTERACTIVE_CMDS candidate (this is how M-003/M-002 were caught). Deps guards, `run`/`spawn`, `$DRY_RUN` semantics all match convention.
- `bin/pos` — INTERACTIVE_CMDS list (259) was complete except the 3 new stdin readers (M-002..M-004); no stale entries (lint reverse-check green). Dispatcher longest-prefix logic unchanged from prior fix session.
- `bin/pos-system-firewall` — entirely interactive; root-check-first is correct, but no usage/-h at all (M-015).
- `bin/pos-system-health` — pure passive reporter; per-check `command -v` probes are correct for graceful degradation, but leave the no-top-level-guard pattern undocumented (M-014). Prints to stdout by design (it IS the report) — the notify path is what the docs claim (M-006) but was removed in fe7708f.
- `install.sh``--steps` comma-only parsing (M-005); the phase functions themselves and step numbering are consistent with the header. Legacy `x-systemd.automount`-style remnants: none — the fstab/automount handling in share tools uses correct systemd units now (verified in prior fix session, commits 1724096/93fb6b6).
- `pos-docker-compose` — hardcoded template dir + config path (M-020); `SERVICES_BASE` documented in `# POS_CONFIG:` and used at runtime for deployments, but not for SCALE_DIR.
- No `x-systemd.automount` in any unit `Options=` (grep clean). No `interact`/`getconf`-style stale-format headers anywhere in `bin/`.
- Entertainment plugins: none source common.sh; all carry `# POS_PLUGIN:` + `# POS_KEYS:` (lint green).
- systemd units: `TimeoutStopSec=` + `[Install] WantedBy=` present everywhere (lint green); no leftover `pos-health.{service,timer}` (removed in fe7708f, correct).
## Next-session brief
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 && 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
- lib/common.sh (deps/run/spawn/confirm — semantics verified)
- lib/notify.sh (logic — only the stdout-fallback leak, M-007)
- legacy wrappers bin/wr-*, bin/mp3, bin/mp4, bin/vbox, bin/ssh-load-all (thin, forward to pos)
- systemd/*.service (TimeoutStopSec + WantedBy everywhere)
- entertainment/*.sh (no common.sh, POS_PLUGIN/POS_KEYS present)
- secrets: no committed credentials anywhere (grep for TOKEN/SECRET/AUTHKEY/RPC_SECRET literals clean — all guards/config-reads/runtime-gen)
- stale-format conventions: no `x-systemd.automount`, no old header formats, no leftover pos-health units