Skip to content

feat(cli): track config apply/plan/export in PostHog#131

Merged
tonychang04 merged 3 commits into
mainfrom
worktree-posthog-config-tracking
May 15, 2026
Merged

feat(cli): track config apply/plan/export in PostHog#131
tonychang04 merged 3 commits into
mainfrom
worktree-posthog-config-tracking

Conversation

@tonychang04
Copy link
Copy Markdown
Member

@tonychang04 tonychang04 commented May 15, 2026

Summary

  • Emits cli_config_invoked from insforge config apply / plan / export (subcommand, outcome, flag shape, change counts, sections_changed from the TOML schema). Same analytics path as diagnose and payments.
  • Documents the property allowlist in README.md; adds the convention to DEVELOPMENT.md. Companion OSS docs page in InsForge/InsForge#1279.

What is captured

  • Identity: event name, subcommand, outcome (success / applied / aborted / dry_run / no_changes / error / all_skipped).
  • Run shape: dry_run, json_mode, auto_approved, force, changes_count, applied_count, skipped_count, sections_changed (TOML schema keys only, e.g. auth.smtp).
  • Project metadata when linked: project_id, project_name, org_id, region, oss_mode.

What is never captured

SQL, TOML contents, credentials, secrets, env() resolved values, file paths, free text. Full allowlist lives in src/lib/analytics.ts.

Test plan

  • npm run build clean
  • npm run lint 0 errors (39 pre-existing warnings)
  • npm run test 373 pass / 13 skipped / 0 fail
  • End-to-end smoke test: fired representative events against the real ingest endpoint; all properties intact in PostHog within seconds

Tracked in Linear: INS-217.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Documentation
    • Clarified Analytics section: only non-sensitive metadata is captured (commands/outcomes, flag shapes, config schema section names, region, OSS-vs-cloud); SQL, config contents, credentials, env var values, and free text are never sent. Removed note about analytics being enabled by default in the published package.
  • Improvements
    • Improved analytics attribution and reporting for config-related CLI commands and ensured analytics are reliably terminated on completion or error.

Review Change Stack

Note

Track config apply, plan, and export commands in PostHog analytics

  • Adds trackConfig to src/lib/analytics.ts that emits a cli_config_invoked event with subcommand name, project context, and run metadata (flags, outcome, change counts, affected sections).
  • Instruments config apply, config plan, and config export with analytics calls on all code paths: success, dry-run, aborted confirmation, and error.
  • Hardens shutdownAnalytics to no-op when no client exists and to null the client before shutdown to prevent double-flush.
  • Updates the README analytics section to enumerate the non-sensitive metadata captured and clarify that building without POSTHOG_API_KEY makes analytics a no-op.

Macroscope summarized 606aaad.

Wire the local-file config workflow into the PostHog analytics path
that already covers `diagnose` and `payments`. Emits `cli_config_invoked`
from `apply`, `plan`, and `export` with subcommand, outcome, flag shape,
change counts, and the TOML schema sections that were touched. No SQL,
TOML contents, credentials, or env() values cross the wire.

Adds a hard opt-out: `INSFORGE_TELEMETRY=0` (also accepts `false`/`no`,
case-insensitive). When set, `getClient()` short-circuits before
constructing the PostHog client. Covered by `analytics.test.ts`.

Docs: README "Analytics" section rewritten with the property allowlist
and opt-out instructions; DEVELOPMENT.md notes the convention for new
commands. The companion OSS docs page lives in InsForge/insforge.

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

coderabbitai Bot commented May 15, 2026

Walkthrough

This PR adds a trackConfig helper and instruments config commands (apply, export, plan) to emit analytics events for distinct outcomes, ensures safe analytics shutdown, and updates README to clarify telemetry capture scope as non-sensitive metadata only.

Changes

Config Command Analytics Instrumentation

Layer / File(s) Summary
Analytics helper and safe shutdown
src/lib/analytics.ts
Exports trackConfig() that composes cli_config_invoked events using ProjectConfig when available (derives distinctId, sets oss_mode) and updates shutdownAnalytics() to avoid double-shutdown by nulling the client before awaiting shutdown.
Config apply command analytics instrumentation
src/commands/config/apply.ts
Imports trackConfig, shutdownAnalytics, getProjectConfig; initializes projectConfig, computes sectionsChanged, and emits trackConfig('apply', ...) for dry-run/no-changes, aborted confirmation, successful apply (applied/skipped/auto_approve), and error paths. Awaits shutdownAnalytics() in finally.
Config export command analytics instrumentation
src/commands/config/export.ts
Imports trackConfig, shutdownAnalytics, getProjectConfig; acquires projectConfig early and emits trackConfig('export', ...) on overwrite-abort, success (with skipped_count), and error. Calls shutdownAnalytics() before error handling and in finally.
Config plan command analytics instrumentation
src/commands/config/plan.ts
Imports trackConfig, shutdownAnalytics, getProjectConfig; initializes projectConfig and emits trackConfig('plan', ...) on success (json mode, change/skip counts, changed sections) and on error. Ensures shutdownAnalytics() is called in catch and finally.
Analytics scope documentation
README.md
Rewrites Analytics section to explicitly define captured telemetry metadata (command/subcommand, outcome, flag shapes, schema section names, region, OSS-vs-cloud flag) and lists data never sent (SQL, config contents, credentials, environment variable values, free text). Removes prior "enabled by default" wording.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • jwfing

Poem

🐰 Hop along the config trail,
I track each plan, apply, and export tale.
Events tidy, properties light,
Shutdown neat before goodnight.
Telemetry safe — hop, hop, delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(cli): track config apply/plan/export in PostHog' directly and clearly describes the main change: instrumenting the config apply/plan/export commands to emit analytics events in PostHog.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree-posthog-config-tracking

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 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 `@README.md`:
- Line 677: Update the README's description of the telemetry "outcome" field to
enumerate all emitted values used by config events (not just "success / error /
aborted"); include "applied", "dry_run", "no_changes", and "all_skipped"
alongside the existing values so the documented list for the outcome field
matches the actual emitted values and avoids telemetry contract confusion.

In `@src/commands/config/apply.ts`:
- Around line 33-35: Move the getProjectConfig() call into the try block so its
exceptions are routed through the same error/finally handling: declare a
variable (e.g., let projectConfig) before the try, then inside the try assign
projectConfig = getProjectConfig() (before or after await requireAuth() as
needed), keeping the rest of the logic intact; this ensures any throw from
getProjectConfig() is caught by handleError(...) and the finally block.
Reference: getProjectConfig(), requireAuth(), handleError, finally.

In `@src/commands/config/export.ts`:
- Around line 24-26: Move the call to getProjectConfig() into the existing try
block so any exception it throws is handled by handleError(...) and the finally
block; specifically, inside the function that declares projectConfig and calls
requireAuth(), remove the top-level getProjectConfig() invocation and instead
call and assign projectConfig within the try (before or after await
requireAuth()), ensuring the same projectConfig variable name is used so the
rest of the export command (and the finally cleanup) still operates correctly.

In `@src/commands/config/plan.ts`:
- Around line 25-27: Move the call to getProjectConfig() into the try block so
exceptions go through the centralized error rendering and finally cleanup:
declare a variable (e.g., let projectConfig;) before the try, then inside the
try assign projectConfig = getProjectConfig() immediately before await
requireAuth(); keep references to projectConfig elsewhere unchanged so
subsequent code uses the initialized variable.
🪄 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: ac64204b-c552-48e7-8002-55545253394c

📥 Commits

Reviewing files that changed from the base of the PR and between 5cc4c0e and 426b166.

📒 Files selected for processing (7)
  • DEVELOPMENT.md
  • README.md
  • src/commands/config/apply.ts
  • src/commands/config/export.ts
  • src/commands/config/plan.ts
  • src/lib/analytics.test.ts
  • src/lib/analytics.ts

Comment thread README.md Outdated
Comment thread src/commands/config/apply.ts Outdated
Comment thread src/commands/config/export.ts Outdated
Comment thread src/commands/config/plan.ts Outdated
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

4 issues found across 7 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="src/commands/config/plan.ts">

<violation number="1" location="src/commands/config/plan.ts:25">
P2: `getProjectConfig()` is called outside the `try` block, so malformed local project config JSON can throw before command error handling runs.</violation>
</file>

<file name="src/commands/config/export.ts">

<violation number="1" location="src/commands/config/export.ts:24">
P2: `getProjectConfig()` is called outside the command's `try/catch`, so a malformed local `.insforge/project.json` can throw an uncaught error before normal CLI error handling runs.</violation>
</file>

<file name="src/commands/config/apply.ts">

<violation number="1" location="src/commands/config/apply.ts:33">
P2: `getProjectConfig()` is called outside the `try`, so malformed local config can crash with an uncaught exception before `handleError` runs.</violation>

<violation number="2" location="src/commands/config/apply.ts:159">
P2: `shutdownAnalytics()` in this `finally` block will not run on error paths because `handleError()` exits the process first.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
Re-trigger cubic

Comment thread src/commands/config/plan.ts Outdated
Comment thread src/commands/config/export.ts Outdated
Comment thread src/commands/config/apply.ts
Comment thread src/commands/config/apply.ts Outdated
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 15, 2026

Greptile Summary

This PR wires PostHog telemetry into the three config subcommands (apply, plan, export), emitting a cli_config_invoked event at every exit point and flushing the client before handleError terminates the process. The shutdownAnalytics helper is also improved to null its internal reference before calling shutdown(), so the catch-then-finally double-call is a guaranteed no-op.

  • trackConfig added in analytics.ts — accepts a nullable ProjectConfig so pure-OSS runs (no linked project) fall back to FAKE_PROJECT_ID as the distinct ID, matching the convention used elsewhere.
  • All three command files (apply, plan, export) call trackConfig at every exit branch (early return, aborted, success, error) and rely on the finally block's shutdownAnalytics() to flush on non-error paths, with an explicit pre-exit flush in the catch block before handleError can call process.exit().
  • README.md documents the property allowlist and removes the outdated "enabled by default in npm" sentence."

Confidence Score: 5/5

Additive analytics instrumentation with no changes to command logic; safe to merge.

The changes are purely additive: trackConfig calls at every exit branch, a finally-block flush, and an explicit pre-exit flush before handleError. The shutdownAnalytics null-guard correctly turns the catch+finally double-call into a no-op. No command logic, auth flows, or data paths are modified.

No files require special attention; the one minor outcome-naming inconsistency in apply.ts is captured in the inline suggestion.

Important Files Changed

Filename Overview
src/lib/analytics.ts Added trackConfig() for the three config subcommands; improved shutdownAnalytics() to null the client reference before calling shutdown(), preventing double-flush on catch+finally paths.
src/commands/config/apply.ts Added trackConfig at all exit points (no-changes/dry-run, aborted, applied/all-skipped, error); getProjectConfig() moved inside try; shutdownAnalytics called in catch before handleError to flush before process.exit(), with a no-op repeat in finally. Minor: outcome conflates dry_run + no_changes when both conditions are true.
src/commands/config/export.ts Added trackConfig at aborted, success, and error exit points; added the previously missing reportCliUsage call on the aborted path; consistent catch+finally shutdown pattern.
src/commands/config/plan.ts Added trackConfig on success and error paths with sections_changed, changes_count, and skipped_count; correct catch+finally shutdown pattern.
README.md Updated analytics section to document the property allowlist and removed the outdated "enabled by default in npm" statement.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI Action (apply/plan/export)
    participant PH as trackConfig()
    participant PHC as PostHog Client (queue)
    participant SA as shutdownAnalytics()

    CLI->>CLI: getProjectConfig() [inside try]
    alt Success path
        CLI->>PH: "trackConfig(subcommand, config, {outcome})"
        PH->>PHC: captureEvent() enqueue
        CLI->>SA: (finally) shutdownAnalytics()
        SA->>PHC: client.shutdown() flush
    else Early return (dry-run / aborted / no-changes)
        CLI->>PH: "trackConfig(subcommand, config, {outcome})"
        PH->>PHC: captureEvent() enqueue
        CLI->>CLI: return
        CLI->>SA: (finally) shutdownAnalytics()
        SA->>PHC: client.shutdown() flush
    else Error path
        CLI->>PH: trackConfig(subcommand, config, outcome error)
        PH->>PHC: captureEvent() enqueue
        CLI->>SA: (catch) shutdownAnalytics() flush + null client
        CLI->>CLI: handleError() process.exit()
        CLI->>SA: (finally) shutdownAnalytics() no-op client is null
    end
Loading

Reviews (3): Last reviewed commit: "review fixes: hoist getProjectConfig + f..." | Re-trigger Greptile

Comment thread src/commands/config/apply.ts Outdated
Comment thread src/commands/config/apply.ts
The published npm package already ships with the PostHog key baked in
at build time, so a runtime env var doesn't actually change the privacy
posture for end users in any meaningful way. The build-from-source
no-op (missing `POSTHOG_API_KEY`) remains the real opt-out for forks
and local dev.

Removes: telemetryDisabled() gate in analytics.ts, the analytics.test.ts
suite that only covered that gate, the README "how to disable" copy and
env var row, the DEVELOPMENT.md bullet.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@tonychang04 tonychang04 changed the title feat(cli): track config apply/plan/export in PostHog + add INSFORGE_TELEMETRY opt-out feat(cli): track config apply/plan/export in PostHog May 15, 2026
Comment thread src/commands/config/export.ts
Addresses the review comments on #131.

- Move `getProjectConfig()` inside the `try` block in apply/plan/export
  so a malformed `.insforge/project.json` flows through `handleError()`
  instead of crashing with a raw `SyntaxError` from `JSON.parse`.
  (CodeRabbit, cubic, greptile — same finding across 6 comments.)
- Flush analytics before `process.exit()` on error paths: catch path
  now calls `await shutdownAnalytics()` immediately before
  `handleError()`. `handleError()` exits synchronously, so the queued
  `error` event would otherwise never make it to PostHog. The finally
  block stays as a safety net; `shutdownAnalytics()` is now idempotent
  (nulls the client after first call) so the double-call is harmless.
  (cubic.)
- Reorder each early-return branch to call `reportCliUsage` before
  `trackConfig` so a `reportCliUsage` throw doesn't fire a second
  `cli_config_invoked` (the normal-outcome event + the catch's
  `error` event). (greptile.)
- Add the missing `reportCliUsage('cli.config.export', true)` to the
  `export` aborted prompt path so it matches the symmetric `apply`
  aborted path. (greptile.)
- README: expand the documented `outcome` value list to match what
  the config events actually emit (`applied`, `dry_run`,
  `no_changes`, `all_skipped` alongside the existing values).
  (CodeRabbit.)

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

Addressed all the review comments in 606aaad. Summary:

getProjectConfig() outside try — CodeRabbit/cubic/greptile (6 comments on apply.ts, plan.ts, export.ts): hoisted the variable declaration above the try, moved the assignment inside. A malformed .insforge/project.json now flows through handleError() instead of crashing with a raw SyntaxError.

shutdownAnalytics() in finally won't flush on error paths — cubic (apply.ts:159): handleError() calls process.exit() synchronously, killing the event loop before the await in finally can resolve. Fixed by calling await shutdownAnalytics() inside catch immediately before handleError(). The finally block stays as a safety net for the success path; shutdownAnalytics() is now idempotent (nulls the client after first call) so the double-invocation is harmless.

Double cli_config_invoked event if reportCliUsage throws — greptile (apply.ts:72): reordered each early-return branch to call reportCliUsage before trackConfig, so a reportCliUsage throw can no longer produce one event for the normal outcome and a second for error from the catch.

Asymmetric reportCliUsage in export's aborted path — greptile (export.ts:51): added await reportCliUsage('cli.config.export', true) to match apply.ts.

README outcome enumeration — CodeRabbit (README.md:677): expanded to success, applied, aborted, dry_run, no_changes, all_skipped, error.

Build clean, 0 lint errors, 373/373 unit tests pass.

@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 15, 2026

Want your agent to iterate on Greptile's feedback? Try greploops.

Copy link
Copy Markdown
Member

@jwfing jwfing left a comment

Choose a reason for hiding this comment

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

LGTM, approved.

@tonychang04 tonychang04 merged commit 11adfea into main May 15, 2026
4 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.

2 participants