v0.8.16: hotfix set -u unbound-variable abort in /ssh-set-hciroot + /ssh REPL slash handlers

Single-line `local a=… b="…$a…"` referenced the 1st var before it was bound
within the SAME `local` statement, aborting under set -u on Cygwin/MobaXterm
(and modern bash). Split larry.sh:6903 (/ssh-set-hciroot) and the same latent
pattern at larry.sh:6925 (/ssh) into set-u-safe declare-then-assign form.
Codebase-wide bug-class audit (larry.sh + all lib/*.sh + scripts): zero
remaining instances. Closed the v0.8.15 gate gap by driving the ACTUAL REPL
slash-dispatch handler bodies under set -u + BASH_COMPAT=3.2 (not just the
ssh-helper subcommand): /ssh-set-hciroot normal + empty-path-clear, /ssh, and
usage paths all pass; old code aborts under the same harness. No-traffic-bypass
line unchanged.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Bryan Johnson 2026-05-28 09:13:26 -07:00
parent fc667e2451
commit 0e6495223a
4 changed files with 64 additions and 9 deletions

View File

@ -4,6 +4,53 @@ All notable changes to `cloverleaf-larry` / `larry-anywhere` are recorded here.
Versioning is loose-semver; bumps trigger the in-process self-update on every
running client via `LARRY_BASE_URL` + `MANIFEST`.
## v0.8.16 — 2026-05-28
Hotfix (Clover): `set -u` unbound-variable abort in the v0.8.15
`/ssh-set-hciroot` REPL slash command, reported live on Bryan's MobaXterm /
Cygwin bash (`larry.sh: line 6903: _sh_alias: unbound variable`).
**Root cause.** `larry.sh:6903` was a single-line `local` declaration in which
the 2nd variable's initializer referenced the 1st variable assigned in the SAME
`local` statement:
`local _sh_alias="${rest%% *}" _sh_path="${rest#"$_sh_alias"}"`.
Under `set -u`, `$_sh_alias` is treated as unbound at expansion time within its
own `local` statement, aborting the command. This is NOT Cygwin/bash-3.2
specific — it reproduces identically on bash 5.x. The same latent pattern lived
at `larry.sh:6925` (the `/ssh <alias> <cmd>` handler:
`local alias="${rest%% *}" rcmd="${rest#"$alias"}"`), not yet triggered only
because that path hadn't been run.
**Fix.** Both split into set-u-safe form — declare the locals first, assign the
first, THEN reference it on a later line. Correct on every bash version.
1. `/ssh-set-hciroot` handler (was `larry.sh:6903`) — safe-split; preserves the
empty-path "clear the pin" semantics.
2. `/ssh` handler (was `larry.sh:6925`) — same safe-split.
**Codebase-wide bug-class audit.** Every `.sh` (larry.sh + all `lib/*.sh` +
top-level scripts) was scanned for any single-statement `local`/`declare`/
`typeset`/`readonly`/`export` where one variable's initializer references
another variable assigned in the SAME statement. After the two fixes above,
**zero remaining instances**. All superficially-similar lines elsewhere are
`;`-separated sequential statements (e.g. `_x="${rest%% *}"; rest="${rest#"$_x"}"`)
where the referenced variable is already bound — these are the CORRECT idiom and
are safe under `set -u`.
**Gate-gap closed.** The v0.8.15 gate exercised the `ssh-helper.sh set-hciroot`
SUBCOMMAND but never the larry.sh `/ssh-set-hciroot` REPL slash dispatch — where
the bug actually was. The v0.8.16 gate drives the ACTUAL slash-command handler
bodies (extracted verbatim from the fixed source) through a `case` dispatcher
under `set -u` with `BASH_COMPAT=3.2`, non-interactively, with no self-update /
API / bootstrap. Verified `/ssh-set-hciroot <alias> <path>` (normal),
`/ssh-set-hciroot <alias>` (empty-path CLEAR), trailing-space clear, no-args
usage, `/ssh <alias> <cmd>`, and `/ssh <alias>` (usage) all execute WITHOUT the
unbound-var abort. The same harness run against the OLD v0.8.15 line aborts with
`_sh_alias: unbound variable` (rc=1), confirming the gate now catches this
regression class. The no-traffic-bypass security line is unchanged.
---
## v0.8.15 — 2026-05-28
Legacy/qa remote-enumeration fix (Clover). Three confirmed-live properties of

View File

@ -23,16 +23,16 @@
# scripts/make-manifest.sh and bump VERSION.
# Top-level scripts
larry.sh 2e7650eb7a014624bd6956c30ce3a54e0e87d4ccfc73bb0b2ae82d1a31b882e0
larry.sh 7bbd920f7a29379aadad3e59a298ff416333e9299241db7c7bb8c42cc7750f9d
larry-tunnel.sh 6b050e4eeab15669f4858eaf3b807f168f211ced07815db9521bc40a093f6aaa
larry-auth.sh a220cdf7878569dc3028951ee57fc8d5e706a8ca5c6aa45347b58facb386f831
larry-rollback.sh 91b5e9aa6c79266bf306dcfba4ca791c07971bd6924d67a779037531648aa6d0
install-larry.sh e97da4e12a0d8863ca18d79b12f6c4294c72fa6d4b11dffeab66504236bb4eb1
# Metadata
VERSION 8517de55d0fc1041caab07518dbf7da86dba47c3befe0a6ef84d005872cb799d
VERSION 088faf7f331988e309c37e27272af81cd41fdcf4466022059ffdd3184e3c7d34
MANUAL.md 666128a086b59ff3c31a574aec0c5dd681666d66319da9f078451bf9013ca5e1
CHANGELOG.md 0b8f2dba750577f934935dd7d5805c498afa9d516cd37e5b6cda039cb86ec350
CHANGELOG.md 3db03a7913b3a66310f9cb282eaa6c3554d5ed59ef92eca78ef02257dc97277f
# Agent personas (system-prompt overlays)
agents/larry.md 11ea905fa7cac6fa7baeb11b2d62af07b15a666ce90cfe36491bcbc555244397

View File

@ -1 +1 @@
0.8.15
0.8.16

View File

@ -78,7 +78,7 @@ set -o pipefail
# ─────────────────────────────────────────────────────────────────────────────
# Config
# ─────────────────────────────────────────────────────────────────────────────
LARRY_VERSION="0.8.15"
LARRY_VERSION="0.8.16"
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
# ─────────────────────────────────────────────────────────────────────────────
@ -6900,8 +6900,13 @@ main_loop() {
if [ -z "$rest" ]; then
err "usage: /ssh-set-hciroot <alias> <path> (empty path clears the pin)"; continue
fi
local _sh_alias="${rest%% *}" _sh_path="${rest#"$_sh_alias"}"
_sh_path="${_sh_path# }"
# v0.8.16: set-u-safe split. A single-line `local a=… b="…$a…"`
# references $_sh_alias before it is bound within the SAME `local`
# statement, which aborts under `set -u` on bash 3.2/Cygwin
# (MobaXterm) AND modern bash. Declare first, assign sequentially.
local _sh_alias _sh_path
_sh_alias="${rest%% *}"
_sh_path="${rest#"$_sh_alias"}"; _sh_path="${_sh_path# }"
if [ -z "$_sh_alias" ]; then
err "usage: /ssh-set-hciroot <alias> <path>"; continue
fi
@ -6922,8 +6927,11 @@ main_loop() {
continue ;;
/ssh*) local rest; rest=$(_slash_args "/ssh" "$input")
if [ -z "$rest" ]; then err "usage: /ssh <alias> <command>"; continue; fi
local alias="${rest%% *}" rcmd="${rest#"$alias"}"
rcmd="${rcmd# }"
# v0.8.16: same set-u-safe split as /ssh-set-hciroot above —
# $alias was referenced before binding in a single-line `local`.
local alias rcmd
alias="${rest%% *}"
rcmd="${rest#"$alias"}"; rcmd="${rcmd# }"
if [ -z "$alias" ] || [ -z "$rcmd" ]; then
err "usage: /ssh <alias> <command>"; continue
fi