From 3dcefe9fb3d697e6bd15b42b2d06139f72bffd30 Mon Sep 17 00:00:00 2001 From: Brian McMahon Date: Thu, 21 May 2026 17:08:40 -0700 Subject: [PATCH] =?UTF-8?q?fix(scripts):=20salience=5Fphase0.sh=20?= =?UTF-8?q?=E2=80=94=20flyctl=20ssh=20-C=20is=20exec,=20not=20shell?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs in cmd_snapshot caught on first real run: 1. `flyctl ssh -C "cmd && echo done"` doesn't interpret `&&` as a shell operator. flyctl execs the command directly (no `sh -c` wrapper), so `&&` and following tokens get passed as literal args to the first command. cp ended up with: cp /data/default.vec.npz /tmp/snap.vec.npz "vec copy done" …and tried to use "vec copy done" as a destination directory, which doesn't exist → error. Fix: drop the `&& echo X` confirmation. Single command only. If chaining is needed in future, wrap in `sh -c "..."` explicitly. 2. Error handling pattern was wrong — `flyctl ... || echo_warn` followed by an unconditional `echo_ok` printed BOTH the warning AND the success line on failure: ⚠ remote vecstore copy failed — embedding signals will be zero ✓ remote /tmp/snap.vec.npz written ← false! Fix: switch from `cmd || warn` (followed by unconditional ok) to `if cmd; then ok; else warn; fi`. Applied to both Step 2 (vec copy) and Step 4 (remote cleanup) — same pattern bug. After this fix, re-running `scripts/salience_phase0.sh snapshot` will: - Step 2: cp succeeds cleanly, vec.npz lands in /tmp/snap.vec.npz - Step 3: sftp pulls both files - Result: snapshot has Vectors: ~2433 (matching live memories) Then `scripts/salience_phase0.sh score` will use the embedding signals (constraint_score, time_penalty) as designed. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/salience_phase0.sh | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/scripts/salience_phase0.sh b/scripts/salience_phase0.sh index 94fe8fa..af4afb6 100755 --- a/scripts/salience_phase0.sh +++ b/scripts/salience_phase0.sh @@ -78,10 +78,16 @@ cmd_snapshot() { echo_ok "remote /tmp/snap.sqlite written" echo_step " Step 2/4 — vecstore copy on Fly (numpy savez is atomic)" - flyctl ssh console -a "$FLY_APP" -C \ - "cp /data/default.vec.npz /tmp/snap.vec.npz && echo 'vec copy done'" \ - || echo_warn "remote vecstore copy failed — embedding signals will be zero" - echo_ok "remote /tmp/snap.vec.npz written" + # flyctl ssh -C does NOT spawn a shell — it execs the command directly, + # so shell operators like `&& echo X` get passed as literal cp args + # (caught 2026-05-21: cp tried to use "vec copy done" as a destination). + # Single command only. If we need chaining, wrap in `sh -c "..."`. + if flyctl ssh console -a "$FLY_APP" -C \ + "cp /data/default.vec.npz /tmp/snap.vec.npz"; then + echo_ok "remote /tmp/snap.vec.npz written" + else + echo_warn "remote vecstore copy failed — embedding signals will be zero" + fi echo_step " Step 3/4 — sftp download (sqlite + vec.npz)" rm -f "$SNAPSHOT_PATH" "$VEC_PATH" @@ -100,9 +106,11 @@ cmd_snapshot() { fi echo_step " Step 4/4 — clean up remote temp files" - flyctl ssh console -a "$FLY_APP" -C "rm -f /tmp/snap.sqlite /tmp/snap.vec.npz" \ - || echo_warn "remote cleanup failed (harmless — /tmp churns on machine restart)" - echo_ok "remote /tmp files removed" + if flyctl ssh console -a "$FLY_APP" -C "rm -f /tmp/snap.sqlite /tmp/snap.vec.npz"; then + echo_ok "remote /tmp files removed" + else + echo_warn "remote cleanup failed (harmless — /tmp churns on machine restart)" + fi # Quick sanity: count live memories + verify vec.npz is loadable local live_count vec_count