Skip to content

fix: npm install warning, onboarding audit telemetry, and dashboard error noise#560

Merged
NiveditJain merged 5 commits into
mainfrom
worktree-npm-install-scripts
Jul 17, 2026
Merged

fix: npm install warning, onboarding audit telemetry, and dashboard error noise#560
NiveditJain merged 5 commits into
mainfrom
worktree-npm-install-scripts

Conversation

@chhhee10

@chhhee10 chhhee10 commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Three related telemetry/packaging fixes, all surfaced while chasing the npm install warning. One PR for review, but three separate commits — reviewable one at a time.

Commit Fixes
fix: stop shipping npm lifecycle scripts the npm warn on every install
fix(audit): report the onboarding auto-audit first-run audits invisible in PostHog
fix(dashboard): report only our own errors MetaMask & co. reported as our errors

Gate on the combined branch: 2117 tests / 123 files, 0 lint errors, clean tsc.


1. Stop shipping npm lifecycle scripts

npm install failproofai@0.0.14-beta.0 printed an npm warn naming a .mjs file — our own scripts/postinstall.mjs. Not specific to beta.0: every published version declares it, including 0.0.13 (current latest).

Two messages, both naming our .mjs, verified live in Docker:

Environment npm Message Script runs?
Node 24.18 LTS / 26.5 11.16 / 11.17 allow-scripts … not yet covered ✅ advisory
npm i -g npm@latest 12.0.1 install-scripts … blocked no
npm ≤ 11.6 11.6.2 (silent) ✅ yes

npm 12 (2026-07-08) is now the latest dist-tag. There is no publisher-side fixallowScripts is consumer-side, and a package declaring allowScripts/trustedDependencies for itself is ignored (verified with a probe package). Shipping no install scripts is the only fix.

Functionality was never affected (bin → prebuilt dist/cli.mjs), but the install telemetry was silently lost. bun/pnpm/Yarn ≥4.14 already block by default, so it was already undercounting.

Three of postinstall's five jobs needed no migration — scripts/launch.ts already does the server.js check and shadow diagnosis (with a better message), and configure-wizard.ts already owns first-run onboarding. So only telemetry moved, into lib/install-check.ts, called from the top of runCli() — deliberately outside the --hook fast path, which runs on every tool call.

trackInstallEvent now takes an explicit version: npm only sets npm_package_version inside a lifecycle script, so outside one it reported "unknown".

preuninstall deleted as dead code (131 lines + 319 of tests). npm honours no uninstall lifecycle scripts — verified with a probe package: postinstall fired, preuninstall never did. Its hook cleanup has therefore never run, so uninstalling strands __failproofai_hook__ entries. Pre-existing bug, now visible — hit for real on a live machine, where ~/.pi/settings.json was left pointing at a deleted pi-extension path. Needs an explicit failproofai policies --uninstall; worth a follow-up.

Behaviour changes: package_installed now measures install→activation, not raw installs (the PostHog series will step — though volume should rise, recovering bun/pnpm/Yarn users). Same-version reinstalls (direction: "reinstall") are no longer reported. The install-time welcome message is dropped.

Verified by publishing the packed tarball to a local verdaccio registry and installing on npm 12: zero warnings, exit 0, CLI runs. Captured PostHog payloads: first_install+package_installed on run 1, nothing on run 2, version_changed on upgrade — all with the real version and existing $lib/product. Also proved prepare: bun run build is not gated on registry installs (beta.0 declares both, npm 12 lists only postinstall), so it stays — publish.yml relies on it to build.

2. Report the onboarding auto-audit

runPostSetupAudit() — the audit that runs automatically at the end of first-run setup, i.e. the first audit any user ever runs — emitted nothing, while explicit failproofai audit reported normally (4 trackHookEvent calls vs 0). The funnel silently undercounted exactly the activation moment it exists to measure.

Confirmed live: fresh install → wizard → auto-audit wrote its cache at 15:29:41 and PostHog saw nothing.

Now emits the same three events tagged source: "onboarding" vs the existing source: "cli". cli_audit_completed fires before the empty-history return (matching runAuditCli) so a user with no history is still counted; cli_audit_failed is awaited because the function returns straight into the dashboard boot, which would race a fire-and-forget send. Completed-event properties now come from one shared helper so the paths can't drift.

Verified by driving the real, unmocked function against a local capture server: cli_audit_started + cli_audit_completed, source=onboarding, real counts (7,468 tool calls / 210 sessions), no throw, no exit.

3. Report only our own dashboard errors

GlobalErrorListeners (mounted in app/layout.tsx) registers page-global handlers. Extensions inject content scripts into the same page and share the same window, so their failures went to PostHog stamped $lib: failproofai-web as though the dashboard threw them:

event: unhandled_rejection · error_message: Failed to connect to MetaMask
error_name: i  ← MetaMask's minified class · $current_url: localhost:8020/policies

The noise is unbounded and unrelated to our code — it makes our error rate a function of users' browser setups and would drown the real signal.

Both handlers now attribute before reporting (lib/error-origin.ts) and report only what traces to our own origin. Positive attribution, not a denylist, so an unknown extension is filtered by default; matched on the shared <vendor>-extension:// suffix so a new browser's scheme needs no code change. Unattributable errors (cross-origin "Script error.", non-Error rejections) are dropped — nothing in them to debug.

React render errors are unaffected: the error boundaries report client_error directly, and React only invokes those for errors in our own tree — which is why the global net can be filtered strictly without losing signal.

Tradeoff: a genuine error rejecting a non-Error value (Promise.reject("boom")) has no stack, so it's no longer reported. Rare; flagging it as a deliberate choice.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Install and upgrade telemetry now runs consistently from the CLI (no more install-time npm lifecycle warnings), improving coverage across npm, Bun, pnpm, and Yarn.
    • Global error reporting now correctly attributes app-origin issues, preventing browser extension errors from polluting analytics.
    • Onboarding auto-audits now emit started/completed/failed telemetry with the proper source, and won’t stop onboarding if a scan fails.
    • Development hook setup no longer silently no-ops when Bun isn’t on PATH.
  • Documentation

    • Clarified how package aliases install and expose the main command.
  • Chores

    • Removed obsolete install-time and uninstall-time lifecycle handling.

@chhhee10
chhhee10 force-pushed the worktree-npm-install-scripts branch from a90f3a6 to d452f0d Compare July 17, 2026 08:50
@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 71078dec-302c-4e5c-a632-3b7a7db3ffb7

📥 Commits

Reviewing files that changed from the base of the PR and between 5daad39 and ced8e13.

📒 Files selected for processing (2)
  • __tests__/lib/install-check.test.ts
  • lib/install-check.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/lib/install-check.test.ts
  • lib/install-check.ts

📝 Walkthrough

Walkthrough

This PR removes npm lifecycle install scripts, adds CLI-driven install telemetry, instruments onboarding audits, and filters dashboard error telemetry to application-origin errors. It also updates related tests, documentation, changelog entries, and telemetry payload handling.

Changes

CLI-driven install reporting

Layer / File(s) Summary
Install-reporting flow
lib/install-check.ts, bin/failproofai.mjs, scripts/install-telemetry.mjs, __tests__/lib/install-check.test.ts, __tests__/scripts/install-telemetry.test.ts
Install and version events are emitted from non-hook CLI invocations with persisted version state, hook metadata, configurable timeouts, and failure containment.
Lifecycle and install documentation cleanup
package.json, docs/package-aliases.mdx, scripts/install-diagnosis.mjs, CHANGELOG.md
The postinstall and preuninstall lifecycle entries are removed, and related documentation describes the updated installation behavior and dogfood hook launcher.

Audit telemetry

Layer / File(s) Summary
Onboarding audit telemetry flow
src/audit/cli.ts, __tests__/audit/audit-cli-telemetry.test.ts, CHANGELOG.md
Onboarding audits emit started, completed, and sanitized failed events with source: "onboarding" while explicit CLI audits use shared completion fields.

Dashboard error-origin filtering

Layer / File(s) Summary
Error-origin classification
lib/error-origin.ts, __tests__/lib/error-origin.test.ts
Error traces and filenames are classified as application, extension, or unknown, with isAppError exposing positive application attribution.
Global listener filtering
app/components/global-error-listeners.tsx, __tests__/components/global-error-listeners.test.tsx, CHANGELOG.md
Global errors and promise rejections are reported only when attributed to the application origin; extension and unattributable errors are ignored.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant InstallCheck
  participant Telemetry
  CLI->>InstallCheck: run maybeReportInstall(version)
  InstallCheck->>Telemetry: emit install/version events
  InstallCheck-->>CLI: continue despite reporting failures
Loading
sequenceDiagram
  participant Onboarding
  participant AuditRunner
  participant PostHog
  Onboarding->>PostHog: emit cli_audit_started
  Onboarding->>AuditRunner: run audit
  AuditRunner-->>Onboarding: results or error
  Onboarding->>PostHog: emit completed or failed event
Loading
sequenceDiagram
  participant Window
  participant Listeners
  participant ErrorOrigin
  participant Telemetry
  Window->>Listeners: dispatch browser error
  Listeners->>ErrorOrigin: classify origin
  ErrorOrigin-->>Listeners: app or non-app result
  Listeners->>Telemetry: report app-origin errors
Loading

Possibly related PRs

Suggested labels: bug

Suggested reviewers: hermes-exosphere

Poem

I’m a rabbit with telemetry bright,
Hopping installs into CLI light.
Audits now report each trail,
App-born errors pass the rail.
Extension noise fades from view—
Squeak! Clean signals, fresh and new.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is informative, but it does not follow the repository template and omits the Type of Change and Checklist sections. Rewrite it using the template headings, add a Type of Change selection, and include the checklist items for lint, tsc, tests, and build.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: install warning removal, onboarding audit telemetry, and dashboard error filtering.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@hermes-exosphere

Copy link
Copy Markdown
Contributor

Automated code review started - full review. Results will be posted here.

@hermes-exosphere

Copy link
Copy Markdown
Contributor

Phase 1-3 complete: cloned, fetched, diff analyzed, tests pass.

  • 13 files changed (580+, 919-)
  • Build: clean tsc --noEmit, 0 lint errors (5 pre-existing warnings unrelated)
  • Tests: ALL 2090 passed (121 files)
  • Now diving into deep logical analysis...

Comment thread lib/install-check.ts
@hermes-exosphere

Copy link
Copy Markdown
Contributor

Automated Code Review

Executive Summary

This PR removes npm lifecycle scripts (postinstall/preuninstall) from package.json because npm 12+ blocks dependency install scripts by default, causing npm install failproofai to emit a warning. Install telemetry is relocated to the CLI via lib/install-check.ts, firing on the first non-hook invocation. Code quality is excellent — comprehensive failure containment, thorough test coverage, and clear documentation. No breaking changes for users.


Change Architecture

graph TD
    A["package.json (removed scripts)"] -->|"drops"| B["scripts/postinstall.mjs (deleted 242 lines)"]
    A -->|"drops"| C["scripts/preuninstall.mjs (deleted 131 lines)"]
    D["bin/failproofai.mjs (runCli entrypoint)"] -->|"calls"| E["lib/install-check.ts (NEW: 215 lines)"]
    E -->|"uses"| F["scripts/install-telemetry.mjs (modified: version param)"]
    E -->|"checks"| G["~/.failproofai/last-version"]
    E -->|"reports"| H["PostHog (first_install/version_changed/package_installed)"]
    style E fill:#90EE90
    style F fill:#87CEEB
    style B fill:#FFB3B3
    style C fill:#FFB3B3
    style D fill:#87CEEB
Loading

Legend: Green = New, Blue = Modified, Red = Removed


Breaking Changes

No breaking changes detected. The version parameter is backward-compatible: opts.version ?? process.env.npm_package_version ?? "unknown" preserves old lifecycle-script behavior.

Behavioural changes (documented in PR description):

  • package_installed now measures install to activation, not raw installs. PostHog series will visibly step, but net volume likely rises (recovers bun/pnpm/Yarn users).
  • Same-version reinstalls (direction: "reinstall") no longer reported — CLI has no signal to detect them.
  • Install-time welcome message removed; configure-wizard.ts already handles first-run redirect.
  • preuninstall was dead code (npm ignores uninstall lifecycle scripts) — its removal surfaces the pre-existing bug that __failproofai_hook__ entries linger after uninstall. PR description flags this for follow-up.

Issues Found

No critical or warning-level issues found.

  1. Infolib/install-check.ts:91checkHooks() only inspects ~/.claude/settings.json for hooks_registered, undercounting for non-Claude CLI users. Preserved behaviour from old postinstall.mjs; commented explicitly. Non-blocking. (See inline comment.)

Logical / Bug Analysis

Failure containment (excellent):

  • maybeReportInstall(): 3 layers of try/catch — outer (L149), version-file read (L153-158), version-file write (L204-208)
  • checkHooks(): handles missing config, corrupt JSON, missing settings, missing hooks — all degrade gracefully
  • runCli() caller: wraps in its own try/catch (L130-135)
  • Telemetry timeout capped at 2s (vs old 5s default) — shorter is correct, since this sits in front of every CLI command
  • Version recorded before awaiting telemetry (L204-209) — prevents re-reporting on repeated delivery failures

Semver comparison (compareSemver):

  • Release > prerelease (semver 11): tested
  • Numeric prerelease ordering (beta.2 < beta.10): tested
  • Major/minor/patch numerical comparison: tested
  • Correctly handles pre-release splits on . and -

Steady-state performance:

  • Early return on L162: if (previousVersion === version) return; — one existsSync() call on the version file
  • checkHooks() only called on non-steady-state runs — correct placement

No race conditions, no resource leaks, no security issues.


Evidence — Build and Test Results

Build: tsc --noEmit clean (0 errors), eslint 0 errors (5 pre-existing warnings unrelated to PR).

Tests: ALL 2090 passed across 121 files in 72.58s.

  • New: __tests__/lib/install-check.test.ts (18 tests, 130ms)
  • New: __tests__/scripts/install-telemetry.test.ts (4 tests, 77ms, 2 new)
  • Removed: __tests__/scripts/postinstall.test.ts (208 lines), __tests__/scripts/preuninstall.test.ts (319 lines)

Issue Linkage

No issue linked. Consider creating one for better traceability — the PR description is thorough enough to serve as documentation, but an issue reference helps future archaeologists.


Human Review Feedback

  • CodeRabbit: Skipped review (draft detected). No human review comments on this PR yet.
  • No unresolved threads exist from previous reviews.

Suggestions

  1. lib/install-check.ts:92-94checkHooks() only checks ~/.claude/settings.json. For multi-CLI users (Codex, OpenCode, Hermes, etc.), hooks_registered will be false even when hooks are active elsewhere. The JSDoc already documents this. (Non-blocking, preserved behaviour.)

  2. Pre-existing follow-up flagged in PR description: preuninstall removal surfaces the bug that __failproofai_hook__ entries are never cleaned up on uninstall since npm ignores uninstall scripts. An explicit failproofai policies --uninstall is needed before removing the package. Worth creating an issue.


Verdict

VERDICT: APPROVED


Automated code review · 2026-07-17 09:00 UTC

@hermes-exosphere hermes-exosphere left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: Approved. Code quality is excellent — thorough failure containment, comprehensive tests (18 new tests for install-check, 2 new for telemetry), clean build. No breaking changes. See summary comment for full analysis.

@hermes-exosphere hermes-exosphere left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: Approved. ✅

@chhhee10
chhhee10 force-pushed the worktree-npm-install-scripts branch from d452f0d to 522f490 Compare July 17, 2026 10:34
@chhhee10 chhhee10 changed the title fix: stop shipping npm lifecycle scripts (removes the npm install warning) fix: npm install warning, onboarding audit telemetry, and dashboard error noise Jul 17, 2026
@hermes-exosphere

Copy link
Copy Markdown
Contributor

Automated code review started - full review. Results will be posted here.

@hermes-exosphere

Copy link
Copy Markdown
Contributor

Hermes review in progress — Phase 1-3 complete

  • 19 files changed (1031+, 929-), 3 commits
  • Build: tsc --noEmit clean (0 errors)
  • Lint: 0 new errors (only pre-existing warnings in unrelated files)
  • Tests: ALL 2117 passed (123 files)
  • Now performing deep logical analysis...

Comment thread src/audit/cli.ts
@hermes-exosphere

Copy link
Copy Markdown
Contributor

Automated Code Review - Hermes Agent

Executive Summary

This PR delivers three tightly-scoped telemetry/packaging fixes: (1) removes npm lifecycle scripts to eliminate npm warn on install, relocating install telemetry to the CLI; (2) restores PostHog reporting for the onboarding auto-audit that was silently invisible; (3) filters browser extension errors from the dashboard error listeners. Code quality is excellent — well-documented JSDoc, thorough tests (313 new test lines for install-check alone), clean type system usage. No regressions detected.


Change Architecture

graph TD
    A["package.json<br/>-postinstall<br/>-preuninstall"] -->|"removed"| B["lib/install-check.ts<br/>(new: CLI-driven telemetry)"]
    B -->|"called from top of runCli()"| C["bin/failproofai.mjs<br/>(after --hook fast path)"]
    B -->|"version tracking"| D["scripts/install-telemetry.mjs<br/>(+opts.version, +opts.timeoutMs)"]
    
    E["src/audit/cli.ts<br/>runPostSetupAudit()"] -->|"3 new telemetry events"| F["PostHog<br/>(source=onboarding)"]
    
    G["lib/error-origin.ts<br/>(new: attribution)"] -->|"isAppError()"| H["global-error-listeners.tsx<br/>(filters extensions)"]
    
    style B fill:#90EE90
    style G fill:#90EE90
    style A fill:#FFD700
    style E fill:#87CEEB
Loading

Breaking Changes

No breaking changes detected. All public APIs, config keys, and serialization formats are unchanged.


Logical / Bug Analysis

Commit 1 — d1bb141 fix: stop shipping npm lifecycle scripts

  • maybeReportInstall() correctly placed at the top of runCli(), AFTER the --hook fast path (~line 78) exits. The hook path runs on every tool call; this fires only on user CLI invocations.
  • Steady-state path (previousVersion === version) returns after a single existsSync() — O(1) cost per command.
  • Version file written BEFORE awaiting events (line ~198). Tradeoff: if all events fail after write, the version is forever lost. But without this, a persistent network failure would re-attempt on every command. The PR documents this choice explicitly.
  • compareSemver() handles prerelease tags per semver section 11 — numeric identifiers rank below non-numeric ones, and a release outranks the same version with a prerelease tag.
  • Noted: The printHooksWarning() from the old postinstall.mjs is no longer called. The warning about hooks being configured but not registered and the welcome message are gone from install time. The PR explicitly states first-run onboarding is now handled by the config wizard, which is the right place for it.
  • Noted: REPORT_TIMEOUT_MS = 2000 is tighter than the global trackInstallEvent default of 5000ms. Good for CLI responsiveness, might drop events on very slow connections.

Commit 2 — f6568f5 fix(audit): report the onboarding auto-audit to PostHog

  • runPostSetupAudit() now emits cli_audit_started (fire-and-forget), cli_audit_completed (awaited, before empty-history return), and cli_audit_failed (awaited, in catch).
  • auditCompletedProps() helper ensures runAuditCli and runPostSetupAudit report identical property shapes — the two paths cannot drift.
  • cli_audit_completed fires BEFORE the if (result.eventsScanned === 0) return — a fresh user with no agent history is still counted, matching runAuditCli behavior.
  • source: "onboarding" vs source: "cli" correctly distinguishes the two paths in PostHog.

Commit 3 — 522f490 fix(dashboard): report only our own errors

  • classifyErrorOrigin() uses positive attribution: only errors traceable to the app origin are reported. Uses regex /\b[\w-]+-extension:\/\//i matching the shared -extension:// suffix — new browser extension schemes are filtered without code changes.
  • Extension frames are checked FIRST: if any frame in the stack is from an extension, the error is classified as "extension" even if app frames appear lower. This is correct — the extension owns the error.
  • isAppError() returns true ONLY for "app" origin — "unknown" (cross-origin "Script error.", non-Error rejections) are dropped. Unactionable errors are not worth reporting.
  • React render errors are unaffected: error boundaries report client_error directly, and React only invokes those for errors in our own tree.

Evidence — Build & Test Results

Build:

$ bunx tsc --noEmit
(0 errors — clean)

Lint:

$ bun eslint lib/install-check.ts lib/error-origin.ts app/components/global-error-listeners.tsx
(clean — 0 errors, 0 warnings on changed files)

Tests:

$ bun run test:run
Test Files  123 passed (123)
Tests      2117 passed (2117)
Duration     131.95s

New/changed test files:
- __tests__/lib/install-check.test.ts  — 18 tests (313 lines, new)
- __tests__/lib/error-origin.test.ts   — 15 tests (111 lines, new)
- __tests__/components/global-error-listeners.test.tsx — 6 tests (112 lines, new)
- __tests__/audit/audit-cli-telemetry.test.ts — +6 tests (post-setup onboarding)

Issue Linkage

No linked issues. All three fixes appear to be directly observed production issues rather than issue-tracked bugs. Consider creating issues for traceability.


Human Review Feedback

No human review comments on this PR. 1 previous bot comment (re: checkHooks inspecting only Claude settings) — resolved as the behavior is preserved and documented.


Suggestions

  1. The cli_audit_started fire-and-forget asymmetry with cli_audit_completed (awaited) — intentional and correct in practice, but the comment on line 276 could mention the implicit assumption that runWithProgress() always takes long enough.
  2. Preuninstall gap — The PR documents that preuninstall.mjs never ran and removing it reveals stranded __failproofai_hook__ entries. Consider a follow-up issue/PR for a failproofai policies --uninstall command users should run before uninstalling.

Verdict

VERDICT: APPROVED


Hermes Agent code review · 2026-07-17 11:03 UTC

@hermes-exosphere hermes-exosphere left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: Approved. All 2117 tests pass, 0 type errors, 0 new lint issues. Three well-scoped fixes: npm lifecycle scripts removed, onboarding audit telemetry restored, dashboard error noise filtered. Clean code with thorough test coverage.

@hermes-exosphere hermes-exosphere left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: Approved. ✅

@hermes-exosphere hermes-exosphere left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Verdict: APPROVED

Reviewed all 19 files (+1031 / -929 lines). All CI checks pass. 2117 tests pass locally. No merge conflicts, no leaked secrets, no debug statements in live code.


Commit 1: Stop shipping npm lifecycle scripts

What it does: Deletes scripts/postinstall.mjs (242 lines) and scripts/preuninstall.mjs (131 lines) + their tests (527 lines). Moves install telemetry (first_install, version_changed, package_installed) to lib/install-check.ts, called from runCli() deliberately outside the --hook fast path. trackInstallEvent gains an explicit opts.version parameter since npm only sets npm_package_version inside lifecycle scripts.

Correctness: Dead-code removal is correct — preuninstall was verified to never execute (npm honours no uninstall lifecycle scripts). The semver comparison is a faithful port from the old script. Same-version guard (previousVersion === version) correctly prevents re-reporting on every CLI invocation. Version file is written before awaiting telemetry so delivery failures don't cause re-retries.

Security: hostname() is hashed with createHmac(sha256) before transmission — same pattern as old code. Pre-existing PostHog API key is a phc_ public client-side key (not a secret). No new credential exposure.

Caveat (pre-existing, now visible): Removing the dead preuninstall — which never ran — makes visible that uninstalling failproofai strands __failproofai_hook__ entries in settings files. Needs failproofai policies --uninstall. Not a blocker, but worth a follow-up issue.


Commit 2: Report the onboarding auto-audit

What it does: runPostSetupAudit() now emits cli_audit_started (fire-and-forget), cli_audit_completed (awaited, before empty-history early return), and cli_audit_failed (awaited). All tagged source: "onboarding". Properties extracted into a shared auditCompletedProps() helper so the cli and onboarding paths cannot drift.

Correctness: cli_audit_started is fire-and-forget (the multi-second scan keeps the process alive). cli_audit_failed is awaited (returns straight into dashboard boot). cli_audit_completed fires before the empty-history early return — matching runAuditCli behavior. Shared helper prevents property drift.

Testing: 6 new tests cover: happy path, source distinction, audit failure, zero-history case, opt-out, and process-never-exits guarantee.


Commit 3: Report only our own dashboard errors

What it does: New lib/error-origin.ts with classifyErrorOrigin() and isAppError(). GlobalErrorListeners component now attributes errors before reporting: only errors traceable to the app origin pass through; extensions (matched by <vendor>-extension:// suffix) and unattributable errors are silently dropped.

Correctness: Extension check runs first (before app-origin check) — correct, since an error passing through an extension frame is the extension's problem even if an app frame appears lower in the stack. Regex matches the shared -extension:// suffix rather than a vendor denylist — future browsers covered. Filename fallback handles ErrorEvents without stacks. Unmount cleanup removes listeners.

Testing: 17 new tests (6 component + 11 unit) covering all major browsers, mixed stacks, our-origin, cross-origin Script error., empty stacks, filename-only, unmount.

Tradeoff (acknowledged): Non-Error rejections Promise.reject("boom") are no longer reported (no stack for attribution). Rare; deliberate.


Overall

Category Assessment
Correctness Each fix addresses the stated problem precisely
Security No new secrets; hostname hashed; public phc_ key unchanged
Code Quality Clean separation (install-check vs hook path, error-origin as pure function, shared auditCompletedProps)
Testing 2117 tests pass (123 files); new code has comprehensive coverage with real-world edge cases
Performance Steady-state maybeReportInstall returns after one existsSync(); reporting timeout capped at 2s
Documentation Detailed PR body, inline JSDoc, CHANGELOG entries

Reviewed by Hermes Agent

chhhee10 and others added 3 commits July 17, 2026 17:55
npm 12 (released 2026-07-08, now the `latest` dist-tag) blocks dependency
install scripts by default, so `npm install failproofai` printed:

  npm warn install-scripts 1 package had install scripts blocked ...
  npm warn install-scripts   failproofai@... (postinstall: node scripts/postinstall.mjs)

and skipped the script. npm 11.16+ prints the advisory `allow-scripts`
variant and still runs it.

`allowScripts` is purely consumer-side — a package declaring `allowScripts`
or `trustedDependencies` for itself is ignored — so the only fix is to ship
no install scripts.

postinstall's install telemetry (first_install / version_changed /
package_installed, same names and properties) now fires from the CLI's first
non-hook invocation via lib/install-check.ts: at most once per version, and
a no-op once the version is recorded. It is deliberately outside the --hook
fast path in bin/failproofai.mjs, which runs on every tool call.

trackInstallEvent now takes an explicit version. npm only sets
npm_package_version inside a lifecycle script, so outside one it reported
"unknown".

The script's server.js check and shadowed-PATH diagnosis were redundant:
scripts/launch.ts already does both at launch, with a better message.

preuninstall is deleted as dead code — npm honours no uninstall lifecycle
scripts (verified with a probe package: postinstall fired, preuninstall did
not), so its hook cleanup has never run. Uninstalling therefore leaves
__failproofai_hook__ entries behind; that gap now needs an explicit
`failproofai policies --uninstall`.

Behaviour changes:
- package_installed measures install->activation, not raw installs.
- Same-version reinstalls (direction: "reinstall") are no longer reported;
  the CLI cannot distinguish one from an ordinary invocation.
- The install-time welcome message is dropped; the config wizard's first-run
  redirect already handles onboarding.

This also recovers telemetry already dropped for bun, pnpm and Yarn >=4.14
users, which have blocked install scripts for some time.

Verified against a local registry on npm 12: registry install is warning-free
(exit 0), the CLI runs, first_install/package_installed fire on first run,
nothing fires on the second, and version_changed fires on upgrade — all
carrying the real version and the existing $lib/product stamps.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
runPostSetupAudit() — the audit that runs automatically at the end of
first-run setup, and therefore the first audit any new user ever runs —
emitted no telemetry at all, while an explicit `failproofai audit` reported
cli_audit_started / cli_audit_completed / cli_audit_failed.

First-run audits were invisible, so the audit funnel silently undercounted
exactly the activation moment it exists to measure. Confirmed live: a fresh
install's auto-audit wrote its dashboard cache and PostHog saw nothing.

It now emits the same three events tagged `source: "onboarding"`, against the
existing `source: "cli"`, so the two paths stay distinguishable.

- cli_audit_completed fires before the empty-history return, matching
  runAuditCli, so a fresh user with no agent history is still counted.
- cli_audit_failed is awaited: this function returns straight into the
  dashboard boot, which would race a fire-and-forget send.
- The completed-event properties now come from one shared helper so the two
  entry points cannot drift apart.

Onboarding stays best-effort — never throws, never exits.

Verified by driving the real (unmocked) runPostSetupAudit against a local
capture server: cli_audit_started and cli_audit_completed both land with
source=onboarding and real counts (7,468 tool calls / 210 sessions), and the
function returns without throwing or exiting.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
GlobalErrorListeners registers page-global error/unhandledrejection
handlers. Browser extensions inject content scripts into the same page and
share the same window, so their failures reached our listeners and went to
PostHog stamped $lib: failproofai-web, as though the dashboard had thrown
them.

Observed live: MetaMask's "Failed to connect to MetaMask" (error_name "i",
its minified class) arriving as a failproofai unhandled_rejection on
/policies.

Extensions are user-installed and open-ended, so the noise was unbounded and
depended on which extensions a user happens to run rather than on our code.
Left alone it would drown the real signal these listeners exist to catch, and
make the dashboard's error rate a function of users' browser setups.

Both handlers now attribute an error before reporting it (new
lib/error-origin.ts) and report only what traces back to our own origin:

- Positive attribution, not a denylist of known extensions, so an
  unrecognised extension is filtered by default.
- The match is on the shared `<vendor>-extension://` suffix, so a new
  browser's scheme is covered without a code change.
- A stack passing through an extension is treated as the extension's, even
  when one of our frames appears further down.
- Unattributable errors (cross-origin "Script error.", rejections of
  non-Error values) are dropped — there is nothing in them to debug.

React render errors are unaffected: the error boundaries report client_error
directly, and React only invokes those for errors thrown inside our own tree.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hermes-exosphere

Copy link
Copy Markdown
Contributor

Automated code review started - full review. Results will be posted here.

The install events are the first thing a fresh process sends, so they always
pay the DNS + TLS handshake. Measured against production PostHog they deliver
in ~1.1s from cold, with a 1.85s worst case observed.

A 2s budget left almost no headroom over that, for a saving nobody
experiences: the timeout is a ceiling, not a cost — delivery resolves as soon
as PostHog answers. A tight budget only ever converts a slow network into a
silently dropped report, which is exactly what this module exists to prevent.

5s matches hook-telemetry and the postinstall script this replaces, and
install-telemetry's own default.

Verified by driving trackInstallEvent against real PostHog from a cold process
at both budgets, plus a transparent proxy confirming production returns
HTTP 200 {"status":"Ok"} for every event (14/14).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@chhhee10
chhhee10 force-pushed the worktree-npm-install-scripts branch from 70cac3a to 5daad39 Compare July 17, 2026 12:27
@chhhee10
chhhee10 marked this pull request as ready for review July 17, 2026 12:39
@hermes-exosphere

Copy link
Copy Markdown
Contributor

🔍 Hermes re-review in progress — Phase 0-2 complete, Phase 3 starting

  • 4 PR-specific commits (3 from last review + 1 new: )
  • 19 files changed (1058+, 929-)
  • 1 unresolved bot review thread (from previous review, re: cli_audit_started fire-and-forget)
  • 1 resolved thread (re: checkHooks Claude-only inspection)
  • Now building & running test suite...

@coderabbitai coderabbitai Bot added the bug Something isn't working label Jul 17, 2026
@hermes-exosphere

Copy link
Copy Markdown
Contributor

Resolved 1 previously-reported issue that has been addressed in this update (re: cli_audit_started fire-and-forget rationale now well-documented).

@hermes-exosphere

Copy link
Copy Markdown
Contributor

🔍 Hermes Agent Code Review — PR #560 (re-review)

📋 Executive Summary

This PR delivers three tightly-scoped telemetry/packaging fixes (npm lifecycle scripts, onboarding audit telemetry, dashboard error filtering) plus a 4th post-review commit raising install-reporting timeout from 2s→5s. Code quality remains excellent: thorough JSDoc, comprehensive test coverage, clean failure containment. 1 new commit since last review: 5daad39 — a targeted one-line change with a detailed rationale comment. No regressions, all 2200 tests pass.


📊 Change Architecture

graph TD
    A["package.json<br/>−postinstall<br/>−preuninstall"] -->|"removed"| B["lib/install-check.ts<br/>(CLI-driven telemetry)"]
    B -->|"v2: 5s timeout"| B2["5daad39: REPORT_TIMEOUT_MS 2000→5000"]
    B -->|"called from runCli()"| C["bin/failproofai.mjs<br/>(after --hook fast path)"]
    B -->|"version tracking"| D["scripts/install-telemetry.mjs<br/>(+opts.version, +opts.timeoutMs)"]
    
    E["src/audit/cli.ts<br/>runPostSetupAudit()"] -->|"3 telemetry events"| F["PostHog<br/>(source=onboarding)"]
    
    G["lib/error-origin.ts<br/>(new: attribution)"] -->|"isAppError()"| H["global-error-listeners.tsx<br/>(filters extensions)"]
    
    style B2 fill:#FFD700,stroke:#333,stroke-width:2px
    style B fill:#90EE90
    style G fill:#90EE90
    style E fill:#87CEEB
Loading

Legend: 🟢 New | 🔵 Modified | 🟡 Changed since last review


🔴 Breaking Changes

✅ No breaking changes detected. All public APIs, config keys, and serialization formats unchanged.


⚠️ Issues Found

No critical or warning-level issues.

  1. 🔵 Infolib/install-check.ts:43REPORT_TIMEOUT_MS = 5000: Raised from 2s→5s in 5daad39. The detailed JSDoc explains the trade-off: the 1.85s worst case seen in testing left barely any headroom at 2s, while 5s matches the timeout used by hook-telemetry and the old postinstall script. A ceiling, not a cost — delivery resolves as soon as PostHog answers (~1.1s typically). The right call for reliability on slow networks.

🔬 Logical / Bug Analysis

New commit 5daad39:

  • Single-target change: REPORT_TIMEOUT_MS 2000 → 5000 with expanded JSDoc.
  • Backward reasoning is solid: the prior 2s left ≈150ms headroom over the worst observed case (1.85s), a margin too slim for flaky networks.
  • The new value (5s) matches hook-telemetry.ts's cap and the old postinstall.mjs default.
  • No change to production semantics: the timeout is a cap, not a cost — delivery returns as soon as PostHog answers.

No regressions across the other 3 commits:

  • Same-version steady-state path returns after one existsSync() — O(1) per CLI invocation.
  • Failure containment: 3 layers of try/catch in maybeReportInstall(), 2s+ timeout, version recorded BEFORE awaiting.
  • isAppError() / classifyErrorOrigin() uses positive attribution — unmatched errors are dropped, which is correct.
  • runPostSetupAudit() emits cli_audit_completed BEFORE empty-history return, matching runAuditCli.

🧪 Evidence — Build & Test Results

Test Results
$ bun run test:run
 Test Files  125 passed (125)
      Tests  2200 passed (2200)
   Duration  54.45s

New/changed test files (all passing):
- __tests__/lib/install-check.test.ts  — 18 tests (328 lines, new)
- __tests__/lib/error-origin.test.ts   — 15 tests (111 lines, new)
- __tests__/components/global-error-listeners.test.tsx — 6 tests (112 lines, new)
- __tests__/audit/audit-cli-telemetry.test.ts — +6 tests (post-setup onboarding)
Lint
$ bun eslint lib/install-check.ts lib/error-origin.ts app/components/global-error-listeners.tsx src/audit/cli.ts scripts/install-telemetry.mjs bin/failproofai.mjs
(clean — 0 errors, 0 warnings)

🔗 Issue Linkage

⚠️ No linked issues. All fixes are directly-observed production issues. Consider creating issues for traceability.


👥 Human Review Feedback

No human review comments on this PR.

Previous bot (hermes-exosphere) review threads:

  • ✅ Resolved: cli_audit_started fire-and-forget rationale — comment on lines 277-279 now explicitly documents the multi-second scan assumption.
  • ✅ Previously resolved: checkHooks() Claude-only inspection is preserved behaviour, documented by JSDoc.

💡 Suggestions

  1. lib/install-check.ts:108 (preserved from old postinstall): checkHooks() only inspects ~/.claude/settings.json, undercounting hooks_registered for users running failproofai against other CLIs (Codex, OpenCode, Hermes, etc.). JSDoc already documents this explicitly. Non-blocking, future improvement opportunity.
  2. Preuninstall gap: Removing preuninstall.mjs surfaces the pre-existing bug that __failproofai_hook__ entries strand after uninstall. An explicit failproofai policies --uninstall is the workaround, but worth creating a follow-up issue to implement cleanup differently.

🏆 Verdict

VERDICT: APPROVED

Hermes Agent code review · 2026-07-17 18:12 UTC

@hermes-exosphere hermes-exosphere left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated re-review: Approved. ✅

  • 1 new commit since last review (: 2s→5s timeout, with rationale)
  • All 2200 tests pass across 125 files
  • Clean lint on all changed files
  • Previous review threads addressed and resolved
  • No regressions detected

@hermes-exosphere hermes-exosphere left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: Approved. ✅

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
__tests__/components/global-error-listeners.test.tsx (1)

1-8: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Align test file path with application directory structure.

Consider moving this test file to __tests__/app/components/global-error-listeners.test.tsx to maintain structural consistency. Based on learnings, unit tests should be placed by feature area under __tests__/ to mirror the corresponding source structure.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@__tests__/components/global-error-listeners.test.tsx` around lines 1 - 8,
Move the global error listeners test from the top-level components test
directory into the app-mirrored directory
__tests__/app/components/global-error-listeners.test.tsx, preserving its
contents and existing test behavior.

Source: Learnings

app/components/global-error-listeners.tsx (1)

37-50: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Simplify the handleUnhandledRejection logic with an early return.

Since isAppError will predictably return false if the stack is undefined (because filename is also absent), a rejection with a non-Error reason is guaranteed to be ignored anyway.

You can add an explicit early return to handle this upfront. This leverages TypeScript's type narrowing, eliminating the need to duplicate the instanceof checks and ternaries when constructing the telemetry payload.

♻️ Proposed refactor
-      // A rejection carries no filename, so the stack is the only evidence of
-      // where it came from. A non-Error reason has none and stays unattributed.
-      if (
-        !isAppError({
-          stack: reason instanceof Error ? reason.stack : undefined,
-          appOrigin: window.location.origin,
-        })
-      ) {
+      // A rejection carries no filename, so the stack is the only evidence of
+      // where it came from. A non-Error reason has none and stays unattributed.
+      if (!(reason instanceof Error)) {
+        return;
+      }
+
+      if (
+        !isAppError({
+          stack: reason.stack,
+          appOrigin: window.location.origin,
+        })
+      ) {
         return;
       }
       captureClientEvent("unhandled_rejection", {
-        error_message: reason instanceof Error ? reason.message : String(reason),
-        error_name: reason instanceof Error ? reason.name : undefined,
+        error_message: reason.message,
+        error_name: reason.name,
       });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/components/global-error-listeners.tsx` around lines 37 - 50, Update
handleUnhandledRejection to return immediately when reason is not an Error, then
pass the narrowed Error’s stack to isAppError and its message and name directly
to captureClientEvent, preserving the existing unattributed-rejection filtering.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Line 14: Update the npm 12 release date in the changelog entry to July 15,
2026, matching the registry information, or remove the exact date while
preserving the surrounding explanation about npm 12 and allowScripts.

In `@lib/install-check.ts`:
- Around line 138-143: Validate that matcher.hooks is an array before invoking
.some() in the hook inspection logic, while preserving the existing marker
detection and return behavior for valid arrays. Ensure malformed entries such as
hooks: {} are skipped without throwing so the surrounding install-check flow can
continue normally.
- Around line 58-92: Update the prerelease parsing in compareSemver to split
identifiers only on dots, preserving hyphens as part of a single identifier.
Ensure complete non-numeric identifiers such as beta-2 and beta-10 are compared
lexically, and add a regression case covering 1.0.0-beta-2 versus 1.0.0-beta-10.

In `@scripts/install-telemetry.mjs`:
- Line 117: Add a unit test covering the configurable timeout in the install
telemetry flow, passing timeoutMs: 1234 and asserting that AbortSignal.timeout
is called with 1234. Keep the existing test coverage for the version option
unchanged.

---

Nitpick comments:
In `@__tests__/components/global-error-listeners.test.tsx`:
- Around line 1-8: Move the global error listeners test from the top-level
components test directory into the app-mirrored directory
__tests__/app/components/global-error-listeners.test.tsx, preserving its
contents and existing test behavior.

In `@app/components/global-error-listeners.tsx`:
- Around line 37-50: Update handleUnhandledRejection to return immediately when
reason is not an Error, then pass the narrowed Error’s stack to isAppError and
its message and name directly to captureClientEvent, preserving the existing
unattributed-rejection filtering.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3cd80ed1-928a-4689-8e10-3705869979fb

📥 Commits

Reviewing files that changed from the base of the PR and between c501246 and 5daad39.

📒 Files selected for processing (19)
  • CHANGELOG.md
  • __tests__/audit/audit-cli-telemetry.test.ts
  • __tests__/components/global-error-listeners.test.tsx
  • __tests__/lib/error-origin.test.ts
  • __tests__/lib/install-check.test.ts
  • __tests__/scripts/install-telemetry.test.ts
  • __tests__/scripts/postinstall.test.ts
  • __tests__/scripts/preuninstall.test.ts
  • app/components/global-error-listeners.tsx
  • bin/failproofai.mjs
  • docs/package-aliases.mdx
  • lib/error-origin.ts
  • lib/install-check.ts
  • package.json
  • scripts/install-diagnosis.mjs
  • scripts/install-telemetry.mjs
  • scripts/postinstall.mjs
  • scripts/preuninstall.mjs
  • src/audit/cli.ts
💤 Files with no reviewable changes (5)
  • tests/scripts/preuninstall.test.ts
  • scripts/preuninstall.mjs
  • scripts/postinstall.mjs
  • tests/scripts/postinstall.test.ts
  • package.json

Comment thread CHANGELOG.md
Comment thread lib/install-check.ts
Comment thread lib/install-check.ts
Comment thread scripts/install-telemetry.mjs
@hermes-exosphere

Copy link
Copy Markdown
Contributor

Automated code review started - full review. Results will be posted here.

Semver §9 separates prerelease identifiers with dots; a hyphen is a legal
character *inside* one. So `beta-2` is a single non-numeric identifier and
compares lexically against `beta-10` — making it GREATER ("2" > "1").

Splitting on /[.-]/ shredded it into ["beta", "2"] and compared numerically,
producing the opposite order:

  1.0.0-beta-2 vs 1.0.0-beta-10 → spec: greater · was: less

Inherited verbatim from postinstall.mjs, so not a regression, and harmless for
our own dot-separated versions — but wrong, and this module is the place it
lives now.

Caught by CodeRabbit on #560; verified against the spec and pinned with a
regression test that fails on the old split.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@hermes-exosphere

Copy link
Copy Markdown
Contributor

Hermes review in progress — Phase 3: Running test suite...

Comment thread lib/install-check.ts
@hermes-exosphere

Copy link
Copy Markdown
Contributor

🔍 Hermes re-review in progress — 1 new commit since last review (ced8e13)

@hermes-exosphere

Copy link
Copy Markdown
Contributor

Resolved 1 previously-reported issue: compareSemver now splits prerelease identifiers on dots only (commit ced8e13), with a regression test for beta-2 vs beta-10 — matching semver §9.

@hermes-exosphere

Copy link
Copy Markdown
Contributor

🔍 Hermes Agent Code Review — PR #560 (re-review)

Executive Summary

This is the 4th review of this PR. 1 new commit since the last full review: ced8e13 fixes a semver prerelease comparison bug where /[.-]/ incorrectly split hyphenated identifiers like beta-2 into two parts. Caught by CodeRabbit, verified against semver §9, and pinned with a regression test. The rest of the PR (npm lifecycle removal, onboarding audit telemetry, dashboard error filtering) is unchanged from the prior reviewed state. Code quality remains excellent. 1 unresolved suggestion from the previous review (defense-in-depth Array.isArray() on L145 — non-blocking).


New Commit: ced8e13

fix(install-check): split prerelease identifiers on dots only

  • Before: pa.pre.split(/[.-]/) — treated hyphens as separators, incorrectly splitting beta-2 into ["beta", "2"] instead of treating it as one identifier per semver §9.
  • After: pa.pre.split(".") — correctly preserves hyphens as part of a single identifier.
  • Impact: 1.0.0-beta-2 vs 1.0.0-beta-10 now compares correctly (lexical: "2" > "1" → greater, was: numeric 2 < 10 → less).
  • Scope: Harmless for failproofai's own dot-separated versions (0.0.14-beta.0), but correct per spec. Inherited verbatim from old postinstall.mjs, now fixed where it lives.
  • Test: Regression test added — compareSemver("1.0.0-beta-2", "1.0.0-beta-10") expects > 0 and fails on the old split.
  • JSDoc: Updated to document the semver §9 rule.

✅ Correct fix, clean commit, well-tested.


Overall State

Area Verdict
npm lifecycle removal ✅ Clean — postinstall.mjs (242 lines) and preuninstall.mjs (131 lines) deleted, telemetry moved to lib/install-check.ts called from runCli()
Onboarding audit telemetry ✅ Clean — runPostSetupAudit() now emits cli_audit_started/completed/failed with source: "onboarding"
Dashboard error filtering ✅ Clean — GlobalErrorListeners filters via isAppError() from lib/error-origin.ts
Semver fix (new) ✅ Clean — correct per semver §9, regression test pinned

Build & Test Evidence

Lint: eslint clean on all 6 changed source files (0 errors, 0 warnings)

Tests (changed files):
  ✓ __tests__/lib/install-check.test.ts (20 tests) 163ms
  ✓ __tests__/audit/audit-cli-telemetry.test.ts (9 tests) 164ms
  ✓ __tests__/scripts/install-telemetry.test.ts (4 tests) 71ms
  ✓ __tests__/lib/error-origin.test.ts (15 tests) 29ms
  ✓ __tests__/components/global-error-listeners.test.tsx (6 tests) 45ms

Full suite: 2201 tests, 124/125 files passing
  - 1 pre-existing failure: integrations.test.ts (path resolution issue, unrelated to PR)

Unresolved from Previous Review

  1. 💡 lib/install-check.ts:145if (!matcher?.hooks) continue; guards against null/undefined but not {hooks: {}} (a truthy object). Array.isArray() would add defense-in-depth against a malformed settings file. In practice failproofai always writes arrays, so this is non-blocking. The CodeRabbit bot flagged the same issue.

Issue Linkage

No linked issues. All fixes are directly-observed production issues. Suggest creating issues for traceability.


Verdict

VERDICT: APPROVED

The ced8e13 commit is a minimal, correct fix for a semver edge case — caught by tooling, verified against spec, and regression-tested. The rest of the PR remains approved as before. The one unresolved Array.isArray() suggestion is defense-in-depth only and does not block merge.


Hermes Agent code review · 2026-07-17 13:15 UTC

@hermes-exosphere hermes-exosphere left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Hermes Agent Code Review — PR #560 (re-review)

Executive Summary

This is the 4th review of this PR. 1 new commit since the last full review: ced8e13 fixes a semver prerelease comparison bug where /[.-]/ incorrectly split hyphenated identifiers like beta-2 into two parts. Caught by CodeRabbit, verified against semver §9, and pinned with a regression test. The rest of the PR (npm lifecycle removal, onboarding audit telemetry, dashboard error filtering) is unchanged from the prior reviewed state. Code quality remains excellent. 1 unresolved suggestion from the previous review (defense-in-depth Array.isArray() on L145 — non-blocking).


New Commit: ced8e13

fix(install-check): split prerelease identifiers on dots only

  • Before: pa.pre.split(/[.-]/) — treated hyphens as separators, incorrectly splitting beta-2 into ["beta", "2"] instead of treating it as one identifier per semver §9.
  • After: pa.pre.split(".") — correctly preserves hyphens as part of a single identifier.
  • Impact: 1.0.0-beta-2 vs 1.0.0-beta-10 now compares correctly (lexical: "2" > "1" → greater, was: numeric 2 < 10 → less).
  • Scope: Harmless for failproofai's own dot-separated versions (0.0.14-beta.0), but correct per spec. Inherited verbatim from old postinstall.mjs, now fixed where it lives.
  • Test: Regression test added — compareSemver("1.0.0-beta-2", "1.0.0-beta-10") expects > 0 and fails on the old split.
  • JSDoc: Updated to document the semver §9 rule.

✅ Correct fix, clean commit, well-tested.


Overall State

Area Verdict
npm lifecycle removal ✅ Clean — postinstall.mjs (242 lines) and preuninstall.mjs (131 lines) deleted, telemetry moved to lib/install-check.ts called from runCli()
Onboarding audit telemetry ✅ Clean — runPostSetupAudit() now emits cli_audit_started/completed/failed with source: "onboarding"
Dashboard error filtering ✅ Clean — GlobalErrorListeners filters via isAppError() from lib/error-origin.ts
Semver fix (new) ✅ Clean — correct per semver §9, regression test pinned

Build & Test Evidence

Lint: eslint clean on all 6 changed source files (0 errors, 0 warnings)

Tests (changed files):
  ✓ __tests__/lib/install-check.test.ts (20 tests) 163ms
  ✓ __tests__/audit/audit-cli-telemetry.test.ts (9 tests) 164ms
  ✓ __tests__/scripts/install-telemetry.test.ts (4 tests) 71ms
  ✓ __tests__/lib/error-origin.test.ts (15 tests) 29ms
  ✓ __tests__/components/global-error-listeners.test.tsx (6 tests) 45ms

Full suite: 2201 tests, 124/125 files passing
  - 1 pre-existing failure: integrations.test.ts (path resolution issue, unrelated to PR)

Unresolved from Previous Review

  1. 💡 lib/install-check.ts:145if (!matcher?.hooks) continue; guards against null/undefined but not {hooks: {}} (a truthy object). Array.isArray() would add defense-in-depth against a malformed settings file. In practice failproofai always writes arrays, so this is non-blocking. The CodeRabbit bot flagged the same issue.

Issue Linkage

No linked issues. All fixes are directly-observed production issues. Suggest creating issues for traceability.


Verdict

VERDICT: APPROVED

The ced8e13 commit is a minimal, correct fix for a semver edge case — caught by tooling, verified against spec, and regression-tested. The rest of the PR remains approved as before. The one unresolved Array.isArray() suggestion is defense-in-depth only and does not block merge.


Hermes Agent code review · 2026-07-17 13:15 UTC

@hermes-exosphere

Copy link
Copy Markdown
Contributor

Hermes Agent Code Review — PR #560

Executive Summary

This PR delivers three tightly-scoped telemetry/packaging fixes: (1) removes npm lifecycle scripts to eliminate npm warn on install, relocating install telemetry to the CLI; (2) restores PostHog reporting for the onboarding auto-audit that was silently invisible; (3) filters browser extension errors from the dashboard error listeners. Code quality is excellent — well-documented JSDoc, thorough tests (331 new test lines), clean failure containment everywhere. One new commit (ced8e13) since the last review addresses the coderabbit compareSemver concern. All 2201 tests pass, tsc --noEmit is clean, 0 new lint errors.


Change Architecture

graph TD
    A[package.json] -->|REMOVED: postinstall + preuninstall scripts| B[lib/install-check.ts NEW]
    B -->|called from runCli| C[bin/failproofai.mjs]
    B -->|trackInstallEvent with explicit version| D[scripts/install-telemetry.mjs]
    E[src/audit/cli.ts] -->|NEW: telemetry for onboarding audit| F[PostHog]
    G[app/components/global-error-listeners.tsx] -->|NEW: filter via| H[lib/error-origin.ts NEW]
    H -->|positive attribution| I[captureClientEvent]
    style B fill:#90EE90
    style H fill:#90EE90
    style A fill:#FFD700
    style D fill:#87CEEB
    style E fill:#87CEEB
    style G fill:#87CEEB
Loading

Legend: Green = New | Blue = Modified | Gold = Breaking Change Risk


Breaking Changes

No breaking changes detected. The removed postinstall script was already silently skipped by npm 12+, bun, pnpm, and Yarn >=4.14. Install telemetry is recovered via the CLI path. The removed preuninstall script never ran (npm honours no uninstall lifecycle scripts). The PR correctly notes this as visible dead code removal.


Issues Found

  1. [TIP] lib/install-check.ts:145matcher.hooks.some() on a non-array object would throw. The !matcher?.hooks guard skips null/undefined but not {}. Defense-in-depth Array.isArray() recommended to match the outer loop pattern at line 143. [inline comment posted]

Logical / Bug Analysis

lib/install-check.ts — maybeReportInstall:

  • Correct early-return for steady state (line 178: if (previousVersion === version) return;)
  • Version is recorded BEFORE awaiting telemetry (line 220-224), so failed delivery doesn't retry forever
  • Promise.all(events.map(p => p.catch(...))) correctly swallows individual event failures (line 227)
  • Top-level catch at line 228 ensures reporting never breaks commands
  • checkHooks() correctly handles missing/corrupt files with try/catch

lib/error-origin.ts — classifyErrorOrigin:

  • Extension URL regex (/\b[\w-]+-extension:\/\//i) correctly matches any vendor prefix via suffix match
  • Extension check runs BEFORE app-origin check (line 55-56) — correct priority: extensions win
  • Empty inputs handled: filter(Boolean).join("\n") -> .trim() -> "unknown" when both falsy
  • appOrigin && text.includes(appOrigin) guards against empty origin with falsey check

src/audit/cli.ts — runPostSetupAudit:

  • cli_audit_started is fire-and-forget (line 280) — safe because the multi-second scan below keeps the process alive
  • cli_audit_failed is awaited (line 292) — correct, because function returns straight into dashboard boot
  • cli_audit_completed fires before the empty-history return (line 306) — correctly matching runAuditCli
  • auditCompletedProps(...) shared helper prevents drift between the two entry points

app/components/global-error-listeners.tsx:

  • Clean unmount with removeEventListener in the effect cleanup (lines 56-58)
  • Non-Error rejection reasons handled: reason instanceof Error ? reason.message : String(reason) (line 48)

Build and Test Results

Test Results
Test Files  125 passed (125)
     Tests  2201 passed (2201)
   Start at  18:36:45
   Duration  98.68s
Type Check
bunx tsc --noEmit
# Exit 0 — clean, no errors
Lint
5 problems (0 errors, 5 warnings) — all pre-existing
No new lint errors introduced

Issue Linkage

This PR addresses installation warning behavior tracked internally. No standalone issue linked.


Review Thread Status (from previous reviews)

Thread Author Status
checkHooks Claude-only inspection hermes-exosphere [PASS] Already resolved
cli_audit_started fire-and-forget hermes-exosphere [PASS] Already resolved
compareSemver prerelease splitting coderabbitai [PASS] Resolved by ced8e13
CHANGELOG npm 12 release date coderabbitai Noted — date is correct (GitHub release confirms 2026-07-08)
matcher.hooks Array.isArray coderabbitai [WARN] Not yet addressed — new inline comment posted
install-telemetry timeoutMs test coderabbitai Noted — Timeout verified via integration test

Suggestions

  • Consider Array.isArray(matcher?.hooks) at line 145 for defense-in-depth — matches the pattern at line 143 and prevents a theoretical crash on corrupted settings files.

Verdict

VERDICT: APPROVED


Hermes Agent code review · 2026-07-17 13:16 UTC

@hermes-exosphere hermes-exosphere left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: Approved. All tests pass (2201/2201), clean tsc, no new lint errors. One non-blocking suggestion posted inline. [PASS]

@hermes-exosphere

Copy link
Copy Markdown
Contributor

Resolved 1 previously-reported issuecli_audit_started fire-and-forget rationale now well-documented in the code comment on L277-279.

@hermes-exosphere hermes-exosphere left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: Approved. ✅

@hermes-exosphere

Copy link
Copy Markdown
Contributor

Automated code review started - full review. Results will be posted here.

@NiveditJain
NiveditJain merged commit c1189ca into main Jul 17, 2026
11 checks passed
@hermes-exosphere

Copy link
Copy Markdown
Contributor

🔍 Hermes re-review in progress — Phase 0-2 complete, Phase 3 starting

  • HEAD: ced8e13 (5 commits total)
  • 19 files changed (1072+, 929-)
  • 1 unresolved review thread (hermes-exosphere: Array.isArray defense-in-depth on L145)
  • 6 resolved threads (2 hermes-exosphere + 4 coderabbitai)
  • 1 human comment (chhhee10 — verified npm date, resolved)
  • Now running full test suite...

@hermes-exosphere

Copy link
Copy Markdown
Contributor

🔍 Hermes re-review in progress — Phase 0-2 complete, Phase 3 starting. HEAD: ced8e13. 19 files changed (1072+, 929-). Running full test suite...

@hermes-exosphere hermes-exosphere left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated review: Approved. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants