@@ -2040,6 +2040,57 @@ assert_eq "Null hooks: exactly 1 track hook installed" "1" "$null_track"
20402040null_end=$( jq ' [.hooks.SessionEnd[]?.hooks[]? | select((.command // "") | contains("claude-session-cleanup"))] | length' " $HOME /.claude/settings.json" )
20412041assert_eq " Null hooks: exactly 1 cleanup hook installed" " 1" " $null_end "
20422042
2043+ # --- Test 7h: Current + stale hook coexist (cleanup must still run) ---
2044+ #
2045+ # If both the current-path hook AND a stale-path duplicate are present
2046+ # (e.g. from manual editing or corruption), the cleanup block must still
2047+ # fire to remove the stale copy. Previously the exact-match guard would
2048+ # see the current hook, skip the block, and leave the stale duplicate.
2049+
2050+ echo " "
2051+ echo " === Test 7h: Current + stale hook coexist ==="
2052+ echo " "
2053+
2054+ rm -f " $HOME /.claude/settings.json"
2055+ echo ' {}' > " $HOME /.claude/settings.json"
2056+
2057+ # Inject both the CURRENT path and a STALE path for SessionStart and SessionEnd
2058+ current_track=" bash '${REPO_DIR} /hooks/claude-session-track.sh'"
2059+ current_cleanup=" bash '${REPO_DIR} /hooks/claude-session-cleanup.sh'"
2060+ tmp_dual=$( mktemp)
2061+ jq --arg cur_track " $current_track " --arg stale_track " $stale_track " \
2062+ --arg cur_cleanup " $current_cleanup " --arg stale_cleanup " $stale_cleanup " '
2063+ .hooks = {
2064+ "SessionStart": [
2065+ {"matcher": "", "hooks": [{"type": "command", "command": $cur_track}]},
2066+ {"matcher": "", "hooks": [{"type": "command", "command": $stale_track}]}
2067+ ],
2068+ "SessionEnd": [
2069+ {"matcher": "", "hooks": [{"type": "command", "command": $cur_cleanup}]},
2070+ {"matcher": "", "hooks": [{"type": "command", "command": $stale_cleanup}]}
2071+ ]
2072+ }
2073+ ' " $HOME /.claude/settings.json" > " $tmp_dual " && mv " $tmp_dual " " $HOME /.claude/settings.json"
2074+
2075+ # Verify both are in place
2076+ dual_before=$( jq ' [.hooks.SessionStart[]?.hooks[]? | select(.command | contains("claude-session-track"))] | length' " $HOME /.claude/settings.json" )
2077+ assert_eq " Dual: 2 track hooks present before cleanup" " 2" " $dual_before "
2078+
2079+ # Run the plugin — must remove the stale copy, keep exactly 1
2080+ bash " $REPO_DIR /tmux-assistant-resurrect.tmux"
2081+
2082+ dual_after=$( jq ' [.hooks.SessionStart[]?.hooks[]? | select(.command | contains("claude-session-track"))] | length' " $HOME /.claude/settings.json" )
2083+ assert_eq " Dual: exactly 1 SessionStart hook after cleanup" " 1" " $dual_after "
2084+
2085+ dual_cmd=$( jq -r ' .hooks.SessionStart[]?.hooks[]? | select(.command | contains("claude-session-track")) | .command' " $HOME /.claude/settings.json" )
2086+ assert_eq " Dual: surviving hook has current path" " $current_track " " $dual_cmd "
2087+
2088+ dual_end_after=$( jq ' [.hooks.SessionEnd[]?.hooks[]? | select(.command | contains("claude-session-cleanup"))] | length' " $HOME /.claude/settings.json" )
2089+ assert_eq " Dual: exactly 1 SessionEnd hook after cleanup" " 1" " $dual_end_after "
2090+
2091+ dual_end_cmd=$( jq -r ' .hooks.SessionEnd[]?.hooks[]? | select(.command | contains("claude-session-cleanup")) | .command' " $HOME /.claude/settings.json" )
2092+ assert_eq " Dual: surviving SessionEnd hook has current path" " $current_cleanup " " $dual_end_cmd "
2093+
20432094# --- Test 8: strip_assistant_pane_contents() ---
20442095
20452096suite " strip_pane_contents"
0 commit comments