- bin/pos: _pos_category_list now emits category-less tools (pos-bank,
pos-config, pos-tree) as sorted menu entries with an empty subcommand
column, deduped against real categories (pos-ai). bank previously
never appeared in the bare 'pos' menu.
- bin/pos-bank: fixed invalid 'local name="" -a cli_params=()' at
cmd_run (line 150) — bash rejected '-a' after an assignment, crashing
every 'pos bank run' with 'local: -a: not a valid identifier'.
Split into 'local name=""' + 'local -a cli_params=()'.
- tests/t-bank.sh: added B12-B14 covering cmd_run (no-params,
missing-command, param substitution) — would have caught the crash.
- AGENT_TODO.md: bank feature moved to Done.
Architect-approved (C): install.sh now skips+aborts (exit 0) when the
installed version equals the current git-derived version, with a --force
flag to re-install. Version scheme 0.0cN (N = git commit count) bumps by
construction on every commit.
- install_version(): derives 0.0c$count via git rev-list; empty when .git
absent (gate skipped); INSTALL_VERSION_OVERRIDE presence-check seam for
tests (empty override simulates no-git deterministically)
- Gate after arg parse, before phases; numeric comparison (strip 0.0c, -eq);
messages: 'Already installed (X). Use --force to re-install.' and
'(dry-run) Would skip install: already at version X', both exit 0
- flag_set installed_version after 'Bootstrap complete' banner (not in
dry-run, even under --force, never on phase failure)
- Docs: SCRIPTS.md flag table + gate description, AGENT_Context line count
248->301 + flags + flow diagram, README --force row, tests/README row,
AGENT_TODO Done entry
Verified: new tests/t-install-version.sh 21 checks (9 contracted cases,
real install.sh + hermetic env seams); suite 20 files / 461 checks /
0 fail / 0 skip; make gen byte-idempotent; make check OK; make lint
0 FAIL, 0 WARN; bash -n clean; git diff --check clean; Reviewer
APPROVE_WITH_NOTES with 3 mutation disproofs (8/21, 9/21, 4/21 fail)
Permanent coverage for the 2026-09-07 share mountpoint UX change
(commit 35eb90a): the REAL ask_mountpoint body is brace-extracted from
each client file (smb + nfs), exercised through the real common.sh +
share-lib.sh helper chain via the deterministic non-TTY stdin path, with
a fake sudo shim (records to SUDO_LOG, honors SUDO_FAIL) so no real
filesystem is touched.
Covers: existing dir used as-is (rc 0, provable NO confirm / NO mkdir
via empty SUDO_LOG) added by 35eb90a; new-dir confirm=y creates via sudo
mkdir; confirm=n and confirm=EOF both fail closed rc 1; existing
non-directory rejected 'not a directory'; relative / trailing-slash /
system paths (/etc, /root, /home/*/.ssh*) and empty-EOF all rc 1 with no
side effects; mkdir failure rc 1. Stdout purity (path only). Static
guards fail if anyone regresses n=new / n|N / ask_new_mountpoint, and
verify the existing-dir [-d] branch precedes confirm/mkdir. Byte-symmetry
guard keeps smb/nfs bodies identical.
117 checks new (18 files / 416 total). Every case can genuinely fail
(negative-control verified by Tester). Hard-skip contract respected: 0
skips — all cases deterministic non-interactively.
Verified: make check OK, make lint 0 FAIL/0 WARN, make test 18 files /
416 checks / 0 fail / 0 skip (~46s), bash -n clean, git diff --check clean.