testsAndMisc/docs/superpowers/evidence/analyze-repo-runtime-fix-2026-08-16.json
Krzysztof kuhy Rudnicki 936e0ec70d Fix two runtime defects the analyze_repo.sh split introduced
41cc045 passed bash -n, shellcheck, a function-set diff, a line-set diff
and a stubbed source run -- and still shipped a script that aborted after
language detection. Both bugs needed a real execution to see.

1. set -e. The eight `((LANG_FILES[x] > 0)) && HAS_Y=true` lines were
   top-level statements, where a false `&&` merely left $? non-zero. As
   the tail of detect_languages they became its return value, and the
   last one (java, false for most repos) killed the script. Now `|| true`.

2. Self-referencing nameref. `local -n LANG_CODE_FILES="$1"` pointing at
   a global of the same name makes bash 5.3 warn "circular name
   reference" on every access -- into this script's own output. The
   locals are now _code_files.

The rename then hid the array from shellcheck (SC2034), since it was only
named as a bare word. Fixed by passing it through CODE_FILES_MAP so the
reference is real, not by suppressing.

Verified against 133fccf end to end on a fixture repo: same file set,
every text artifact byte-identical, exit 0 both sides, zero warnings.
Only cscope.out.in differs, in binary padding -- same input list, same
90112 bytes.

The lesson for the recipe doc: a stubbed run sources the libs and never
calls them, so it cannot reach a bug in a seam that passes state.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY
2026-08-16 22:02:17 +02:00

51 lines
4.7 KiB
JSON

{
"intent": "Fix two runtime defects the analyze_repo.sh split introduced in 41cc045, both of which every static check passed clean, and verify the fix by running the script end to end against the pre-split original.",
"scope": [
"linux_configuration/scripts/single_use/utils/analyze_repo.sh",
"linux_configuration/scripts/single_use/utils/lib/analyze_repo_separate.sh",
"linux_configuration/scripts/single_use/utils/lib/analyze_repo_report.sh",
"Non-goal: any further restructuring; the seam from 41cc045 is unchanged"
],
"changes": [
"Renamed the nameref locals from LANG_CODE_FILES to _code_files in both libs. A nameref whose local name equals the global it points at is a self-reference: bash 5.3 still moves the data, but warns 'circular name reference' on every single access, and those warnings land in this script's user-facing output.",
"Replaced the entry's bare `declare -A LANG_CODE_FILES` plus two literal call arguments with a CODE_FILES_MAP variable holding the name. After the rename shellcheck could no longer see the array as used (SC2034), because it was only ever mentioned as a bare word; passing it through a variable makes the reference real rather than suppressing the warning.",
"Added `|| true` to the eight `((LANG_FILES[x] > 0)) && HAS_Y=true` lines in detect_languages. These were top-level statements before the split, where a false `((...)) &&` merely left $? non-zero. As the tail of a function they become its return value, and under `set -e` the last one -- java, false for most repositories -- aborted the entire script immediately after language detection."
],
"verification": [
{
"command": "bash <stubbed entry> <fixture repo> <outdir>, against the same run from a detached worktree at 133fccf (pre-split)",
"result": "pass",
"evidence": "Pre-split exits 0 and completes. The split as committed in 41cc045 exited 1 immediately after 'Files found by language', producing none of STEPs 5-8. After the fix it exits 0 and reaches the final summary, with zero 'circular name reference' warnings (was one per map access)."
},
{
"command": "diff of every generated artifact, pre-split run vs fixed run",
"result": "pass",
"evidence": "Identical file set, and every text artifact byte-identical: tokei_stats, scc_stats, scc_complexity, code_identifiers, comment_words, grep_keywords, grep_function_calls, grep_imports, the per_language/* files, tags, ctags_symbols, ctags_kinds and cscope.files. Only cscope.out.in differs, in binary hash-table padding; its input list cscope.files is identical and both indexes are the same 90112 bytes."
},
{
"command": "shellcheck analyze_repo.sh lib/analyze_repo_*.sh",
"result": "pass",
"evidence": "Zero findings, no suppression added. The SC2034 introduced by the rename was fixed by making the reference real, not by silencing it."
},
{
"command": "audit of every wrapped function's last statement for the same set -e hazard",
"result": "pass",
"evidence": "detect_languages was the only one ending in a conditional. analyze_symbols and analyze_cscope end in 'fi', analyze_clang_ast and separate_code_and_comments in echo, report_languages in a command already carrying '|| true'. Confirmed by the end-to-end run reaching the final summary."
},
{
"command": "bash ~/utils/scripts/check_file_length.sh",
"result": "pass",
"evidence": "entry 234, report 228, separate 193, symbols 112, tools 124, scan 86. All six still under the cap after the added lines."
}
],
"risks": [
"Both defects were invisible to bash -n, shellcheck, the function-set diff, the line-set diff and the stubbed source run -- that last one sources the libs but never calls the functions, so it could not reach either bug. The general lesson is that a stubbed run proves the source lines resolve and nothing more; only a real execution exercises a seam that passes state.",
"The fixture repository used for the comparison contains one Python and one C file, so the java/ruby/go/rust/shell branches of separate_code_and_comments were not executed. They are structurally identical to the two that were, and the line-set diff shows they differ from the original only by the mechanical rename.",
"install_missing_tools was stubbed for both runs, so the package-installation path remains unexecuted under decision 6. It is unchanged by this commit."
],
"rollback": [
"git revert this commit to return to 41cc045's state -- but note that state is the broken one: the script aborts after language detection. A rollback should go to 133fccf, reverting the split itself.",
"After rollback, re-run the script against a small fixture repo and confirm it exits 0 and prints the STEP 8 summary."
]
}