diff --git a/MANUAL.md b/MANUAL.md index bbdde9b..91f5533 100644 --- a/MANUAL.md +++ b/MANUAL.md @@ -398,6 +398,66 @@ Output: markdown report with cluster overview, per-thread protocol-block diff, p --- +## Cross-environment Larry — how the boxes communicate + +**They don't, directly.** Each Larry on each box is independent. For regression +testing across two firewalled environments, the workflow is: + +``` +env-A (Windows) env-B (Linux) + nc-regression --phase env-a → nc-regression --phase env-b + --bundle-out /tmp/reg.tar.gz --bundle-in /tmp/reg.tar.gz + │ │ + └────── you move the bundle ────────┘ + (scp, gh release, USB, + shared mount, etc.) +``` + +The `--bundle-out` flag (after env-A phases 1-3) produces a tarball with: +- `inputs/*.msgs` — sampled messages from env-A smatdbs +- `outputs/env-a/*/*.out` — env-A route_test outputs +- `manifest.json` — env-A metadata + hints +- `inbounds.txt` — the threads tested +- `README.md` — instructions for the env-B side + +Move the bundle however you can (see below). Then on env-B: + +```bash +nc-regression.sh \ + --bundle-in /path/to/reg.tar.gz \ + --out /tmp/reg \ + --env-b $HCIROOT --site-b $HCISITE \ + --route-test-cmd '' \ + --phase env-b +``` + +That runs phases 4-6 (route_test on env-B with same inputs, diff each pair, +master summary). + +### Bundle transport options (pick whichever your network allows) + +| how | when | +|---|---| +| `scp` between the boxes | both boxes mutually SSH-reachable | +| `scp` to your laptop, `scp` to env-B | you can SSH to each separately | +| Private GitHub release / gist | both boxes can reach github.com but not each other | +| Shared NFS / SMB mount | the boxes share a filesystem | +| Box / SharePoint / S3 | enterprise common-storage | +| USB stick / portable drive | nothing else works | +| Tarball as email attachment | small bundles, both boxes have email | + +The bundle is plain `tar.gz` — nothing Cloverleaf-specific. Inspect it with +`tar -tzf reg.tar.gz` to see what's inside before moving it. + +### Lessons / refinements use the same pattern + +Larry on a client captures lessons locally (`/lesson`, lesson_record tool). +You export the lesson bundle with `lib/lessons.sh export` and paste it back +to home-Larry (the dev-machine session you have with me). Same model: +local capture, manual transport, central merge. No direct peer protocol. + +--- + ## Regression testing — end to end (`lib/nc-regression.sh`) Full Example 6 orchestrator. Six phases: discover → sample → route-test A → route-test B → diff → summary. diff --git a/VERSION b/VERSION index 2b7c5ae..17b2ccd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.2 +0.4.3 diff --git a/larry.sh b/larry.sh index 28b38e0..2df2fd4 100755 --- a/larry.sh +++ b/larry.sh @@ -32,7 +32,7 @@ set -o pipefail # ───────────────────────────────────────────────────────────────────────────── # Config # ───────────────────────────────────────────────────────────────────────────── -LARRY_VERSION="0.4.2" +LARRY_VERSION="0.4.3" LARRY_HOME="${LARRY_HOME:-$HOME/.larry}" LARRY_UPDATE_URL="${LARRY_UPDATE_URL:-https://raw.githubusercontent.com/bojj27/cloverleaf-larry/main/larry.sh}" LARRY_AGENTS_URL="${LARRY_AGENTS_URL:-https://raw.githubusercontent.com/bojj27/cloverleaf-larry/main/agents}" diff --git a/lib/nc-regression.sh b/lib/nc-regression.sh index c35f611..abaa60e 100755 --- a/lib/nc-regression.sh +++ b/lib/nc-regression.sh @@ -70,6 +70,8 @@ DRY_RUN=0 INBOUND_MODE="all" ENV_B_HOST="" ENV_B_USER="" +BUNDLE_OUT="" # after env-A phases, tar up the artifacts here +BUNDLE_IN="" # at start, untar a bundle here as the env-A artifacts while [ $# -gt 0 ]; do case "$1" in @@ -88,6 +90,8 @@ while [ $# -gt 0 ]; do --inbound-mode) shift; INBOUND_MODE="$1" ;; --env-b-host) shift; ENV_B_HOST="$1" ;; --env-b-user) shift; ENV_B_USER="$1" ;; + --bundle-out) shift; BUNDLE_OUT="$1" ;; + --bundle-in) shift; BUNDLE_IN="$1" ;; -h|--help) sed -n '2,55p' "$NC_SELF"; exit 0 ;; -*) die "unknown flag: $1" ;; *) die "extra arg: $1" ;; @@ -95,16 +99,35 @@ while [ $# -gt 0 ]; do shift done -[ -n "$SCOPE" ] || die "missing --scope (thread:NAME | threads:N1,N2 | site | server)" -[ -n "$ENV_A" ] || die "missing --env-a HCIROOT_A" -[ -n "$ENV_B" ] || die "missing --env-b HCIROOT_B" [ -n "$OUT" ] || die "missing --out DIR" -[ -d "$ENV_A" ] || die "env-a is not a directory: $ENV_A" -case "$PHASE" in 1|2|3|4|5|6|all) ;; *) die "bad --phase" ;; esac +# When --bundle-in is given, we don't need scope/env-a/etc. — the bundle has them. +if [ -z "$BUNDLE_IN" ]; then + [ -n "$SCOPE" ] || die "missing --scope (thread:NAME | threads:N1,N2 | site | server)" + [ -n "$ENV_A" ] || die "missing --env-a HCIROOT_A" + [ -n "$ENV_B" ] || die "missing --env-b HCIROOT_B" + [ -d "$ENV_A" ] || die "env-a is not a directory: $ENV_A" +fi +case "$PHASE" in 1|2|3|4|5|6|all|env-a|env-b) ;; *) die "bad --phase (use 1|2|3|4|5|6|all|env-a|env-b)" ;; esac [ "$DRY_RUN" = "1" ] || [ -n "$ROUTE_TEST_CMD" ] || say "WARNING: --route-test-cmd is unset; phases 3 and 4 will be skipped (you can run them manually using the generated input files)" mkdir -p "$OUT" "$OUT/inputs" "$OUT/outputs/env-a" "$OUT/outputs/env-b" "$OUT/diff" 2>/dev/null +# If --bundle-in given, untar the bundle into $OUT first. Manifest tells us +# what env-A was and (optionally) what route_test command to use. +if [ -n "$BUNDLE_IN" ]; then + [ -f "$BUNDLE_IN" ] || die "bundle-in file not found: $BUNDLE_IN" + say "unpacking bundle $BUNDLE_IN into $OUT/" + tar -xzf "$BUNDLE_IN" -C "$OUT" 2>&1 | tail -5 + if [ -f "$OUT/manifest.json" ]; then + say "manifest from env-A:" + cat "$OUT/manifest.json" >&2 + # Pull scope and route-test-cmd hints if not overridden + if [ -z "$SCOPE" ] && command -v jq >/dev/null 2>&1; then + SCOPE=$(jq -r '.scope // ""' "$OUT/manifest.json") + fi + fi +fi + # ───────────────────────────────────────────────────────────────────────────── # Phase 1: discover inbound threads in scope # ───────────────────────────────────────────────────────────────────────────── @@ -321,12 +344,57 @@ phase_6() { # Dispatch # ───────────────────────────────────────────────────────────────────────────── case "$PHASE" in - 1) phase_1 ;; - 2) phase_1 && phase_2 ;; - 3) phase_3 ;; - 4) phase_4 ;; - 5) phase_5 ;; - 6) phase_6 ;; - all) phase_1 && phase_2 && phase_3 && phase_4 && phase_5 && phase_6 ;; + 1) phase_1 ;; + 2) phase_1 && phase_2 ;; + 3) phase_3 ;; + 4) phase_4 ;; + 5) phase_5 ;; + 6) phase_6 ;; + all) phase_1 && phase_2 && phase_3 && phase_4 && phase_5 && phase_6 ;; + env-a) phase_1 && phase_2 && phase_3 ;; # everything that uses env-A + env-b) phase_4 && phase_5 && phase_6 ;; # everything that uses env-B + diff esac + +# Optional: produce a portable bundle of the env-A artifacts (inputs + a-outputs) +# so the user can move them to the env-B box manually. +if [ -n "$BUNDLE_OUT" ]; then + say "producing bundle: $BUNDLE_OUT" + { + printf '{' + printf '"generated":"%s",' "$(date -Iseconds 2>/dev/null || date)" + printf '"host":"%s",' "$(hostname 2>/dev/null || echo unknown)" + printf '"env_a":"%s",' "$ENV_A" + printf '"site_a":"%s",' "$SITE_A" + printf '"env_b_expected":"%s",' "$ENV_B" + printf '"site_b_expected":"%s",' "$SITE_B" + printf '"scope":"%s",' "$SCOPE" + printf '"count":%s,' "$COUNT" + printf '"ignore":"%s",' "$IGNORE" + printf '"route_test_cmd_hint":"%s"' "$ROUTE_TEST_CMD" + printf '}\n' + } > "$OUT/manifest.json" + cat > "$OUT/README.md" < \\ + --route-test-cmd '' \\ + --phase env-b +\`\`\` + +The bundle contains: +- inputs/ — sampled messages from env-A (one .msgs per inbound) +- outputs/env-a/ — route_test outputs from env-A +- manifest.json — env-A metadata +- inbounds.txt — the threads tested + +env-B side will produce outputs/env-b/ and the diff/ tree. +EOF + tar -czf "$BUNDLE_OUT" -C "$OUT" inputs outputs/env-a inbounds.txt manifest.json README.md 2>/dev/null + say "bundle ready: $BUNDLE_OUT ($(du -h "$BUNDLE_OUT" | awk '{print $1}'))" +fi + say "regression run done. Output root: $OUT"