v0.8.23: regression chain-walk route-test capture (nc-regression --chain-walk)
Resolves the downstream route chain via nc-paths, grabs N recent messages from the START inbound's SMAT, walks each ENTRY node (START + post-==> remote inbounds) running hciroutetest -a -d -f nl, chaining each step's selected .out.<DEST> across cross-site hops. Generates per-chain commands.sh for the engine box; --dry-run stubs the engine. Command syntax mined verbatim from the v1/v2 route_test wrappers. Fixes --help sed range (header ends at 94). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
3c8b5d6f49
commit
f5f56439d0
55
CHANGELOG.md
55
CHANGELOG.md
@ -4,6 +4,61 @@ All notable changes to `cloverleaf-larry` / `larry-anywhere` are recorded here.
|
|||||||
Versioning is loose-semver; bumps trigger the in-process self-update on every
|
Versioning is loose-semver; bumps trigger the in-process self-update on every
|
||||||
running client via `LARRY_BASE_URL` + `MANIFEST`.
|
running client via `LARRY_BASE_URL` + `MANIFEST`.
|
||||||
|
|
||||||
|
## v0.8.23 — 2026-05-28
|
||||||
|
|
||||||
|
**★ REGRESSION CHAIN-WALK route-test capture (Bryan's priority).** New
|
||||||
|
`--chain-walk` mode in `lib/nc-regression.sh` — a single-env (per-env) capture
|
||||||
|
that runs `route_test` at every routing(inbound) thread along an `nc-paths`
|
||||||
|
chain and chains each step's per-destination output into the next step's input.
|
||||||
|
Orthogonal to the existing 6-phase env-A/env-B pipeline; Bryan runs it on env-A
|
||||||
|
and env-B with the same START input, then the existing `hl7-diff --ignore MSH.7`
|
||||||
|
diff phase compares the captured outputs.
|
||||||
|
|
||||||
|
Workflow (verbatim Bryan spec):
|
||||||
|
- Take a START thread + N. Grab the N most-recent messages from the START
|
||||||
|
inbound's SMAT via `nc-msgs --limit N --format raw` → `input.msgs`.
|
||||||
|
- Resolve the downstream chain(s) from START with `nc-paths --format jsonl`.
|
||||||
|
`--target <site/thread>` restricts to the single chain ending at that node;
|
||||||
|
with no `--target`, ALL fan-out branches are walked (each branch a chain dir).
|
||||||
|
- The route_test ENTRY threads are the START node plus every node that
|
||||||
|
immediately follows a cross-site `==>` hop (the remote inbound). Outbound
|
||||||
|
*sender* nodes (e.g. `OB3_RAD_ordersS`) are NOT entries — they are produced as
|
||||||
|
`.out.<sender>` files by the upstream route_test.
|
||||||
|
- At each entry E: `hciroutetest -a -d -f nl -s <out_base> <E> <input>` — the
|
||||||
|
command string is mined VERBATIM from the v1/v2 wrapper
|
||||||
|
(`cloverleaf_tools/tools/route_test_wrapper.py:10/149-156`); `-f nl` yields
|
||||||
|
NEWLINE output directly (no manual `len2nl`+delete). `-a` (all routes) writes
|
||||||
|
every fan-out branch's output in one invocation.
|
||||||
|
- route_test writes ONE FILE PER DESTINATION named `<out_base>.out.<DEST>`
|
||||||
|
(`.out.<DEST>` naming confirmed from `legacy_workflow_commands.py:1015`). File
|
||||||
|
SELECTION: the node immediately AFTER E in the chain is the suffix selected to
|
||||||
|
feed the NEXT step. For the cross-site boundary `S ==> R`, the selected
|
||||||
|
`.out.<S>` payload becomes the input fed to the next site's inbound `R`.
|
||||||
|
- PRODUCES under `$OUT/chain/<NN>/`: `input.msgs` (original START messages),
|
||||||
|
`step-NN.<E>.out.<DEST>` (per-destination outputs), `step-NN.<E>.selected`
|
||||||
|
(the chosen next-step input), `commands.sh` (the exact generated command
|
||||||
|
sequence), `chain.txt` (the resolved v1 chain).
|
||||||
|
|
||||||
|
`hciroutetest` is a Linux engine binary needing a live engine — NOT runnable off
|
||||||
|
the server — so chain-walk GENERATES the orchestration + command STRINGS +
|
||||||
|
file-chaining/selection and stubs the engine call under `--dry-run`; real
|
||||||
|
execution is on the server (source the Cloverleaf profile first). Everything that
|
||||||
|
does NOT need the engine is self-verified against the real integrator: the
|
||||||
|
nc_paths chain resolution, the nc-msgs message-grab command, the per-step
|
||||||
|
route_test command strings, and the `.out.<DEST>` → next-step SELECTION.
|
||||||
|
|
||||||
|
New flags: `--chain-walk --start <site/thread|thread> [--site SITE] --count N
|
||||||
|
--hciroot HCIROOT --out DIR [--target <site/thread>] [--route-test-bin BIN]
|
||||||
|
[--dry-run]`. Portable bash (Win+Linux), pure bash+awk, **API-FREE**, no
|
||||||
|
python/.pyz. Existing phase pipeline untouched.
|
||||||
|
|
||||||
|
Verified on `ORUto_CodaMextrix orders`
|
||||||
|
(`mux/RadOrdfr_epic_972310 --> mux/OB3_RAD_ordersS ==> orders/IB3_RAD_muxS -->
|
||||||
|
orders/ORUto_CodaMextrix`): 2 route_test entries (`RadOrdfr_epic_972310`@mux,
|
||||||
|
`IB3_RAD_muxS`@orders); start grab + the exact per-step `hciroutetest` strings +
|
||||||
|
the `.out.OB3_RAD_ordersS` (cross-site) → `.out.ORUto_CodaMextrix` (terminus)
|
||||||
|
selection. No-target run walks all 6 `IB3_RAD_muxS` fan-out branches.
|
||||||
|
|
||||||
## v0.8.22 — 2026-05-28
|
## v0.8.22 — 2026-05-28
|
||||||
|
|
||||||
Interface **`document`** tool follow-on (`lib/nc-document.sh`, `inbound-systems.tsv`,
|
Interface **`document`** tool follow-on (`lib/nc-document.sh`, `inbound-systems.tsv`,
|
||||||
|
|||||||
8
MANIFEST
8
MANIFEST
@ -23,16 +23,16 @@
|
|||||||
# scripts/make-manifest.sh and bump VERSION.
|
# scripts/make-manifest.sh and bump VERSION.
|
||||||
|
|
||||||
# Top-level scripts
|
# Top-level scripts
|
||||||
larry.sh fd6c46db5dd8872d2fabe7f7776a5d8e672d4448c77bd2ed6646931da93ed92e
|
larry.sh 57a96b78c7ab319537b2203a7363454ebcd482ed048d63dc05668fad3c292ee7
|
||||||
larry-tunnel.sh 6b050e4eeab15669f4858eaf3b807f168f211ced07815db9521bc40a093f6aaa
|
larry-tunnel.sh 6b050e4eeab15669f4858eaf3b807f168f211ced07815db9521bc40a093f6aaa
|
||||||
larry-auth.sh a220cdf7878569dc3028951ee57fc8d5e706a8ca5c6aa45347b58facb386f831
|
larry-auth.sh a220cdf7878569dc3028951ee57fc8d5e706a8ca5c6aa45347b58facb386f831
|
||||||
larry-rollback.sh 91b5e9aa6c79266bf306dcfba4ca791c07971bd6924d67a779037531648aa6d0
|
larry-rollback.sh 91b5e9aa6c79266bf306dcfba4ca791c07971bd6924d67a779037531648aa6d0
|
||||||
install-larry.sh fa36e23a39eacbd0d7ecedd3b42131902f816ee7e98241dfc6e28c6e4ba80423
|
install-larry.sh fa36e23a39eacbd0d7ecedd3b42131902f816ee7e98241dfc6e28c6e4ba80423
|
||||||
|
|
||||||
# Metadata
|
# Metadata
|
||||||
VERSION 86456bcc629d981e2e34d7fd53096f0dba9690460593b3b84583be05f3fd544e
|
VERSION 189191ed6bf46fb5d0b7f887c60f28c097b6b4df83273227744f0510b00d89db
|
||||||
MANUAL.md c64bd0251a51ad150508b4e1185355bc4826a64071d4de339f92ed550dbfacde
|
MANUAL.md c64bd0251a51ad150508b4e1185355bc4826a64071d4de339f92ed550dbfacde
|
||||||
CHANGELOG.md a09d2ad791bcb7eafe6a181191dd9b10e10d12f3887e8dda4d034dbd23f92e4a
|
CHANGELOG.md 646791fb1a6f99c326869c57e17cb1662827afa566e68d91fe580ed68d0f02df
|
||||||
|
|
||||||
# Agent personas (system-prompt overlays)
|
# Agent personas (system-prompt overlays)
|
||||||
agents/larry.md 0a1ef737e7fc133ab35be09f79c3a4df33de814e0404b69b950932d0c8a01be1
|
agents/larry.md 0a1ef737e7fc133ab35be09f79c3a4df33de814e0404b69b950932d0c8a01be1
|
||||||
@ -106,4 +106,4 @@ lib/nc-document.sh e0b5c5b0a778abff2f09377cd1692ba445140e7da84aa8a96a002081f31b8
|
|||||||
lib/nc-diff-interface.sh 6b64ec3070a3d75d1d79632c9aeb357177fdbcc77c474aa78e6f6929fda1a324
|
lib/nc-diff-interface.sh 6b64ec3070a3d75d1d79632c9aeb357177fdbcc77c474aa78e6f6929fda1a324
|
||||||
lib/nc-find.sh 8c79e0acad7de56e4e1f12d61e071a4b98c4e2310a1f7fb183697df521215e3f
|
lib/nc-find.sh 8c79e0acad7de56e4e1f12d61e071a4b98c4e2310a1f7fb183697df521215e3f
|
||||||
lib/nc-insert-protocol.sh ad1fa0bafbf4fdfb12bad20f9c22c3eed519f8846774331e26aa9becd6f8898a
|
lib/nc-insert-protocol.sh ad1fa0bafbf4fdfb12bad20f9c22c3eed519f8846774331e26aa9becd6f8898a
|
||||||
lib/nc-regression.sh b3583fb07cbf46518312613401acb1e5b07bd2d81a4d259a297b47342182b403
|
lib/nc-regression.sh 70999a60608439f7bf1a3abb9f5e9854b5ea03025ef29ddbca683896346d1bce
|
||||||
|
|||||||
2
larry.sh
2
larry.sh
@ -78,7 +78,7 @@ set -o pipefail
|
|||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
# Config
|
# Config
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
LARRY_VERSION="0.8.22"
|
LARRY_VERSION="0.8.23"
|
||||||
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
LARRY_HOME="${LARRY_HOME:-$HOME/.larry}"
|
||||||
|
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@ -43,6 +43,55 @@
|
|||||||
# threads:N1,N2,N3 comma-separated list
|
# threads:N1,N2,N3 comma-separated list
|
||||||
# site every inbound in the configured site
|
# site every inbound in the configured site
|
||||||
# server every inbound in every site under HCIROOT
|
# server every inbound in every site under HCIROOT
|
||||||
|
#
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# CHAIN-WALK MODE (v0.8.23, Bryan's REGRESSION CHAIN-WALK route-test capture)
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# A second, orthogonal entry point that captures the route_test output at EVERY
|
||||||
|
# routing(inbound) thread along an nc-paths chain, single-env (per-env capture).
|
||||||
|
# Bryan runs it on env-A and env-B with the same START input, then the existing
|
||||||
|
# diff phase (hl7-diff --ignore MSH.7) compares the captured outputs.
|
||||||
|
#
|
||||||
|
# nc-regression.sh --chain-walk
|
||||||
|
# --start <site/thread | thread> [--site SITE]
|
||||||
|
# --count N
|
||||||
|
# --hciroot HCIROOT # single env (this box)
|
||||||
|
# --out DIR
|
||||||
|
# [--route-test-bin hciroutetest] # default: hciroutetest
|
||||||
|
# [--target <site/thread>] # restrict to the chain ending here
|
||||||
|
# [--dry-run] # generate cmds, do NOT exec engine
|
||||||
|
#
|
||||||
|
# Workflow (Bryan's spec):
|
||||||
|
# 1. Resolve the downstream chain(s) from START via nc-paths (--format jsonl).
|
||||||
|
# Each chain is "site/thread --> ... ==> ... --> site/thread". --target
|
||||||
|
# restricts to the single chain whose terminus matches.
|
||||||
|
# 2. Grab the N most-recent messages from the START inbound's SMAT (nc-msgs
|
||||||
|
# --limit N --format raw) → the chain-walk's ORIGINAL INPUT .msgs file.
|
||||||
|
# 3. Walk the chain. The route_test ENTRY threads are the START node plus every
|
||||||
|
# node that immediately follows a cross-site "==>" hop (the remote inbound).
|
||||||
|
# At each entry thread E:
|
||||||
|
# hciroutetest -a -d -f nl -s <out_base> <E> <input>
|
||||||
|
# route_test writes ONE FILE PER DESTINATION named <out_base>.out.<DEST>
|
||||||
|
# (the OUTBOUND/dest thread is the suffix; -f nl gives NEWLINE output, so no
|
||||||
|
# manual len2nl+delete). We capture ALL fan-out branches.
|
||||||
|
# 4. File SELECTION for the next step: the node IMMEDIATELY AFTER E in the path
|
||||||
|
# is the suffix to select. For E -->(intra) X, select .out.<X>. For the
|
||||||
|
# cross-site boundary S ==> R (S = E's local outbound sender, the path node
|
||||||
|
# right after E), the selected .out.<S> payload is the INPUT fed to the
|
||||||
|
# next site's inbound R. Continue to the chain terminus.
|
||||||
|
# PRODUCES (under $OUT/chain/<chain-id>/):
|
||||||
|
# input.msgs — the original START messages
|
||||||
|
# step-NN.<E>.out.<DEST> — every per-destination route_test output
|
||||||
|
# step-NN.<E>.selected → next input — the file chosen to feed the next step
|
||||||
|
# commands.sh — the exact generated command sequence
|
||||||
|
# chain.txt — the resolved v1 chain line(s)
|
||||||
|
#
|
||||||
|
# hciroutetest is a Linux engine binary needing a LIVE engine — NOT runnable off
|
||||||
|
# the server. So chain-walk GENERATES the orchestration + the exact command
|
||||||
|
# STRINGS + the file-chaining/selection, and (with --dry-run) stubs the engine
|
||||||
|
# call. Real execution is on Bryan's server. The proven command syntax is mined
|
||||||
|
# verbatim from the v1/v2 wrappers (route_test_wrapper.py:10/149-156 and
|
||||||
|
# legacy_workflow_commands.py:1015 .out.<DEST> naming).
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
NC_SELF="$0"
|
NC_SELF="$0"
|
||||||
@ -50,6 +99,7 @@ LIB_DIR="$(cd "$(dirname "$NC_SELF")" && pwd)"
|
|||||||
NCP="$LIB_DIR/nc-parse.sh"
|
NCP="$LIB_DIR/nc-parse.sh"
|
||||||
NCI="$LIB_DIR/nc-inbound.sh"
|
NCI="$LIB_DIR/nc-inbound.sh"
|
||||||
NCM="$LIB_DIR/nc-msgs.sh"
|
NCM="$LIB_DIR/nc-msgs.sh"
|
||||||
|
NCPATHS="$LIB_DIR/nc-paths.sh"
|
||||||
HL7DIFF="$LIB_DIR/hl7-diff.sh"
|
HL7DIFF="$LIB_DIR/hl7-diff.sh"
|
||||||
|
|
||||||
# v0.7.5: shared CR-safety primitives (coerce_int). The phase loops use
|
# v0.7.5: shared CR-safety primitives (coerce_int). The phase loops use
|
||||||
@ -87,9 +137,22 @@ BUNDLE_IN="" # at start, untar a bundle here as the env-A artifacts
|
|||||||
# run locally because all the artifacts live in $OUT (local).
|
# run locally because all the artifacts live in $OUT (local).
|
||||||
SOURCE_SSH_ALIAS=""
|
SOURCE_SSH_ALIAS=""
|
||||||
TARGET_SSH_ALIAS=""
|
TARGET_SSH_ALIAS=""
|
||||||
|
# v0.8.23: chain-walk mode (single-env, per-env route_test capture along a chain)
|
||||||
|
CHAIN_WALK=0
|
||||||
|
START=""
|
||||||
|
HCIROOT="" # single-env HCIROOT for chain-walk (alias for --env-a in this mode)
|
||||||
|
SITE="" # optional explicit site for the START thread
|
||||||
|
TARGET="" # optional: restrict to the chain whose terminus is this node
|
||||||
|
ROUTE_TEST_BIN="hciroutetest"
|
||||||
|
|
||||||
while [ $# -gt 0 ]; do
|
while [ $# -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
|
--chain-walk) CHAIN_WALK=1 ;;
|
||||||
|
--start) shift; START="$1" ;;
|
||||||
|
--hciroot) shift; HCIROOT="$1" ;;
|
||||||
|
--site) shift; SITE="$1" ;;
|
||||||
|
--target) shift; TARGET="$1" ;;
|
||||||
|
--route-test-bin) shift; ROUTE_TEST_BIN="$1" ;;
|
||||||
--scope) shift; SCOPE="$1" ;;
|
--scope) shift; SCOPE="$1" ;;
|
||||||
--count) shift; COUNT="$1" ;;
|
--count) shift; COUNT="$1" ;;
|
||||||
--env-a) shift; ENV_A="$1" ;;
|
--env-a) shift; ENV_A="$1" ;;
|
||||||
@ -109,7 +172,7 @@ while [ $# -gt 0 ]; do
|
|||||||
--bundle-in) shift; BUNDLE_IN="$1" ;;
|
--bundle-in) shift; BUNDLE_IN="$1" ;;
|
||||||
--source-ssh-alias) shift; SOURCE_SSH_ALIAS="$1" ;;
|
--source-ssh-alias) shift; SOURCE_SSH_ALIAS="$1" ;;
|
||||||
--target-ssh-alias) shift; TARGET_SSH_ALIAS="$1" ;;
|
--target-ssh-alias) shift; TARGET_SSH_ALIAS="$1" ;;
|
||||||
-h|--help) sed -n '2,55p' "$NC_SELF"; exit 0 ;;
|
-h|--help) sed -n '2,94p' "$NC_SELF"; exit 0 ;;
|
||||||
-*) die "unknown flag: $1" ;;
|
-*) die "unknown flag: $1" ;;
|
||||||
*) die "extra arg: $1" ;;
|
*) die "extra arg: $1" ;;
|
||||||
esac
|
esac
|
||||||
@ -133,6 +196,210 @@ _run_on() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
# CHAIN-WALK MODE (v0.8.23) — single-env route_test capture along an nc-paths
|
||||||
|
# chain. Orthogonal to the phase pipeline above; runs and exits here.
|
||||||
|
# ═════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
# Normalize a node to its bare thread name (strip a leading "site/" if present).
|
||||||
|
cw_thread_of() { printf '%s' "${1##*/}"; }
|
||||||
|
# Site of a "site/thread" node (empty if no slash).
|
||||||
|
cw_site_of() { case "$1" in */*) printf '%s' "${1%%/*}" ;; *) printf '' ;; esac; }
|
||||||
|
|
||||||
|
# Parse ONE v1 chain line into two parallel newline lists on fd: NODES and ARROWS.
|
||||||
|
# Emits, per line: "N\t<node>" for each node and "A\t<arrow>" for each arrow
|
||||||
|
# ("-->" intra-site, "==>" cross-site), in left-to-right order. We tokenize by
|
||||||
|
# turning the two arrow tokens into sentinels we can split on without regex pain.
|
||||||
|
cw_parse_chain() {
|
||||||
|
local line="$1"
|
||||||
|
# Insert record separators around each arrow token, then read field-by-field.
|
||||||
|
# Use awk for portable tokenization (no bash regex, Win+Linux safe).
|
||||||
|
printf '%s\n' "$line" | awk '
|
||||||
|
{
|
||||||
|
n=0
|
||||||
|
# split on whitespace; arrows are standalone tokens "-->" or "==>"
|
||||||
|
cnt=split($0, tok, /[ \t]+/)
|
||||||
|
for (i=1;i<=cnt;i++) {
|
||||||
|
t=tok[i]
|
||||||
|
if (t=="-->") { print "A\t-->" }
|
||||||
|
else if (t=="==>") { print "A\t==>" }
|
||||||
|
else if (t!="") { print "N\t" t }
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Given the parsed NODES (array) and ARROWS (array, ARROWS[i] is between
|
||||||
|
# NODES[i] and NODES[i+1]), determine the route_test ENTRY indices: the START
|
||||||
|
# (index 0) plus every node that immediately FOLLOWS a "==>" arrow (a remote
|
||||||
|
# inbound after a cross-site hop). Echo the entry indices, one per line.
|
||||||
|
cw_entry_indices() {
|
||||||
|
# args: arrow tokens as "$@" (ARROWS[0..n-2]); NODES count = #arrows+1
|
||||||
|
local i=0
|
||||||
|
echo 0 # START is always a route_test entry
|
||||||
|
for a in "$@"; do
|
||||||
|
if [ "$a" = "==>" ]; then
|
||||||
|
echo $((i+1)) # node right after a cross-site hop is an entry
|
||||||
|
fi
|
||||||
|
i=$((i+1))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
chain_walk() {
|
||||||
|
[ -n "$START" ] || die "chain-walk: missing --start <site/thread | thread>"
|
||||||
|
[ -n "$HCIROOT" ] || die "chain-walk: missing --hciroot (single-env root)"
|
||||||
|
[ -n "$OUT" ] || die "chain-walk: missing --out DIR"
|
||||||
|
[ -x "$NCPATHS" ] || die "chain-walk: nc-paths.sh not found/executable at $NCPATHS"
|
||||||
|
[ -x "$NCM" ] || die "chain-walk: nc-msgs.sh not found/executable at $NCM"
|
||||||
|
|
||||||
|
mkdir -p "$OUT" 2>/dev/null
|
||||||
|
|
||||||
|
# 1. Resolve downstream chains from START.
|
||||||
|
say "=== CHAIN-WALK: resolve downstream chains from $START ==="
|
||||||
|
local jsonl
|
||||||
|
jsonl=$("$NCPATHS" "$START" --downstream --hciroot "$HCIROOT" --format jsonl 2>/dev/null)
|
||||||
|
[ -n "$jsonl" ] || die "chain-walk: nc-paths returned no downstream chains for $START"
|
||||||
|
|
||||||
|
# Extract the v1 path strings (one per JSON line). Pure-awk pull of "path":"…".
|
||||||
|
local paths
|
||||||
|
paths=$(printf '%s\n' "$jsonl" | awk -F'"path":"' 'NF>1 { p=$2; sub(/".*/,"",p); print p }')
|
||||||
|
[ -n "$paths" ] || die "chain-walk: could not extract path strings from nc-paths jsonl"
|
||||||
|
|
||||||
|
# If --target given, keep only the chain whose terminus matches it (bare-thread
|
||||||
|
# match, so caller can pass either site/thread or just the thread name).
|
||||||
|
if [ -n "$TARGET" ]; then
|
||||||
|
local tgt; tgt=$(cw_thread_of "$TARGET")
|
||||||
|
paths=$(printf '%s\n' "$paths" | awk -v t="$tgt" '
|
||||||
|
{ n=split($0,a,/[ \t]+/); last=a[n]; sub(/^.*\//,"",last); if (last==t) print }')
|
||||||
|
[ -n "$paths" ] || die "chain-walk: --target $TARGET matched no chain terminus from $START"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local nchains; nchains=$(printf '%s\n' "$paths" | grep -c .)
|
||||||
|
say "resolved $nchains chain(s) to walk"
|
||||||
|
|
||||||
|
# The START inbound's bare thread name + its site (for the message grab).
|
||||||
|
local start_thread start_site
|
||||||
|
start_thread=$(cw_thread_of "$START")
|
||||||
|
start_site=$(cw_site_of "$START")
|
||||||
|
[ -n "$start_site" ] && [ -z "$SITE" ] && SITE="$start_site"
|
||||||
|
|
||||||
|
# 2. Generate the message-grab for the START inbound's SMAT (nc-msgs).
|
||||||
|
# Bryan: grab the N most-recent from the START inbound. We point nc-msgs at
|
||||||
|
# the START site dir; it locates <thread>.smatdb and emits raw (0x1c-sep).
|
||||||
|
local start_sitedir
|
||||||
|
if [ -n "$SITE" ]; then
|
||||||
|
start_sitedir="$HCIROOT/$SITE"
|
||||||
|
else
|
||||||
|
start_sitedir=$(find "$HCIROOT" -maxdepth 5 -name "${start_thread}.smatdb" -type f 2>/dev/null \
|
||||||
|
| head -1 | sed 's#/exec/processes/.*##')
|
||||||
|
fi
|
||||||
|
local grab_cmd="HCISITEDIR='$start_sitedir' '$NCM' '$start_thread' --limit $COUNT --format raw"
|
||||||
|
|
||||||
|
local chain_id=0 chain_line
|
||||||
|
while IFS= read -r chain_line; do
|
||||||
|
[ -z "$chain_line" ] && continue
|
||||||
|
chain_id=$((chain_id+1))
|
||||||
|
local cdir; cdir="$OUT/chain/$(printf '%02d' "$chain_id")"
|
||||||
|
mkdir -p "$cdir" 2>/dev/null
|
||||||
|
printf '%s\n' "$chain_line" > "$cdir/chain.txt"
|
||||||
|
|
||||||
|
# Parse into NODES[] / ARROWS[].
|
||||||
|
local NODES=() ARROWS=()
|
||||||
|
local rec
|
||||||
|
while IFS=$'\t' read -r tag val; do
|
||||||
|
case "$tag" in
|
||||||
|
N) NODES+=("$val") ;;
|
||||||
|
A) ARROWS+=("$val") ;;
|
||||||
|
esac
|
||||||
|
done < <(cw_parse_chain "$chain_line")
|
||||||
|
|
||||||
|
local nnodes=${#NODES[@]}
|
||||||
|
say "--- chain $chain_id ($nnodes nodes): $chain_line ---"
|
||||||
|
|
||||||
|
# Determine route_test entry indices.
|
||||||
|
local entries; entries=$(cw_entry_indices "${ARROWS[@]}")
|
||||||
|
|
||||||
|
# 3. Walk. The first entry's input is the START messages; thereafter each
|
||||||
|
# step's input is the file SELECTED from the prior route_test.
|
||||||
|
local cmds="$cdir/commands.sh"
|
||||||
|
{
|
||||||
|
printf '#!/usr/bin/env bash\n'
|
||||||
|
printf '# GENERATED by nc-regression.sh --chain-walk (v0.8.23). Run ON THE ENGINE BOX.\n'
|
||||||
|
printf '# Chain: %s\n' "$chain_line"
|
||||||
|
printf '# Source the Cloverleaf profile so HCIROOT/HCISITE + engine libs are set:\n'
|
||||||
|
printf '# export HCIROOT=%s ; cd "$HCIROOT" ; . ./.profile # or your shop pattern\n' "$HCIROOT"
|
||||||
|
printf 'set -e\n\n'
|
||||||
|
printf '# --- step 0: grab the %s most-recent messages from the START inbound SMAT ---\n' "$COUNT"
|
||||||
|
printf '%s > %s\n\n' "$grab_cmd" "$cdir/input.msgs"
|
||||||
|
} > "$cmds"
|
||||||
|
|
||||||
|
local step=0 prev_input="$cdir/input.msgs"
|
||||||
|
local e
|
||||||
|
while IFS= read -r e; do
|
||||||
|
[ -z "$e" ] && continue
|
||||||
|
local node="${NODES[$e]}"
|
||||||
|
local ethread; ethread=$(cw_thread_of "$node")
|
||||||
|
local esite; esite=$(cw_site_of "$node")
|
||||||
|
# The node IMMEDIATELY AFTER E is the DEST suffix to SELECT for the next step.
|
||||||
|
local nexti=$((e+1))
|
||||||
|
local select_suffix=""
|
||||||
|
if [ "$nexti" -lt "$nnodes" ]; then
|
||||||
|
select_suffix=$(cw_thread_of "${NODES[$nexti]}")
|
||||||
|
fi
|
||||||
|
local out_base="$cdir/step-$(printf '%02d' "$step").${ethread}"
|
||||||
|
# The exact, mined-verbatim hciroutetest command (route_test_wrapper.py:149-156):
|
||||||
|
# hciroutetest -a -d -f nl -s <out_base> <source_thread> <input>
|
||||||
|
# -f nl → NEWLINE output (no manual len2nl+delete). HCISITE per the entry's site.
|
||||||
|
local rt_cmd="HCISITE='${esite:-$SITE}' '$ROUTE_TEST_BIN' -a -d -f nl -s '$out_base' '$ethread' '$prev_input'"
|
||||||
|
|
||||||
|
{
|
||||||
|
printf '# --- step %s: route_test at routing(inbound) thread %s%s ---\n' \
|
||||||
|
"$step" "$ethread" "$([ -n "$esite" ] && printf ' (site=%s)' "$esite")"
|
||||||
|
printf '%s\n' "$rt_cmd"
|
||||||
|
if [ -n "$select_suffix" ]; then
|
||||||
|
printf '# select the per-destination output that feeds the NEXT step:\n'
|
||||||
|
printf '# route_test wrote one file per DEST as %s.out.<DEST>\n' "$out_base"
|
||||||
|
printf '# the next node in the chain is %s → select %s.out.%s\n' \
|
||||||
|
"$select_suffix" "$out_base" "$select_suffix"
|
||||||
|
printf 'cp "%s.out.%s" "%s.selected"\n\n' "$out_base" "$select_suffix" "$out_base"
|
||||||
|
else
|
||||||
|
printf '# chain terminus — no further selection; %s.out.* are the final outputs.\n\n' "$out_base"
|
||||||
|
fi
|
||||||
|
} >> "$cmds"
|
||||||
|
|
||||||
|
# SELF-VERIFY echo (stderr): the generated command + selection for this step.
|
||||||
|
say " step $step ENTRY=$ethread${esite:+ (site=$esite)}"
|
||||||
|
say " route_test : $rt_cmd"
|
||||||
|
if [ -n "$select_suffix" ]; then
|
||||||
|
say " select : ${out_base##*/}.out.$select_suffix → feeds next step"
|
||||||
|
else
|
||||||
|
say " select : (terminus) ${out_base##*/}.out.* are final"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --dry-run: stub the engine call so the chaining/selection is exercised
|
||||||
|
# without hciroutetest. We DO NOT run the real binary here (Bryan's box does).
|
||||||
|
if [ "$DRY_RUN" = "1" ]; then
|
||||||
|
: # generation-only; nothing executed
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The selected file becomes the next step's input (for the NEXT entry).
|
||||||
|
if [ -n "$select_suffix" ]; then
|
||||||
|
prev_input="${out_base}.out.${select_suffix}"
|
||||||
|
fi
|
||||||
|
step=$((step+1))
|
||||||
|
done <<< "$entries"
|
||||||
|
|
||||||
|
say " → generated: $cmds"
|
||||||
|
done <<< "$paths"
|
||||||
|
|
||||||
|
say "chain-walk done. Per-chain artifacts under: $OUT/chain/"
|
||||||
|
say "Run each chain/NN/commands.sh ON THE ENGINE BOX (profile sourced) to capture outputs."
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$CHAIN_WALK" = "1" ]; then
|
||||||
|
chain_walk
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
[ -n "$OUT" ] || die "missing --out DIR"
|
[ -n "$OUT" ] || die "missing --out DIR"
|
||||||
# When --bundle-in is given, we don't need scope/env-a/etc. — the bundle has them.
|
# When --bundle-in is given, we don't need scope/env-a/etc. — the bundle has them.
|
||||||
if [ -z "$BUNDLE_IN" ]; then
|
if [ -z "$BUNDLE_IN" ]; then
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user