From ce139b58673192cb29aab54d9997b2e00057f9fd Mon Sep 17 00:00:00 2001 From: Krzysztof kuhy Rudnicki Date: Mon, 17 Aug 2026 15:15:25 +0200 Subject: [PATCH] extract_shell_functions: never leave a function defined twice On a script with no `set -e` anchor -- every #!/system/bin/sh phone enforcer -- the tool wrote the library, then exited on the missing anchor, leaving the functions in the entry script as well. The result defined each moved function twice, lints clean, and passes a function-body hash check, so nothing downstream catches it. It cost a split of dns_enforcer.sh this session: 325 lines in, 331 lines out. Both writes now happen after the anchor search. A missing anchor is a warning naming the exact line to paste, not an exit, so the move still completes. Added a shrink guard that refuses to write when the entry script would not get shorter, and an explicit error when the selected function set is empty. Verified both paths: dns_enforcer.sh (no anchor) warns and still drops 171 -> 158 with the functions gone, phone_backup.sh (anchored) drops 333 -> 264 with the source line auto-inserted as before. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_011w2Pj4Ek3ZNMQVYcFHgXDY --- ...tract-tool-duplication-fix-2026-08-17.json | 42 +++++++++++++++++++ meta/scripts/extract_shell_functions.py | 36 +++++++++++++--- 2 files changed, 73 insertions(+), 5 deletions(-) create mode 100644 docs/superpowers/evidence/extract-tool-duplication-fix-2026-08-17.json diff --git a/docs/superpowers/evidence/extract-tool-duplication-fix-2026-08-17.json b/docs/superpowers/evidence/extract-tool-duplication-fix-2026-08-17.json new file mode 100644 index 00000000..7b7278dc --- /dev/null +++ b/docs/superpowers/evidence/extract-tool-duplication-fix-2026-08-17.json @@ -0,0 +1,42 @@ +{ + "intent": "Stop extract_shell_functions.py from producing a split in which every moved function is defined twice, which is what it did on any script lacking a `set -e` anchor.", + "scope": [ + "meta/scripts/extract_shell_functions.py", + "Non-goal: changing how functions are located or how the source line is worded when the anchor IS found." + ], + "changes": [ + "Moved both file writes to after the anchor search. The library was previously written before the anchor check and the process then exited, so the functions stayed in the entry script AND appeared in the new library.", + "A missing anchor is now a warning on stderr naming the exact two lines to paste by hand, not an exit. The move still completes, so the operator's remaining job is one paste rather than an undo.", + "Added a shrink guard: if the rewritten entry script would not be shorter than the original, the tool refuses to write anything. A duplicate-definition split lints clean and passes a function-body hash check, so nothing downstream would have caught it.", + "Added an explicit failure when the selected function set is empty, rather than writing an empty library." + ], + "verification": [ + { + "command": "python3 meta/scripts/extract_shell_functions.py lg.sh --functions log,rotate_log", + "result": "pass", + "evidence": "Prints the warning naming `. \"$SCRIPT_DIR/lg.sh\"`, and the entry script drops 171 -> 158 lines with `grep -c '^log()|^rotate_log()'` returning 0. Before this change the same command left both functions in place and the file unchanged in length." + }, + { + "command": "python3 meta/scripts/extract_shell_functions.py pb_lib.sh --functions usage,verify_backup,dump_packages", + "result": "pass", + "evidence": "333 -> 264 lines, library 77 lines, and the source directive is auto-inserted at line 40 after `set -euo pipefail`. The anchored path is unchanged by this fix." + }, + { + "command": "python3 meta/scripts/extract_shell_functions.py