Skip to content

fix(telemetry): Stop recursive background flush helpers - #346

Open
miroslavkiev wants to merge 1 commit into
trailhq:mainfrom
miroslavkiev:fix/telemetry-flush-spawn-loop
Open

fix(telemetry): Stop recursive background flush helpers#346
miroslavkiev wants to merge 1 commit into
trailhq:mainfrom
miroslavkiev:fix/telemetry-flush-spawn-loop

Conversation

@miroslavkiev

Copy link
Copy Markdown

Fix a repeating chain of detached _telemetry-flush helpers 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>.tmp and 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-flush and 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 changing patchState() 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 build also 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%.

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>
@github-actions

github-actions Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

🌱 graft blast radius

2 areas changed → 1 area can be affected. 1 dependent symbol, depth 2.
Tests: 1 area updated its tests.
Tag: @anirudhkumar-nanonets — cli.ts, MCP Server · @Frankie-Xu — cli.ts · Buseong Kim — Telemetry Flushing, MCP Server

flowchart TB
  A0(("MCP Server<br/>1 symbol"))
  classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
  class A0 reached;
Loading
Can be affected Symbols Nearest hop Reached from
MCP Server 1 src/mcp/server.ts:L71-L151 startMcpServer — calls, depth 1 Telemetry Flushing
Who knows this code — 4 people across 3 areas
Area Who knows it
Telemetry Flushing · changed Marcelo Caporale — 1 commit, last 12d ago · Buseong Kim — 1 commit, last 16d ago
cli.ts · changed @anirudhkumar-nanonets — 5 commits, last 2d ago · @Frankie-Xu — 2 commits, last 12d ago
MCP Server · affected @anirudhkumar-nanonets — 1 commit, last 11d ago · Buseong Kim — 1 commit, last 16d ago

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 @ has no GitHub handle in its commit email — tag them by hand, or add a .mailmap entry. A suggestion from history, not a CODEOWNERS rule.

All 1 dependent symbol, grouped by area

MCP Server — 1 symbol in 1 file

  • src/mcp/server.ts:L71-L151 — startMcpServer (calls, depth 1)
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.

  • Telemetry Flushing — 1 of 1 reached · 1 test file changed here: test/telemetry-flush.test.ts
  • cli.ts — no function, method or class changed here

⚠️ 1 changed file not in the graph (CHANGELOG.md) — no parser claims the extension, or the index predates the file.

graft blast · refs/graft/base...HEAD · depth 2 · 4 changed files

Open the interactive graph → — click an area to see the code that changed, and the line that reaches it.

github-actions Bot added a commit that referenced this pull request Sep 10, 2026
@miroslavkiev
miroslavkiev marked this pull request as ready for review September 12, 2026 19:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant