From 529c10b0db35df624bf109a158e9df6f6bac3104 Mon Sep 17 00:00:00 2001 From: "melodic-automation-lanes[bot]" <337595936+melodic-automation-lanes[bot]@users.noreply.github.com> Date: Wed, 11 Nov 2026 19:21:05 +0000 Subject: [PATCH 1/2] Resolve issue #14507 Lane-Activity: work-resolve-issue/resolve-issue --- ...ssue-issue-14507-43084143201-1-a53117c5.md | 7 +++ .../harness-ops/hooks/audit-event-emitter.sh | 23 +++++---- .../harness-ops/hooks/harness-ops-paths.sh | 4 +- .../harness-ops/hooks/hook-failure-audit.sh | 20 +++----- .../harness-ops/hooks/hook-telemetry-sink.sh | 49 ++++++------------- .../hooks/measure-hook-log-budget.sh | 27 +++------- 6 files changed, 54 insertions(+), 76 deletions(-) create mode 100644 .changes/harness-ops/work-resolve-issue-issue-14507-43084143201-1-a53117c5.md mode change 100644 => 100755 plugins/harness-ops/hooks/harness-ops-paths.sh diff --git a/.changes/harness-ops/work-resolve-issue-issue-14507-43084143201-1-a53117c5.md b/.changes/harness-ops/work-resolve-issue-issue-14507-43084143201-1-a53117c5.md new file mode 100644 index 00000000000..80235ee856d --- /dev/null +++ b/.changes/harness-ops/work-resolve-issue-issue-14507-43084143201-1-a53117c5.md @@ -0,0 +1,7 @@ +--- +bump: patch +--- + +### Changed + +- Internal simplification, no behavior change. diff --git a/plugins/harness-ops/hooks/audit-event-emitter.sh b/plugins/harness-ops/hooks/audit-event-emitter.sh index c039cb7109d..115f12c96b1 100755 --- a/plugins/harness-ops/hooks/audit-event-emitter.sh +++ b/plugins/harness-ops/hooks/audit-event-emitter.sh @@ -76,15 +76,22 @@ EVENT="" SESSION_ID="" [[ "$INPUT" =~ \"session_id\"[[:space:]]*:[[:space:]]*\"([A-Za-z0-9._-]+)\" ]] && SESSION_ID="${BASH_REMATCH[1]}" +# emit::subject +# Emits one envelope whose data is the subject alone. +emit::subject() { + local data + data=$(jq -nc --arg session_id "$SESSION_ID" --arg subject "$1" \ + '{subject: $subject} + (if $session_id == "" then {} else {session_id: $session_id} end)') + hook::emit_telemetry "$2" "$3" "$4" "$START" "$data" "${CLAUDE_PROJECT_DIR:-}" +} + # emit::subject_row # The rows whose whole projection is one payload field as the subject. An # absent or empty field is a silent skip. emit::subject_row() { - local subject data + local subject subject=$(hook::jq_field "$INPUT" "$1") || return 0 - data=$(jq -nc --arg session_id "$SESSION_ID" --arg subject "$subject" \ - '{subject: $subject} + (if $session_id == "" then {} else {session_id: $session_id} end)') - hook::emit_telemetry "$2" "$3" "$4" "$START" "$data" "${CLAUDE_PROJECT_DIR:-}" + emit::subject "$subject" "$2" "$3" "$4" } # emit::tool_row @@ -119,7 +126,7 @@ emit::tool_row() { # back in for one-off debugging with # CLAUDE_PLUGIN_OPTION_INSTRUCTIONS_LOADED_AUDIT_LOG_SESSION_START=true. emit::instructions_loaded_row() { - local file_path load_reason file_disp project_dir subject data + local file_path load_reason file_disp project_dir # Both payload fields in ONE jq process; see emit::tool_row for why. hook::jq_fields "$INPUT" '.file_path' '.load_reason' || return 0 file_path="${HOOK_JQ_FIELDS[0]}" @@ -146,11 +153,7 @@ emit::instructions_loaded_row() { esac fi - subject="${file_disp}:${load_reason}" - data=$(jq -nc --arg session_id "$SESSION_ID" --arg subject "$subject" \ - '{subject: $subject} + (if $session_id == "" then {} else {session_id: $session_id} end)') - hook::emit_telemetry "instructions-loaded-audit" "InstructionsLoaded" "ok" \ - "$START" "$data" "${CLAUDE_PROJECT_DIR:-}" + emit::subject "${file_disp}:${load_reason}" "instructions-loaded-audit" "InstructionsLoaded" "ok" } # The UserPromptExpansion row: user-typed slash-command / MCP-prompt diff --git a/plugins/harness-ops/hooks/harness-ops-paths.sh b/plugins/harness-ops/hooks/harness-ops-paths.sh old mode 100644 new mode 100755 index 919debdacce..2395b7e8d0c --- a/plugins/harness-ops/hooks/harness-ops-paths.sh +++ b/plugins/harness-ops/hooks/harness-ops-paths.sh @@ -126,14 +126,14 @@ harness_ops::resolve_skill_usage_dir() { case "$scope" in user) [[ -n "${HOME:-}" && -d "${HOME:-}" ]] || return 2 - harness_ops::resolve_project_relative_dir "$HOME" "$rel_dir" || return 1 + harness_ops::resolve_project_relative_dir "$HOME" "$rel_dir" ;; data-dir) [[ -n "${CLAUDE_PLUGIN_DATA:-}" ]] || return 2 printf '%s' "${CLAUDE_PLUGIN_DATA%/}/skill-usage/$(harness_ops::repo_slug "$project_dir")" ;; *) - harness_ops::resolve_project_relative_dir "$project_dir" "$rel_dir" || return 1 + harness_ops::resolve_project_relative_dir "$project_dir" "$rel_dir" ;; esac } diff --git a/plugins/harness-ops/hooks/hook-failure-audit.sh b/plugins/harness-ops/hooks/hook-failure-audit.sh index 1eddff9ff21..d209bef3b12 100755 --- a/plugins/harness-ops/hooks/hook-failure-audit.sh +++ b/plugins/harness-ops/hooks/hook-failure-audit.sh @@ -211,6 +211,11 @@ cursor_advance() { [[ -n "$CURSOR_FILE" ]] || return 0 printf 'b%s\n%s\n' "$SCANNED" "$TRANSCRIPT" 2>/dev/null >"$CURSOR_FILE" || : } +# The scanned lines need no warning: record them as audited and stop. +dispose_and_exit() { + cursor_advance + exit 0 +} # Bounded COLD read: the first Stop of a session (or a reset) is the one read # the cursor cannot bound, so the byte cap stays. When the cap truncates, the @@ -402,10 +407,7 @@ if ((CURSOR == 0)); then fi fi LINES=() -[[ -n "$RECORDS" ]] || { - cursor_advance - exit 0 -} +[[ -n "$RECORDS" ]] || dispose_and_exit # `printf | jq` and NOT a here-string, even though the pipeline costs a process # the here-string would not. What is known, stated as known: hook::jq_field in # the shared library documents this hazard and refuses the here-string form for @@ -459,10 +461,7 @@ SUMMARY=$(printf '%s' "$RECORDS" | # it stood so the same lines are audited again. `[]` is a document, and one that # disposes of them. [[ -n "$SUMMARY" ]] || exit 0 -[[ "$SUMMARY" != "[]" ]] || { - cursor_advance - exit 0 -} +[[ "$SUMMARY" != "[]" ]] || dispose_and_exit # Once per session per hook name. Markers live under ${CLAUDE_PLUGIN_DATA} # (survives plugin updates) in the directory resolved above; stale sessions' @@ -488,10 +487,7 @@ NEW=$(jq -cn --argjson summary "$SUMMARY" --arg warned "$WARNED" ' ($warned | split("\n") | map(rtrimstr("\r")) | map(select(length > 0))) as $seen | [$summary[] | select((.hookName + " " + .command) as $k | $seen | index($k) | not)]') [[ -n "$NEW" ]] || exit 0 -[[ "$NEW" != "[]" ]] || { - cursor_advance - exit 0 -} +[[ "$NEW" != "[]" ]] || dispose_and_exit TOTAL=$(jq -rn --argjson new "$NEW" '[$new[].count] | add') diff --git a/plugins/harness-ops/hooks/hook-telemetry-sink.sh b/plugins/harness-ops/hooks/hook-telemetry-sink.sh index 13fbd58bb38..9fbc0654ab6 100755 --- a/plugins/harness-ops/hooks/hook-telemetry-sink.sh +++ b/plugins/harness-ops/hooks/hook-telemetry-sink.sh @@ -95,26 +95,10 @@ CHANGED="${FIELDS[8]}" # error/failed-then-fixed analysis keys on exit_code != 0. A clean/no-op run is 0; # error and blocked are non-clean (2). case "$STATUS" in -ok) - STATUS_OUT=success - EXIT_CODE=0 - ;; -skipped) - STATUS_OUT=skipped - EXIT_CODE=0 - ;; -error) - STATUS_OUT=error - EXIT_CODE=2 - ;; -blocked) - STATUS_OUT=blocked - EXIT_CODE=2 - ;; -*) - STATUS_OUT=error - EXIT_CODE=2 - ;; +ok) STATUS_OUT=success EXIT_CODE=0 ;; +skipped) STATUS_OUT=skipped EXIT_CODE=0 ;; +blocked) STATUS_OUT=blocked EXIT_CODE=2 ;; +*) STATUS_OUT=error EXIT_CODE=2 ;; esac project_dir="" @@ -157,19 +141,18 @@ max_bytes="${CLAUDE_PLUGIN_OPTION_HOOK_EVENTS_MAX_BYTES:-}" # append_capped append_capped() { local file="$1" line="$2" max="$3" size - if command -v flock >/dev/null 2>&1; then - ( - flock -w 2 9 || exit 0 - size=$(wc -c <"$file" 2>/dev/null) || size=0 - ((size > max)) && mv -f "$file" "${file}.1" 2>/dev/null - printf '%s\n' "$line" >>"$file" - ) 9>"${file}.lock" 2>/dev/null - else - size=$(wc -c <"$file" 2>/dev/null) || size=0 - ((size > max)) && mv -f "$file" "${file}.1" 2>/dev/null - printf '%s\n' "$line" >>"$file" 2>/dev/null - fi + size=$(wc -c <"$file" 2>/dev/null) || size=0 + ((size > max)) && mv -f "$file" "${file}.1" 2>/dev/null + printf '%s\n' "$line" >>"$file" } -append_capped "${root}/hook-events.jsonl" "$LINE" "$max_bytes" +events_file="${root}/hook-events.jsonl" +if command -v flock >/dev/null 2>&1; then + ( + flock -w 2 9 || exit 0 + append_capped "$events_file" "$LINE" "$max_bytes" + ) 9>"${events_file}.lock" 2>/dev/null +else + append_capped "$events_file" "$LINE" "$max_bytes" 2>/dev/null +fi exit 0 diff --git a/plugins/harness-ops/hooks/measure-hook-log-budget.sh b/plugins/harness-ops/hooks/measure-hook-log-budget.sh index 706a3203175..662ce179e5b 100755 --- a/plugins/harness-ops/hooks/measure-hook-log-budget.sh +++ b/plugins/harness-ops/hooks/measure-hook-log-budget.sh @@ -100,10 +100,7 @@ if [[ -n "$CHECK_DOC" ]]; then echo "missing: windows-git-bash stamp or unmeasured placeholder" >&2 missing=1 fi - if [[ "$missing" -eq 0 ]]; then - exit 0 - fi - exit 1 + exit "$missing" fi if [[ ! "$SAMPLES" =~ ^[1-9][0-9]*$ ]]; then @@ -156,11 +153,10 @@ time_ms() { } median_ms() { - local i=0 + local i : >"$samples_file" - while [[ "$i" -lt "$SAMPLES" ]]; do + for ((i = 0; i < SAMPLES; i++)); do time_ms "$@" >>"$samples_file" - i=$((i + 1)) done median_of "$samples_file" } @@ -201,12 +197,11 @@ invoking_bash_floor="$(median_ms bash -c :)" kill_off="$(median_ms run_row "$PAYLOAD" false)" wall_ms() { - local value="$1" t0 t1 n=0 + local value="$1" t0 t1 n t0=$EPOCHREALTIME - while [[ "$n" -lt 15 ]]; do + for ((n = 0; n < 15; n++)); do run_row "$PAYLOAD" "$value" & run_row "$STOP_PAYLOAD" "$value" & - n=$((n + 1)) done wait t1=$EPOCHREALTIME @@ -238,13 +233,11 @@ extra_file="$WORK/wide-extra.txt" s_file="$WORK/s.txt" : >"$extra_file" : >"$s_file" -i=0 -while [[ "$i" -lt "$SAMPLES" ]]; do +for ((i = 0; i < SAMPLES; i++)); do small_us="$(time_us run_row "$SMALL_PAYLOAD" true)" wide_us="$(time_us run_row "$WIDE_PAYLOAD" true)" printf '%s\n' "$((wide_us - small_us))" >>"$extra_file" time_us "${launcher_bash:-bash}" -c : >>"$s_file" - i=$((i + 1)) done wide_extra="$(us_to_ms "$(median_of "$extra_file")")" s_ms="$(us_to_ms "$(median_of "$s_file")")" @@ -255,15 +248,13 @@ append_probe() { local pad i : >"$file" pad="$(head -c "$bytes" /dev/zero | tr '\0' 'a')" - i=1 # Each append goes through slog_append, the hook's own write path. - while [[ "$i" -le 33 ]]; do + for ((i = 1; i <= 33; i++)); do ( # shellcheck source=session-log-lib.sh source "$SCRIPT_DIR/session-log-lib.sh" slog_append "$file" "{\"i\":$i,\"pad\":\"$pad\"}" ) & - i=$((i + 1)) done wait local lines=0 corrupt=0 line @@ -355,7 +346,5 @@ EOF )" printf '%s\n' "$capture" -if [[ -n "$RECORD" ]]; then - printf '%s\n' "$capture" >"$RECORD" -fi +[[ -z "$RECORD" ]] || printf '%s\n' "$capture" >"$RECORD" exit 0 From eb3e3e1c00011e20c8d17c2409f6f001f66ecba4 Mon Sep 17 00:00:00 2001 From: Kyle Sexton <153232337+kyle-sexton@users.noreply.github.com> Date: Wed, 11 Nov 2026 15:43:39 -0500 Subject: [PATCH 2/2] fix(harness-ops): carry the sink simplification into the repo copy .claude/hooks/hook-telemetry-sink.sh must match the harness-ops sink apart from its source block (hook-telemetry-sink.test.sh), and the lane cannot push .claude/. Apply the same two hunks to it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VQb9Q3tnh28AzDYZ8AUjhH --- .claude/hooks/hook-telemetry-sink.sh | 49 +++++++++------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/.claude/hooks/hook-telemetry-sink.sh b/.claude/hooks/hook-telemetry-sink.sh index cdc6163f24e..d85d51e81d6 100755 --- a/.claude/hooks/hook-telemetry-sink.sh +++ b/.claude/hooks/hook-telemetry-sink.sh @@ -98,26 +98,10 @@ CHANGED="${FIELDS[8]}" # error/failed-then-fixed analysis keys on exit_code != 0. A clean/no-op run is 0; # error and blocked are non-clean (2). case "$STATUS" in -ok) - STATUS_OUT=success - EXIT_CODE=0 - ;; -skipped) - STATUS_OUT=skipped - EXIT_CODE=0 - ;; -error) - STATUS_OUT=error - EXIT_CODE=2 - ;; -blocked) - STATUS_OUT=blocked - EXIT_CODE=2 - ;; -*) - STATUS_OUT=error - EXIT_CODE=2 - ;; +ok) STATUS_OUT=success EXIT_CODE=0 ;; +skipped) STATUS_OUT=skipped EXIT_CODE=0 ;; +blocked) STATUS_OUT=blocked EXIT_CODE=2 ;; +*) STATUS_OUT=error EXIT_CODE=2 ;; esac project_dir="" @@ -160,19 +144,18 @@ max_bytes="${CLAUDE_PLUGIN_OPTION_HOOK_EVENTS_MAX_BYTES:-}" # append_capped append_capped() { local file="$1" line="$2" max="$3" size - if command -v flock >/dev/null 2>&1; then - ( - flock -w 2 9 || exit 0 - size=$(wc -c <"$file" 2>/dev/null) || size=0 - ((size > max)) && mv -f "$file" "${file}.1" 2>/dev/null - printf '%s\n' "$line" >>"$file" - ) 9>"${file}.lock" 2>/dev/null - else - size=$(wc -c <"$file" 2>/dev/null) || size=0 - ((size > max)) && mv -f "$file" "${file}.1" 2>/dev/null - printf '%s\n' "$line" >>"$file" 2>/dev/null - fi + size=$(wc -c <"$file" 2>/dev/null) || size=0 + ((size > max)) && mv -f "$file" "${file}.1" 2>/dev/null + printf '%s\n' "$line" >>"$file" } -append_capped "${root}/hook-events.jsonl" "$LINE" "$max_bytes" +events_file="${root}/hook-events.jsonl" +if command -v flock >/dev/null 2>&1; then + ( + flock -w 2 9 || exit 0 + append_capped "$events_file" "$LINE" "$max_bytes" + ) 9>"${events_file}.lock" 2>/dev/null +else + append_capped "$events_file" "$LINE" "$max_bytes" 2>/dev/null +fi exit 0