perf(markdown-formatter): gate telemetry payload behind the sink opt-in#26
Conversation
When HOOK_TELEMETRY_SINK is unset (the default for consumers who have not opted in), markdown-format.sh still built the telemetry payload before hook::emit_telemetry's guard could short-circuit: the tool_name jq parse, the FILE_REL block (2x cygpath on Windows), and data_json (1x jq) — about 4 telemetry-only subprocesses per .md edit (~120ms on Windows Git Bash, where each fork costs ~30ms). Add a cheap hook::telemetry_enabled probe to the shared lib (a pure-shell sink-presence test) and gate all payload construction on it, folding the data_json build into emit_tel after both guards. The unwired path now spawns zero telemetry-only subprocesses; REPO_ROOT stays, as it also anchors markdownlint config discovery. hook::emit_telemetry still re-checks the sink itself, so producers that skip the probe stay correct. Tests: lib suite covers the probe's unset/empty/set verdicts; the markdown-format suite counts subprocess spawns via cygpath/jq PATH shims — unwired runs make zero cygpath -lm calls and exactly one jq call (the file_path parse), with a wired positive control proving the shims observe the telemetry spawns. All four carrying plugins bump their patch version so consumers receive the synced lib change (the plugin version is the update cache key). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017K9RRMdUKD7HLExmHwMXAe
|
Claude finished @kyle-sexton's task in 2m 15s —— View job PR Review: perf(markdown-formatter): gate telemetry payload behind the sink opt-in
Summary: This PR is well-designed and correctly implemented. The telemetry gate eliminates ~4 unnecessary subprocess forks per
|
…e IV, flip pre-build (#939) Dated Phase-4 note: operator ruling (2026-07-22) to move at maximum speed within the predicate's intent. Calendar gates stay binding; sanctioned levers are front-loaded seeding (genuine items only), parallel Phase IV ignition (gate already met), and pre-built Phase III flip machinery. Records same-day watch activity: drain PRs #23/#24/#26 + Dependabot #25 merged in kyle-sexton/autonomy-demo-scratch, queue reseeded (scratch#27–#31), predicate completions=6. ## Related - kyle-sexton/autonomy-demo-scratch#27, #28, #29, #30, #31. No linked issue — status-note documentation; nothing in this repo closes. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Fable 5 <noreply@anthropic.com>


Summary
When
HOOK_TELEMETRY_SINKis unset (telemetry not wired — the default), themarkdown-formatproducer built its telemetry payload unconditionally beforehook::emit_telemetry's opt-in guard could short-circuit: thetool_namejq parse, theFILE_RELblock (2×cygpathon Windows), anddata_json(1×jq). That is ~4 telemetry-only subprocesses per.mdedit on the unwired path (~120ms on Windows Git Bash at ~30ms per fork).Change
lib/hook-utils.sh: newhook::telemetry_enabledprobe — a pure-shell[[ -n "${HOOK_TELEMETRY_SINK:-}" ]], no subprocess.hook::emit_telemetrystill re-checks the sink itself, so callers that skip the probe stay correct, just slower.markdown-format.sh:TOOL+FILE_RELconstruction is gated on the probe, and thedata_jsonbuild is folded intoemit_telafter both guards (start-stamp and sink opt-in). The unwired path now spawns zero telemetry-only subprocesses.REPO_ROOTstays unconditional — it also anchors markdownlint config discovery.scripts/sync-hook-utils.sh; all four carrying plugins bump their patch version so consumers receive the lib change (the plugin version is the update cache key).TOOLwas gated in addition to the two items enumerated in the issue: its jq parse feeds only the envelope'sdata.tool, so leaving it would have kept one telemetry-only subprocess on the unwired path.Tests
lib/hook-utils.test.sh: probe verdicts for unset / empty / non-empty sink.markdown-format.test.sh: counts subprocess spawns viacygpath/jqPATH shims — unwired runs make zerocygpath -lmcalls and exactly onejqcall (thefile_pathparse); a wired positive control proves the shims observe the telemetry spawns (2×cygpath -lm, payload jq calls, envelope delivered).Ran locally (Windows Git Bash): lib suite 46/46; markdown-format, bash-lint, ruff-format suites pass (biome suite skips without a Biome binary);
shellcheckclean on all changed scripts;sync-hook-utils.sh --checkand--check-bumppass. One pre-existing, unrelated local flake: the C1 slow-sink timing bound (<2000ms) also fails on unmodifiedmainon this machine when the suite runs end-to-end (in-suite elapsed 5–7s vs ~1.8s standalone, i.e. suite-load noise, not an fd1 leak — the slow-vs-fast sink delta standalone is ~640ms).Closes #6
🤖 Generated with Claude Code
https://claude.ai/code/session_017K9RRMdUKD7HLExmHwMXAe
Note
Low Risk
Performance-only gating with unchanged hook semantics when telemetry is wired;
hook::emit_telemetrystill enforces the sink guard for correctness.Overview
Adds
hook::telemetry_enabledto the shared hook lib (synced to all four formatter/lint plugins) so producers can skip telemetry-only work whenHOOK_TELEMETRY_SINKis unset—the default unwired path.markdown-format.shnow buildstool_name, repo-relativeFILE_REL(including Windowscygpath -lm), and the telemetrydataJSON only when that probe passes;emit_telcentralizes the start-stamp + sink guards and foldsbuild_data_jsonin so unwired edits avoid extrajq/cygpathsubprocesses. Formatting behavior is unchanged;REPO_ROOTstays unconditional for markdownlint discovery.Tests cover the probe in
hook-utils.test.shand PATH-shim subprocess counts inmarkdown-format.test.sh(unwired vs wired positive control). Carrying plugins get patch version bumps so consumers pick up the lib update.Reviewed by Cursor Bugbot for commit 36ce64c. Bugbot is set up for automated code reviews on this repo. Configure here.