From f4d45ce60fd30dc9eb79dfa44683ca0df05284b4 Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Sun, 16 Aug 2026 22:49:48 +0200 Subject: [PATCH] Add an execution-trace harness for shell splits verify_shell_split.sh sources the libs and never calls them, which is why analyze_repo.sh shipped broken after passing every static check. This runs the script for real with ~30 mutating binaries (sudo, pacman, systemctl, adb, nft, mount, reboot...) shadowed by PATH stubs that record their calls and exit 0, then prints a diffable trace: exit status, stubbed calls in order, stdout, stderr. Verified against both bugs the handoff documents: it catches the `set -e` function-tail abort (exit 1, later calls absent from the trace) and the self-referencing nameref (exit 0, correct stdout, `circular name reference` in stderr). `systemctl is-active foo` stayed inactive across all three runs, so nothing reached the live system. This is what lets Decision 6 ("never execute an enforcement script") and the user's "run-and-diff always" coexist. Handoff updated with both, plus the win32_window.cpp resolution: it is tracked and listed in CMakeLists.txt, so gitignore cannot work -- the exemption belongs in ~/utils/file_length/config.py. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY --- .../trace-shell-split-harness-2026-08-16.json | 47 +++++ meta/scripts/trace_shell_split.sh | 163 ++++++++++++++++++ refactor_claude_todo_resume.md | 54 +++++- 3 files changed, 259 insertions(+), 5 deletions(-) create mode 100644 docs/superpowers/evidence/trace-shell-split-harness-2026-08-16.json create mode 100755 meta/scripts/trace_shell_split.sh diff --git a/docs/superpowers/evidence/trace-shell-split-harness-2026-08-16.json b/docs/superpowers/evidence/trace-shell-split-harness-2026-08-16.json new file mode 100644 index 00000000..bb1d825e --- /dev/null +++ b/docs/superpowers/evidence/trace-shell-split-harness-2026-08-16.json @@ -0,0 +1,47 @@ +{ + "intent": "Add an execution-trace harness so shell splits can be verified by running the script with mutating commands stubbed, closing the gap that let a green-but-broken split ship.", + "scope": [ + "meta/scripts/trace_shell_split.sh (new)", + "Non-goal: does not replace verify_shell_split.sh; that still covers syntax, lint and source-line resolution" + ], + "changes": [ + "New harness that shadows 30 mutating binaries (sudo, pacman, systemctl, adb, nft, mount, mkinitcpio, reboot...) with PATH stubs that record their invocation and exit 0, runs the target script, and prints a diffable trace: exit status, stubbed calls in order with arguments, stdout, stderr.", + "Trace is the comparison artifact: capture at the pre-split commit and after the split, then diff." + ], + "verification": [ + { + "command": "trace_shell_split.sh on a fixture reproducing the `set -e` function-tail bug", + "result": "pass (bug detected)", + "evidence": "exit status 1, empty stdout, and the later `sudo systemctl restart foo` absent from the trace -- the script died silently at the false conditional tail. This is the analyze_repo.sh failure that bash -n, shellcheck, a function-set diff and the stubbed probe all passed." + }, + { + "command": "trace_shell_split.sh on the same fixture with `|| true` appended", + "result": "pass", + "evidence": "exit status 0, trace records 'sudo systemctl restart foo', stdout shows 'detected: py=true' and 'done'. The two traces differ, so the harness discriminates rather than always agreeing." + }, + { + "command": "trace_shell_split.sh on a fixture reproducing the self-referencing nameref bug", + "result": "pass (bug detected)", + "evidence": "exit status 0 and correct stdout, but stderr carries four 'circular name reference' warnings that a trace diff surfaces. This bug is invisible to exit-status checking alone." + }, + { + "command": "systemctl is-active foo", + "result": "pass", + "evidence": "inactive -- the stub absorbed the call; the live system was not touched by any of the three runs." + }, + { + "command": "shellcheck + shfmt -d on meta/scripts/trace_shell_split.sh", + "result": "pass", + "evidence": "Both clean. SC2155 fixed by splitting declare from assign rather than suppressing. Banner count 2 before and 2 after shfmt -w (that formatter is known to delete banners). Re-ran both fixtures after formatting and behaviour was unchanged." + } + ], + "risks": [ + "Stubs always exit 0, so a script branching on a real failure exit status takes a path it would not take live; such cases need a hand-written stub and a note in that split's evidence.", + "A mutating command not in STUBBED_COMMANDS runs for real. The list must be extended deliberately before tracing a script that uses something new -- check with a grep for mutating verbs first.", + "Not a substitute for reading the diff: an identical trace proves the same calls happened, not that the split is well-factored." + ], + "rollback": [ + "Delete meta/scripts/trace_shell_split.sh; no other file imports it.", + "Shell splits fall back to verify_shell_split.sh plus the by-hand checks in docs/shell-split-verification.md." + ] +} diff --git a/meta/scripts/trace_shell_split.sh b/meta/scripts/trace_shell_split.sh new file mode 100755 index 00000000..66ad491c --- /dev/null +++ b/meta/scripts/trace_shell_split.sh @@ -0,0 +1,163 @@ +#!/usr/bin/env bash + +# ============================================================================ +# Capture an execution trace of a shell script with every mutating command +# stubbed, so a split can be verified by RUNNING it rather than by sourcing it. +# +# Why this exists: verify_shell_split.sh sources the libs and never calls them, +# which proves `source` lines resolve and nothing more. The analyze_repo.sh +# split passed that, plus bash -n, shellcheck and a function-set diff, and +# still shipped a script that aborted after language detection -- a `set -e` +# function-tail return and a self-referencing nameref, neither reachable +# without execution. See docs/shell-split-verification.md. +# +# The trace is the artifact: run this at the pre-split commit and again after +# the split, then diff. An identical trace means the same commands ran in the +# same order with the same arguments. +# +# Mutating binaries are shadowed by stubs earlier on PATH, so the live system +# is never touched. That is what makes this safe for the enforcement and +# installer scripts Decision 6 forbids executing for real. +# +# Usage: +# trace_shell_split.sh