From 4e339e9ab6004e8d08422579a0dd2420401094d7 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Fri, 27 Mar 2026 09:19:03 -0600 Subject: [PATCH] fix: make telemetry binary calls conditional on _TEL and binary existence Addresses Socket's 14 MEDIUM findings for opaque telemetry binary. Adds local JSONL fallback (always available, inspectable). Remote binary only runs if _TEL != "off" and binary exists. --- scripts/resolvers/preamble.ts | 37 +++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/scripts/resolvers/preamble.ts b/scripts/resolvers/preamble.ts index fe0ba77e..f4fc5475 100644 --- a/scripts/resolvers/preamble.ts +++ b/scripts/resolvers/preamble.ts @@ -1,5 +1,17 @@ import type { TemplateContext } from './types'; +/** + * Preamble architecture — why every skill needs this + * + * Each skill runs independently via `claude -p`. There is no shared loader. + * The preamble provides: update checks, session tracking, user preferences, + * repo mode detection, and telemetry. + * + * Telemetry data flow: + * 1. Always: local JSONL append to ~/.gstack/analytics/ (inline, inspectable) + * 2. If _TEL != "off" AND binary exists: gstack-telemetry-log for remote reporting + */ + function generatePreambleBash(ctx: TemplateContext): string { const runtimeRoot = ctx.host === 'codex' ? `_ROOT=$(git rev-parse --show-toplevel 2>/dev/null) @@ -40,7 +52,15 @@ echo "TEL_PROMPTED: $_TEL_PROMPTED" mkdir -p ~/.gstack/analytics echo '{"skill":"${ctx.skillName}","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true # zsh-compatible: use find instead of glob to avoid NOMATCH error -for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do [ -f "$_PF" ] && ${ctx.paths.binDir}/gstack-telemetry-log --event-type skill_run --skill _pending_finalize --outcome unknown --session-id "$_SESSION_ID" 2>/dev/null || true; break; done +for _PF in $(find ~/.gstack/analytics -maxdepth 1 -name '.pending-*' 2>/dev/null); do + if [ -f "$_PF" ]; then + if [ "$_TEL" != "off" ] && [ -x "${ctx.paths.binDir}/gstack-telemetry-log" ]; then + ${ctx.paths.binDir}/gstack-telemetry-log --event-type skill_run --skill _pending_finalize --outcome unknown --session-id "$_SESSION_ID" 2>/dev/null || true + fi + rm -f "$_PF" 2>/dev/null || true + fi + break +done \`\`\``; } @@ -349,15 +369,20 @@ Run this bash: _TEL_END=$(date +%s) _TEL_DUR=$(( _TEL_END - _TEL_START )) rm -f ~/.gstack/analytics/.pending-"$_SESSION_ID" 2>/dev/null || true -~/.claude/skills/gstack/bin/gstack-telemetry-log \\ - --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \\ - --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +# Local analytics (always available, no binary needed) +echo '{"skill":"SKILL_NAME","duration_s":"'"$_TEL_DUR"'","outcome":"OUTCOME","browse":"USED_BROWSE","session":"'"$_SESSION_ID"'","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true +# Remote telemetry (opt-in, requires binary) +if [ "$_TEL" != "off" ] && [ -x ~/.claude/skills/gstack/bin/gstack-telemetry-log ]; then + ~/.claude/skills/gstack/bin/gstack-telemetry-log \\ + --skill "SKILL_NAME" --duration "$_TEL_DUR" --outcome "OUTCOME" \\ + --used-browse "USED_BROWSE" --session-id "$_SESSION_ID" 2>/dev/null & +fi \`\`\` Replace \`SKILL_NAME\` with the actual skill name from frontmatter, \`OUTCOME\` with success/error/abort, and \`USED_BROWSE\` with true/false based on whether \`$B\` was used. -If you cannot determine the outcome, use "unknown". This runs in the background and -never blocks the user. +If you cannot determine the outcome, use "unknown". The local JSONL always logs. The +remote binary only runs if telemetry is not off and the binary exists. ## Plan Status Footer