Skip to content

fix(telemetry): postinstall — symlink-safe isDirectRun, stdout flush, dead-code#323

Merged
blove merged 1 commit into
mainfrom
worktree-gtm+telemetry-postinstall-fixes
May 15, 2026
Merged

fix(telemetry): postinstall — symlink-safe isDirectRun, stdout flush, dead-code#323
blove merged 1 commit into
mainfrom
worktree-gtm+telemetry-postinstall-fixes

Conversation

@blove
Copy link
Copy Markdown
Contributor

@blove blove commented May 15, 2026

Summary

Three small fixes uncovered while smoke-testing the npm install flow against a local capture server. All confirmed working end-to-end before opening the PR.

Bug 1 — isDirectRun symlink-blindness (macOS, pnpm stores)

process.argv[1] keeps the unresolved path (e.g. /tmp/foo.js), but import.meta.url resolves through realpath (file:///private/tmp/foo.js on macOS, content-addressed paths under pnpm). The naive import.meta.url === \file://${process.argv[1]}`comparison failed even when both pointed at the same file — sonode /tmp/.../postinstall.js` was a silent no-op.

Fixed by normalizing both sides through realpath:

```ts
pathToFileURL(realpathSync(process.argv[1])).href === import.meta.url
```

Bug 2 — stdout opt-out notice was invisible to npm install --foreground-scripts

posthog-node's shutdown() await chain left the notice in stdout's pipe buffer at process exit. npm reaped the script before the buffer drained, so the user never saw the trust-contract opt-out line. Added await flushStdout() (drains via the stream's drain event or one setImmediate tick) before main() returns.

Before: install output silent.
After:
```

@ngaf/telemetry@0.0.0 postinstall
@ngaf/telemetry: sent install ping (@ngaf/telemetry@0.0.0). Disable: DO_NOT_TRACK=1 or NGAF_TELEMETRY_DISABLED=1. See https://github.com/cacheplane/angular-agent-framework/blob/main/libs/telemetry/README.md
```

Bug 3 — Redundant if (!deps.env.CI) guard

isTelemetryDisabled(deps.env) already short-circuits on CI=true, DO_NOT_TRACK, and NGAF_TELEMETRY_DISABLED. The second CI check was unreachable. The README's trust contract correctly says CI = full opt-out; the implementation now has a single source-of-truth gate matching that contract.

Tests

  • 46 passing (was 45)
  • New test pins DO_NOT_TRACK as full opt-out (no event AND no notice)
  • CI=true test gained capturePostinstall.not.toHaveBeenCalled() assertion

End-to-end smoke verification (manual)

  • npm install --foreground-scripts /tmp/ngaf-telemetry-smoke.tgz → notice visible ✓
  • node /tmp/.../postinstall.js (direct, with /tmp symlink) → main() triggers ✓
  • DO_NOT_TRACK=1 npm install … → no event, no notice ✓
  • CI=true npm install … → no event, no notice ✓

🤖 Generated with Claude Code

… dead-code

Three small fixes uncovered by manual smoke testing of the npm install
path against a local capture server.

1. isDirectRun symlink bug (macOS, pnpm stores):
   `process.argv[1]` keeps the unresolved path (e.g. /tmp/foo.js) while
   `import.meta.url` resolves to the realpath (file:///private/tmp/foo.js
   on macOS). The naive string comparison failed even though both
   referenced the same file, so `node /tmp/.../postinstall.js` was a
   silent no-op. Switched to
     pathToFileURL(realpathSync(process.argv[1])).href === import.meta.url
   which normalizes both sides through realpath. Direct invocation now
   triggers main() correctly.

2. stdout opt-out notice was invisible to `npm install --foreground-scripts`:
   posthog-node's `shutdown()` await chain left the notice in stdout's
   pipe buffer at process exit. npm reaped the script before the buffer
   drained. Added `await flushStdout()` (Promise that resolves on the
   stream's drain event or one setImmediate tick) so the line is reliably
   flushed before the process can exit.

3. Dropped redundant `if (!deps.env.CI)` guard:
   `isTelemetryDisabled(deps.env)` already short-circuits on CI=true
   (and DO_NOT_TRACK, NGAF_TELEMETRY_DISABLED) by returning early before
   either the capture or the write. The second CI check was unreachable
   dead code. The README's trust contract correctly says CI = full opt-out;
   the previous code matched that contract semantically but had two
   inconsistent gates.

Tests: 46 passing (was 45). New test asserts DO_NOT_TRACK is a full
opt-out (no event AND no notice), matching the README contract. The
CI=true test gained a `capturePostinstall.not.toHaveBeenCalled()`
assertion to pin that CI is full opt-out too.

Smoke-verified end-to-end:
- npm install --foreground-scripts now shows the opt-out notice ✓
- node /tmp/.../postinstall.js (direct from /tmp) now triggers main() ✓
- DO_NOT_TRACK=1 npm install: no event, no notice ✓
- CI=true npm install: no event, no notice ✓

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cacheplane Ready Ready Preview, Comment May 15, 2026 5:52pm

Request Review

@blove blove merged commit f8e8f46 into main May 15, 2026
16 checks passed
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