fix(telemetry): Stop recursive background flush helpers - #346
fix(telemetry): Stop recursive background flush helpers#346miroslavkiev wants to merge 1 commit into
Conversation
Skip startup upkeep for the internal flush command and require a persisted timestamp before spawning. This prevents repeating helper chains when a sandbox blocks telemetry state writes. Add safe regression coverage for denied writes, daily throttling, and the internal CLI command. Co-Authored-By: Codex <noreply@openai.com>
🌱 graft blast radius2 areas changed → 1 area can be affected. 1 dependent symbol, depth 2. flowchart TB
A0(("MCP Server<br/>1 symbol"))
classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
class A0 reached;
Who knows this code — 4 people across 3 areas
Ownership is git history over each area's own files, weighted towards recent work (120-day half-life). Merge commits and bots are dropped, and you are dropped from your own PR. A name with no All 1 dependent symbol, grouped by areaMCP Server — 1 symbol in 1 file
Test signal per changed area — 1 ✓ · 1 –Reached = a node under a test path has a resolved edge into the changed symbol. It undercounts anything called indirectly — through a CLI, a spawned process or a dynamic import — so read a low ratio as “look here”, never as a coverage gate.
Open the interactive graph → — click an area to see the code that changed, and the line that reaches it. |
Fix a repeating chain of detached
_telemetry-flushhelpers when writes to the user's telemetry state are denied, such as inside a macOS agent sandbox. This was observed in published 0.16.0 and is still present on current main (0.18.0): 332 different helpers appeared in 15 seconds, with about 40 running together. System logs showed denied writes to~/.graft/telemetry.json.<pid>.tmpand denied removal of the queue file.The internal flush command runs the global startup hook, which schedules another flush before processing its own queue.
patchState()deliberately tolerates write failures, so the stored timestamp stays stale and each child can start a successor. Skip startup upkeep for_telemetry-flushand require the timestamp to be read back from disk before scheduling a background flush. The shared guard covers both CLI calls and MCP startup without changingpatchState()or discarding queued events.Three regression tests cover a denied timestamp write, normal daily throttling, and the real internal CLI command. Child creation and network requests are intercepted, so running the tests against the old code cannot start another process chain. Both regression cases fail before the fix and all three tests pass after it.
npm run buildalso passes.The full suite passes: 1,223 tests, zero failures or skips on Node 25.2.1 and macOS. It ran with a temporary home and a separate copy of the same Node binary so global Graft installations could not override the installation-version fixtures.
Applying the same two guards to the affected 0.16.0 installation stopped the helpers without restarting agents. Four consecutive checks found zero helpers, and total CPU usage fell from about 99% to 27%.