v0.8.29: read/inspect tool validation pass — 7 portability/correctness fixes
Ran every read/analysis tool against the real 24-site integrator (lib + wired dispatch). Fixed: nc-find --name (GNU sed \+ → POSIX; 0 rows on BSD/macOS), nc-find tsv/jsonl exit-1-on-success, nc-parse tclproc-refs dropping digit-leading procs (3M_check_ack), nc-xlate diff missing --site, nc-diff-interface + nc-smat-diff printf '-'-leading option-injection dropping output, nc-status not-up crashing on --format, and nc-status not-up's gawk-only \<up\> word-boundary → portable form (BSD/macOS). Test matrix in Deliverables. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
d58e4e0ec8
commit
67cf5fed89
57
CHANGELOG.md
57
CHANGELOG.md
@ -4,6 +4,63 @@ 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.29 — 2026-05-28
|
||||
|
||||
**★ READ/INSPECT TOOL VALIDATION PASS — 6 real bugs found & fixed.** Ran every
|
||||
read/inspect/analysis tool against a real 24-site Cloverleaf integrator fixture
|
||||
(via BOTH the `lib/<x>.sh` path and the wired `execute_tool` dispatch), with real
|
||||
thread/site/xlate/table names discovered from the config. Same class as the
|
||||
v0.8.28 nc-engine arg-parse crash: each only surfaced when actually run. A BSD-awk
|
||||
word-boundary nit in `nc-status.sh` not-up (gawk-only `\<up\>` → portable
|
||||
`(^|[^a-z])up([^a-z]|$)`) was also caught at the Vera gate and folded in.
|
||||
|
||||
- **`nc-find.sh` `--name` mode returned ZERO matches (BSD/macOS).** The protocol-
|
||||
name extraction used the GNU-only `sed \+`. BSD sed (macOS) treats `\+` as a
|
||||
literal `+`, so the thread name came back empty and every `--name` hit was
|
||||
silently dropped. Now POSIX-portable (`[[:space:]][[:space:]]*` /
|
||||
`[A-Za-z0-9_][A-Za-z0-9_]*`). Worked on GNU/Cygwin hosts; broke on BSD.
|
||||
- **`nc-find.sh` exit 1 on success for tsv/jsonl.** The trailing
|
||||
`[ "$FORMAT" = "table" ] && printf ...` test left the script exit code at 1 for
|
||||
non-table formats (the `&&` short-circuits). Added explicit `exit 0` — a
|
||||
successful search (even zero matches) now returns 0; mis-signaled failure to
|
||||
any `$?`/`&&` caller.
|
||||
- **`nc-parse.sh` `tclproc-refs` dropped digit-leading proc names.** The `{ PROC`
|
||||
/ `{ PROCS` regexes required a leading `[A-Za-z_]`, so a real Cloverleaf proc
|
||||
like `3M_check_ack` was never reported — which also blanked `nc_find --tclproc
|
||||
3M_check_ack`. Widened to `[A-Za-z0-9_]+`; `PROCSCONTROL` still excluded.
|
||||
- **`nc-xlate.sh diff` could not find site-scoped xlates.** Unlike show/ops/tree/
|
||||
summary, `diff` did not accept `--site`, so `locate_xlate` only checked
|
||||
`$HCIROOT/Xlate` and always died `no such xlate` for the common case (xlates
|
||||
under `<site>/Xlate/`). `cmd_diff` now takes `--site` (applied to both names)
|
||||
and larry.sh forwards it.
|
||||
- **`nc-diff-interface.sh` printf option-injection.** `printf '---\n\n'` (the
|
||||
markdown horizontal rule) parsed `---` as printf options → `printf: --: invalid
|
||||
option`, dropping the rule. Now `printf '%s\n\n' '---'`.
|
||||
- **`nc-smat-diff.sh` printf option-injection (8 lines).** `printf '- A: ...'`
|
||||
format strings starting with `- ` errored `printf: - : invalid option` in
|
||||
NON-interactive bash, dropping every summary bullet. Guarded with `printf --`.
|
||||
- **`nc-status.sh not-up` crashed on `--format`.** `cmd_not_up` accepted only
|
||||
`--site`/`--filter`; the larry.sh dispatch ALWAYS appends `--format`, so
|
||||
`not-up` died `unknown flag: --format` and was unusable via the wired tool.
|
||||
`cmd_not_up` now accepts `--format` and forwards it to `cmd_threads`.
|
||||
|
||||
All other read/inspect tools (nc_paths up/down/full/all/site-only incl. the
|
||||
cross-site ADTto_CodaMetrix chain, nc_destinations/sources, nc_list_*,
|
||||
nc_protocol_*, nc_find_inbound, nc_document single+system, nc_revisions
|
||||
timeline+diff, nc_msgs raw+field-filter+json, nc_xlate list/show/ops/tree/summary,
|
||||
nc_xlate_refs, nc_tclproc_refs, hl7_field, hl7_diff, nc_diff_interface,
|
||||
nc_regression 6-phase + chain-walk command-gen, nc_smat_diff pairing, nc_engine
|
||||
+ nc_status graceful degrade, list_sites ignore-rules, all 7 nc_tclgen templates
|
||||
verified `info complete` in tclsh) PASSED unchanged. Test matrix:
|
||||
`Deliverables/2026-05-28-cloverleaf-v3-tool-test-matrix.md`.
|
||||
|
||||
KNOWN / TRIAGE (not fixed this pass): `hl7-sanitize.sh` is a silent no-op on
|
||||
LF-delimited input (`RS="\r"` reads the whole file as one record) — fixing needs
|
||||
a portable CR/LF normalizer (BSD awk has no regex `RS`); `nc_engine route-test/
|
||||
testxlate/resend` ignore `--dry-run` (only stop/start/bounce/restart honor it) and
|
||||
the dispatch never forwards it; `lessons.sh:142` has the same `printf '---'`
|
||||
option-injection (out-of-scope write tool).
|
||||
|
||||
## v0.8.28 — 2026-05-28
|
||||
|
||||
**★ EXPOSE 5 lib-only tools as first-class LLM tools.** A roadmap audit found
|
||||
|
||||
18
MANIFEST
18
MANIFEST
@ -23,16 +23,16 @@
|
||||
# scripts/make-manifest.sh and bump VERSION.
|
||||
|
||||
# Top-level scripts
|
||||
larry.sh 4bc6355ebd04b3e301c28d9c34a8ec2fdae44fa223f1f065cdcd2790a4676e33
|
||||
larry.sh 219c0b4f84aabec17baec7ba20c47849364bce9039bbcaa07ed7ba81b7b38a05
|
||||
larry-tunnel.sh 6b050e4eeab15669f4858eaf3b807f168f211ced07815db9521bc40a093f6aaa
|
||||
larry-auth.sh a220cdf7878569dc3028951ee57fc8d5e706a8ca5c6aa45347b58facb386f831
|
||||
larry-rollback.sh 91b5e9aa6c79266bf306dcfba4ca791c07971bd6924d67a779037531648aa6d0
|
||||
install-larry.sh fa36e23a39eacbd0d7ecedd3b42131902f816ee7e98241dfc6e28c6e4ba80423
|
||||
|
||||
# Metadata
|
||||
VERSION 4d1f87fb5ed962079a382ddacb5e4f28b461dd9d6c4a6c4085248832fa8111a5
|
||||
VERSION 14624c56b466c22dbace115e50c329bc4bea74a2c25e1f3aa481766ea49ddff7
|
||||
MANUAL.md c64bd0251a51ad150508b4e1185355bc4826a64071d4de339f92ed550dbfacde
|
||||
CHANGELOG.md 567da1b7ddb2f8200eb42cfce6387f30ca753fc065abe4994f887e732d2a36f9
|
||||
CHANGELOG.md f3a6ac02750188f6cec37e1d7454424c363706f2f9a8a6b041b449b1d783479c
|
||||
|
||||
# Agent personas (system-prompt overlays)
|
||||
agents/larry.md 0a1ef737e7fc133ab35be09f79c3a4df33de814e0404b69b950932d0c8a01be1
|
||||
@ -91,20 +91,20 @@ lib/table-to-csv.sh ad98e73687bc9e9f6ae0cd79ed5ba26c856076902865230f822dec1a1bea
|
||||
|
||||
# NetConfig tooling
|
||||
lib/nc-engine.sh e2b12a1c019d40857b96d48d6c185b94aefadab604536ce41077ecc251b0bc58
|
||||
lib/nc-status.sh 80d2023babff70c065ffab70b5ecf9bdfd80183ae5808f610335da9c8c27f97f
|
||||
lib/nc-status.sh fa08c5e48704d3d17e2206dafc8522ae7668b7a2f9b97f3521e05fd0ba739443
|
||||
lib/nc-table.sh a6d5c11dd460cfb100ea50c74d57c1a46ef49112632037534a32cd28600abe7f
|
||||
lib/nc-xlate.sh b05caae72889f6404a2a1618ba3ba3666dc34f03d49a779664ea31396dddb112
|
||||
lib/nc-smat-diff.sh ac003954701ea6b7f4aa1f6941f8536af5b5cdfbb75e306789753d453f06800e
|
||||
lib/nc-xlate.sh 8621e6f0ef55524dd6ecba91fee055cf9cdc168791e75ba7c15d9bf501fe09bf
|
||||
lib/nc-smat-diff.sh 9c04d9e2f35f22c78d5f3c40a884ed23a3b6aaabc53ee27dfbfb66ab3166a567
|
||||
lib/nc-create-thread.sh 5a9d5407c117183cad831d6b95f0e785b1b806f5ccc67f803c12b3695882b5b7
|
||||
lib/nc-tclgen.sh 5b8e73d7f6950a2b84f563132562ea82f62f4acac907257e233c7e68d85506c9
|
||||
lib/nc-parse.sh 3419b3f8d0cfdaf767f91551d6e2441d0743d80bd31515ffa61c769db1542c2f
|
||||
lib/nc-parse.sh 52fef42d7a4b361534ab0d921deef74586dfeb6c199c941cebb55abcc2c39d4f
|
||||
lib/nc-paths.sh 388d2f4560736587a01218cadc1de612cd59e392819d16db2f56f19174c1111b
|
||||
lib/nc-inbound.sh 52d28c5f8d97bdf96f0fc7b5300d35b106b8e1226578f4cda430deb2a8b4a91b
|
||||
lib/nc-make-jump.sh 08a0bc58a299c95c60a59a5202792daf0ada3a8a0be7dc1b4cccc5724f5c9c79
|
||||
lib/nc-msgs.sh 20517922d1153ec7827c833987497fb305d087b579911d1b9067d65ae156a19f
|
||||
lib/nc-document.sh 47211e99089c0446d25a1e84545a734894720a1c9ad8f59b920332035e4ea880
|
||||
lib/nc-revisions.sh c27856f7decfc4c2e2c990f59eb20136fdff9cf0a52b9d9fbd9370613666a802
|
||||
lib/nc-diff-interface.sh 6b64ec3070a3d75d1d79632c9aeb357177fdbcc77c474aa78e6f6929fda1a324
|
||||
lib/nc-find.sh 8c79e0acad7de56e4e1f12d61e071a4b98c4e2310a1f7fb183697df521215e3f
|
||||
lib/nc-diff-interface.sh c922d10323f06346efa53ada68b44d32d9568ff0bd848c59af3404135f29d1ad
|
||||
lib/nc-find.sh 2264877c56100378a1b780d640dcaa806aa5501ddd204c6b6a8eb5d3e07bf966
|
||||
lib/nc-insert-protocol.sh ad1fa0bafbf4fdfb12bad20f9c22c3eed519f8846774331e26aa9becd6f8898a
|
||||
lib/nc-regression.sh 70999a60608439f7bf1a3abb9f5e9854b5ea03025ef29ddbca683896346d1bce
|
||||
|
||||
5
larry.sh
5
larry.sh
@ -78,7 +78,7 @@ set -o pipefail
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Config
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
LARRY_VERSION="0.8.28"
|
||||
LARRY_VERSION="0.8.29"
|
||||
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
@ -4235,7 +4235,8 @@ tool_nc_xlate() {
|
||||
[ -n "$site" ] && args+=(--site "$site") ;;
|
||||
diff)
|
||||
[ -n "$name" ] && [ -n "$name2" ] || { echo "ERROR: nc_xlate diff needs name and name2"; return 1; }
|
||||
args=(diff "$name" "$name2") ;;
|
||||
args=(diff "$name" "$name2")
|
||||
[ -n "$site" ] && args+=(--site "$site") ;;
|
||||
"") args=(list); [ -n "$site" ] && args+=(--site "$site") ;;
|
||||
*) echo "ERROR: unknown nc_xlate subcommand: $subcmd (list|show|ops|tree|summary|diff)"; return 1 ;;
|
||||
esac
|
||||
|
||||
@ -276,7 +276,7 @@ collect_tclprocs_for() {
|
||||
fi
|
||||
fi
|
||||
|
||||
printf '---\n\n'
|
||||
printf '%s\n\n' '---'
|
||||
printf '_Generated %s by Larry-Anywhere nc-diff-interface.sh (depth=%d)._\n' \
|
||||
"$(date -Iseconds 2>/dev/null || date)" "$DEPTH"
|
||||
} | out_target
|
||||
|
||||
@ -109,7 +109,11 @@ for nc in "${NCONFIGS[@]}"; do
|
||||
# Partial match (substring)
|
||||
while IFS= read -r raw; do
|
||||
line=$(printf '%s' "$raw" | cut -d: -f1)
|
||||
thread_name=$(printf '%s' "$raw" | sed -n 's/^[0-9]*:protocol[[:space:]]\+\([A-Za-z0-9_]\+\)[[:space:]]*{.*$/\1/p')
|
||||
# PORTABILITY: POSIX [[:space:]][[:space:]]* / [A-Za-z0-9_][A-Za-z0-9_]*
|
||||
# instead of the GNU-only `\+`. BSD sed (macOS) treats `\+` as a
|
||||
# literal `+`, so the GNU form extracted an empty name and every
|
||||
# --name match was silently dropped on macOS/BSD hosts.
|
||||
thread_name=$(printf '%s' "$raw" | sed -n 's/^[0-9]*:protocol[[:space:]][[:space:]]*\([A-Za-z0-9_][A-Za-z0-9_]*\)[[:space:]]*{.*$/\1/p')
|
||||
[ -z "$thread_name" ] && continue
|
||||
pname=$("$NCP" protocol-field "$nc" "$thread_name" PROCESSNAME 2>/dev/null | head -1)
|
||||
pport=$("$NCP" protocol-nested "$nc" "$thread_name" PROTOCOL.PORT 2>/dev/null | head -1 | sed 's/^{}$//')
|
||||
@ -233,3 +237,7 @@ esac
|
||||
# (printf '%d' "5\r" fails with "invalid number").
|
||||
n=$(wc -l < "$RESULTS" | tr -cd '0-9')
|
||||
[ "$FORMAT" = "table" ] && printf '\n%d match(es)\n' "${n:-0}" >&2
|
||||
# Always exit 0 on a successful search (even zero matches). Without this, the
|
||||
# trailing `[ "$FORMAT" = "table" ] && ...` test leaves the script exit code at
|
||||
# 1 for tsv/jsonl (the && short-circuits), mis-signaling failure to callers.
|
||||
exit 0
|
||||
|
||||
@ -448,12 +448,14 @@ cmd_tclproc_refs() {
|
||||
{
|
||||
line = $0
|
||||
# PROC <name> (singleton, e.g. DATAFORMAT.PROC)
|
||||
if (match(line, /\{ PROC [A-Za-z_][A-Za-z0-9_]*/)) {
|
||||
# Allow a LEADING DIGIT: Cloverleaf proc names like 3M_check_ack are valid
|
||||
# TCL proc names. The old [A-Za-z_]-first class silently dropped them.
|
||||
if (match(line, /\{ PROC [A-Za-z0-9_]+/)) {
|
||||
v = substr(line, RSTART + 7, RLENGTH - 7)
|
||||
print v
|
||||
}
|
||||
# PROCS <name> (singleton)
|
||||
if (match(line, /\{ PROCS [A-Za-z_][A-Za-z0-9_]*/)) {
|
||||
if (match(line, /\{ PROCS [A-Za-z0-9_]+/)) {
|
||||
v = substr(line, RSTART + 8, RLENGTH - 8)
|
||||
print v
|
||||
}
|
||||
|
||||
@ -124,10 +124,13 @@ B_KEYS=$(awk -F'\t' '{print $1}' "$OUT/b/sorted.tsv" | sort -u)
|
||||
SUMMARY="$OUT/_summary.md"
|
||||
{
|
||||
printf '# smat diff: thread=%s\n\n' "$THREAD"
|
||||
printf '- A: `%s/%s` (%d messages sampled)\n' "$ENV_A" "$SITE_A" "$A_COUNT"
|
||||
printf '- B: `%s/%s` (%d messages sampled)\n' "$ENV_B" "$SITE_B" "$B_COUNT"
|
||||
printf '- pair-on: `%s`\n' "$PAIR_ON"
|
||||
printf '- ignore: `%s`\n\n' "$IGNORE"
|
||||
# printf -- guard: a format string starting with '- ' is otherwise parsed as
|
||||
# an option by the bash printf builtin in NON-interactive shells ("printf: -
|
||||
# : invalid option"), dropping the line. -- ends option parsing.
|
||||
printf -- '- A: `%s/%s` (%d messages sampled)\n' "$ENV_A" "$SITE_A" "$A_COUNT"
|
||||
printf -- '- B: `%s/%s` (%d messages sampled)\n' "$ENV_B" "$SITE_B" "$B_COUNT"
|
||||
printf -- '- pair-on: `%s`\n' "$PAIR_ON"
|
||||
printf -- '- ignore: `%s`\n\n' "$IGNORE"
|
||||
printf '## Per-pair diffs\n\n'
|
||||
printf '| %s | diffs | report |\n|---|---|---|\n' "$PAIR_ON"
|
||||
} > "$SUMMARY"
|
||||
@ -163,10 +166,11 @@ done < <(printf '%s\n%s\n' "$A_KEYS" "$B_KEYS" | sort -u)
|
||||
|
||||
{
|
||||
printf '\n## Summary\n\n'
|
||||
printf '- paired (A and B): %d\n' "$PAIRED"
|
||||
printf '- A-only: %d\n' "$A_ONLY"
|
||||
printf '- B-only: %d\n' "$B_ONLY"
|
||||
printf '- total field differences (post-ignore): %d\n' "$DIFFS_TOTAL"
|
||||
# printf -- guard (see note above): leading '- ' format strings.
|
||||
printf -- '- paired (A and B): %d\n' "$PAIRED"
|
||||
printf -- '- A-only: %d\n' "$A_ONLY"
|
||||
printf -- '- B-only: %d\n' "$B_ONLY"
|
||||
printf -- '- total field differences (post-ignore): %d\n' "$DIFFS_TOTAL"
|
||||
} >> "$SUMMARY"
|
||||
|
||||
printf 'done. Summary: %s\n' "$SUMMARY" >&2
|
||||
|
||||
@ -111,16 +111,21 @@ cmd_threads() {
|
||||
cmd_not_up() {
|
||||
local site="${HCISITE:-}"
|
||||
local filter=""
|
||||
local format="text"
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--site) shift; site="$1" ;;
|
||||
--filter) shift; filter="$1" ;;
|
||||
# Accept --format and forward it to cmd_threads. The larry.sh dispatch
|
||||
# ALWAYS appends --format, so without this not-up died "unknown flag:
|
||||
# --format" and was unusable via the wired tool.
|
||||
--format) shift; format="$1" ;;
|
||||
*) die "unknown flag: $1" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
cmd_threads --site "$site" ${filter:+--filter "$filter"} \
|
||||
| awk 'NR==1 || tolower($0) !~ /\<up\>/'
|
||||
cmd_threads --site "$site" --format "$format" ${filter:+--filter "$filter"} \
|
||||
| awk 'NR==1 || tolower($0) !~ /(^|[^a-z])up([^a-z]|$)/'
|
||||
}
|
||||
|
||||
cmd_connections() {
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
# ops <name> [--site SITE] list operations as TSV (op, in, out, err)
|
||||
# tree <name> [--site SITE] ASCII tree by op type
|
||||
# summary <name> [--site SITE] counts by operation + segments touched
|
||||
# diff <name1> <name2> diff two xlates (semantic, sorted-by-op)
|
||||
# diff <name1> <name2> [--site SITE] diff two xlates (semantic, sorted-by-op)
|
||||
set -o pipefail
|
||||
|
||||
NC_SELF="$0"
|
||||
@ -135,10 +135,23 @@ cmd_summary() {
|
||||
}
|
||||
|
||||
cmd_diff() {
|
||||
local n1="$1" n2="$2"
|
||||
# Accept --site like every other subcommand. Without it, site-scoped xlates
|
||||
# (the common case — xlates live under <site>/Xlate/) could not be located
|
||||
# and diff always died with "no such xlate". --site applies to BOTH names.
|
||||
local site="${HCISITE:-}"
|
||||
local positional=()
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
--site) shift; site="$1" ;;
|
||||
*) positional+=("$1") ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
local n1="${positional[0]:-}" n2="${positional[1]:-}"
|
||||
[ -n "$n1" ] && [ -n "$n2" ] || die "usage: diff NAME1 NAME2 [--site SITE]"
|
||||
local f1 f2
|
||||
f1=$(locate_xlate "$n1") || die "no such xlate: $n1"
|
||||
f2=$(locate_xlate "$n2") || die "no such xlate: $n2"
|
||||
f1=$(locate_xlate "$n1" "$site") || die "no such xlate: $n1"
|
||||
f2=$(locate_xlate "$n2" "$site") || die "no such xlate: $n2"
|
||||
diff -u <(parse_ops "$f1" | sort -k2) <(parse_ops "$f2" | sort -k2)
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user