Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions context/skills/audit-identify/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ shared_docs:
- https://posthog.com/docs/getting-started/identify-users.md
- https://posthog.com/docs/product-analytics/cutting-costs.md
- https://posthog.com/docs/libraries/js/config.md
- https://posthog.com/docs/references/posthog-node.md
- https://posthog.com/docs/product-analytics/best-practices.md
- https://posthog.com/docs/data/anonymous-vs-identified-events.md
variants:
- id: all
Expand Down
2 changes: 1 addition & 1 deletion context/skills/audit-identify/description.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This skill audits an existing PostHog integration's **$identify implementation**

The audit covers three lenses:

- **Fix** — correctness checks on `posthog.identify()` placement, distinct_id stability, cross-runtime identity, and `reset()` on logout. These are the existing data-integrity checks already in the broader PostHog audit, scoped to identification.
- **Fix** — correctness checks on `posthog.identify()` placement, distinct_id stability, cross-runtime person and session correlation, and `reset()` on logout. These are the existing data-integrity checks already in the broader PostHog audit, scoped to identification.
- **Lifecycle quality** — how `$set` / `$set_once`, `alias()`, and `groupIdentify()` are used over time. Code-only checks.
- **Optimize** — cost-side checks on `person_profiles` mode, identified-vs-anonymous traffic ratio, `_isIdentified()` guarding, and duplicate `$identify` / `$groupidentify` events. These use PostHog MCP to read the operator's tenant; they gracefully skip if MCP is unavailable.

Expand Down
45 changes: 36 additions & 9 deletions context/skills/audit-identify/references/2-identify-fix.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@ next_step: 3-identify-lifecycle.md

# Step 2 — Identify (fix)

This step resolves five correctness checks **in parallel**, one subagent per check. The first four ids are reused by the broader PostHog audit so a fix once made here is observable from either entry point. The fifth (`identify-sequential-calls`) is audit-identify-specific.
This step resolves six correctness checks **in parallel**, one subagent per check. The first five ids are reused by the broader PostHog audit so a fix once made here is observable from either entry point. The sixth (`identify-sequential-calls`) is audit-identify-specific.

- `identify-stable-distinct-id`
- `identify-not-late`
- `cross-runtime-distinct-id`
- `cross-runtime-session-id`
- `identify-reset-on-logout`
- `identify-sequential-calls`

## Skip case — no `posthog.identify` calls found
## Reduced case — no `posthog.identify` calls found

If Step 1's identify grep returned **zero** hits, resolve all five checks in a single `audit_resolve_checks` call with `status: "pass"` and `details: "skip: no posthog.identify call sites detected"`. Then continue to **`3-identify-optimize.md`**. Do not dispatch subagents.
If Step 1's identify grep returned **zero** hits, resolve these five identify-dependent checks in one `audit_resolve_checks` call with `status: "pass"` and `details: "skip: no posthog.identify call sites detected"`: `identify-stable-distinct-id`, `identify-not-late`, `cross-runtime-distinct-id`, `identify-reset-on-logout`, and `identify-sequential-calls`. Then dispatch **only Task D** (`cross-runtime-session-id`), because anonymous browser and server events still need the same session correlation. When Task D returns, continue to **`3-identify-lifecycle.md`**.

## Status

Expand All @@ -24,9 +25,9 @@ Emit before dispatching:
[STATUS] Auditing identify correctness
```

## Action — dispatch five subagents in one message
## Action — dispatch six subagents in one message

Make **five `Agent` tool calls in a single message** so they run concurrently. Wait for all five to return, then continue to `3-identify-optimize.md`. Do not run any other tools between dispatch and the next step.
Make **six `Agent` tool calls in a single message** so they run concurrently. Wait for all six to return, then continue to `3-identify-lifecycle.md`. Do not run any other tools between dispatch and the next step.

The bundled `identify-users.md` reference holds PostHog's authoritative guidance on `distinct_id`, `identify()` ordering, and cross-runtime identity. It's typically at `.claude/skills/audit-identify/references/identify-users.md`; if that path doesn't exist, discover it with `Glob` `**/skills/audit-identify/references/identify-users.md`. Each subagent reads it once before judging.

Expand Down Expand Up @@ -96,7 +97,33 @@ Rule:
Emit one `mcp__wizard-tools__audit_resolve_checks` call with a single update for id `cross-runtime-distinct-id`, including `file` (path:line of the most relevant init or capture site) and `details` (one-line explanation). Return when the call completes. Do not write the audit report.
```

### Task D — `identify-reset-on-logout`
### Task D — `cross-runtime-session-id`

`description`: `Audit cross-runtime-session-id`

`prompt`:
```
You are an audit subagent. Resolve exactly one rule and return: cross-runtime-session-id.

Read this skill's bundled `best-practices.md` reference once (typically `.claude/skills/audit-identify/references/best-practices.md`; otherwise discover it with `Glob` `**/skills/audit-identify/references/best-practices.md`). Focus on the browser-and-server runtime guidance. Do not treat tracing headers as authentication.

Run **two** Greps in parallel:
- `posthog\.init\(|new PostHog\(|posthog\.Posthog\(|Posthog\(` — locate PostHog initialization across runtimes.
- `tracing_headers|setupExpressRequestContext|PosthogContextMiddleware|X-POSTHOG-SESSION-ID|x-posthog-session-id|withContext\(` — locate the client-to-server correlation wiring.

Read each file that contains a hit, once. Determine whether the project initializes both a browser SDK and a server SDK. If it does, verify both halves of the hand-off: the browser sends the PostHog tracing headers to the actual backend hostname, and the server binds the incoming session id to request-scoped PostHog context.

Rule:
- pass: `posthog-js` configures `tracing_headers` for the backend hostname and the server consumes the incoming session id through supported middleware (for example `setupExpressRequestContext` or `PosthogContextMiddleware`) or passes it as `sessionId` to request-scoped context.
- error: both browser and server runtimes initialize PostHog, but either the browser does not send tracing headers or the server does not bind `X-POSTHOG-SESSION-ID` to PostHog context.
- warning: both halves appear present, but the configured hostname or request-context flow cannot be matched to the backend from source.
- Skip (`pass` with details: "single runtime"): only a browser or only a server runtime initializes PostHog.
- Never accept a hand-written constant session id. The value must come from the browser SDK's current session through the request header.

Emit one `mcp__wizard-tools__audit_resolve_checks` call with a single update for id `cross-runtime-session-id`, including `file` (path:line of the missing or most relevant wiring) and `details` (one-line explanation of both the client and server evidence). Return when the call completes. Do not write the audit report.
```

### Task E — `identify-reset-on-logout`

`description`: `Audit identify-reset-on-logout`

Expand All @@ -118,7 +145,7 @@ Rule:
Emit one `mcp__wizard-tools__audit_resolve_checks` call with a single update for id `identify-reset-on-logout`, including `file` (path:line of the most relevant logout or reset site) and `details` (one-line explanation). Return when the call completes. Do not write the audit report.
```

### Task E — `identify-sequential-calls`
### Task F — `identify-sequential-calls`

`description`: `Audit identify-sequential-calls`

Expand Down Expand Up @@ -156,6 +183,6 @@ Emit one `mcp__wizard-tools__audit_resolve_checks` call with a single update for
Return when the call completes. Do not write the audit report.
````

## After all five return
## After all six return

Continue to **`3-identify-lifecycle.md`**. Do not write the report yet — that's Step 4's job after Step 3 finishes.
Continue to **`3-identify-lifecycle.md`**. Do not write the report yet — Step 6 writes it after the remaining checks finish.
35 changes: 31 additions & 4 deletions context/skills/audit/references/3-identification.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ next_step: 4-event-capture.md

# Step 3 — Identification

This step resolves four identification checks **in parallel**, one subagent per check:
This step resolves five identification checks **in parallel**, one subagent per check:

- `identify-stable-distinct-id`
- `identify-not-late`
- `cross-runtime-distinct-id`
- `cross-runtime-session-id`
- `identify-reset-on-logout`

Each subagent owns its own grep, reads, evaluates its single rule, and emits one `audit_resolve_checks` call with one update. The ledger's mutex serializes concurrent writes — there's no race.
Expand All @@ -21,9 +22,9 @@ Emit before dispatching:
[STATUS] Auditing identification
```

## Action — dispatch four subagents in one message
## Action — dispatch five subagents in one message

Make **four `Agent` tool calls in a single message** so they run concurrently. Wait for all four to return, then continue to `4-event-capture.md`. Do not run any other tools between dispatch and the next step.
Make **five `Agent` tool calls in a single message** so they run concurrently. Wait for all five to return, then continue to `4-event-capture.md`. Do not run any other tools between dispatch and the next step.

The bundled `identify-users.md` reference holds PostHog's authoritative guidance on `distinct_id`, `identify()` ordering, and cross-runtime identity. It's typically at `.claude/skills/audit/references/identify-users.md`; if that path doesn't exist, discover it with `Glob` `**/skills/audit/references/identify-users.md`. Each subagent reads it once before judging.

Expand Down Expand Up @@ -93,7 +94,33 @@ Rule:
Emit one `mcp__wizard-tools__audit_resolve_checks` call with a single update for id `cross-runtime-distinct-id`, including `file` (path:line of the most relevant init or capture site) and `details` (one-line explanation). Return when the call completes. Do not write the audit report.
```

### Task D — `identify-reset-on-logout`
### Task D — `cross-runtime-session-id`

`description`: `Audit cross-runtime-session-id`

`prompt`:
```
You are an audit subagent. Resolve exactly one rule and return: cross-runtime-session-id.

Read this skill's bundled `best-practices.md` reference once (typically `.claude/skills/audit/references/best-practices.md`; otherwise discover it with `Glob` `**/skills/audit/references/best-practices.md`). Focus on the browser-and-server runtime guidance. Do not treat tracing headers as authentication.

Run **two** Greps in parallel:
- `posthog\.init\(|new PostHog\(|posthog\.Posthog\(|Posthog\(` — locate PostHog initialization across runtimes.
- `tracing_headers|setupExpressRequestContext|PosthogContextMiddleware|X-POSTHOG-SESSION-ID|x-posthog-session-id|withContext\(` — locate the client-to-server correlation wiring.

Read each file that contains a hit, once. Determine whether the project initializes both a browser SDK and a server SDK. If it does, verify both halves of the hand-off: the browser sends the PostHog tracing headers to the actual backend hostname, and the server binds the incoming session id to request-scoped PostHog context.

Rule:
- pass: `posthog-js` configures `tracing_headers` for the backend hostname and the server consumes the incoming session id through supported middleware (for example `setupExpressRequestContext` or `PosthogContextMiddleware`) or passes it as `sessionId` to request-scoped context.
- error: both browser and server runtimes initialize PostHog, but either the browser does not send tracing headers or the server does not bind `X-POSTHOG-SESSION-ID` to PostHog context.
- warning: both halves appear present, but the configured hostname or request-context flow cannot be matched to the backend from source.
- Skip (`pass` with details: "single runtime"): only a browser or only a server runtime initializes PostHog.
- Never accept a hand-written constant session id. The value must come from the browser SDK's current session through the request header.

Emit one `mcp__wizard-tools__audit_resolve_checks` call with a single update for id `cross-runtime-session-id`, including `file` (path:line of the missing or most relevant wiring) and `details` (one-line explanation of both the client and server evidence). Return when the call completes. Do not write the audit report.
```

### Task E — `identify-reset-on-logout`

`description`: `Audit identify-reset-on-logout`

Expand Down
17 changes: 11 additions & 6 deletions context/skills/integration-v2/identify/description.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,17 @@ capture buys nothing.
Find the auth flow first: login and signup handlers, session callbacks. If the
app has no concept of a user, there is nothing to identify — report that and stop.

If the app has both a client and a server, keep them on the same person. Set the
client SDK's `tracing_headers` to the backend's hostname (hostnames only) and it
adds the `X-POSTHOG-DISTINCT-ID` and `X-POSTHOG-SESSION-ID` request headers on its
own — do not hand-roll a fetch wrapper for it. The server reads those headers, but
they are client-controlled: prefer the authenticated user id for anything
security-sensitive.
If the app has both a client and a server, keep them in the same person and
session. Set the client SDK's `tracing_headers` to each backend hostname
(hostnames only). It adds `X-POSTHOG-DISTINCT-ID` and `X-POSTHOG-SESSION-ID` to
matching requests on its own, so do not hand-roll a fetch wrapper. Then bind both
incoming values to request-scoped PostHog context on the server. Use the SDK's
framework middleware where one exists (for example `setupExpressRequestContext`
or `PosthogContextMiddleware`); otherwise read the headers and pass the session as
`sessionId` to the server SDK's context API. Verify that the configured hostname
is the one the browser actually calls. These headers are client-controlled
analytics context: prefer the authenticated user id for security-sensitive
identity and authorization decisions.

## Reference

Expand Down
Loading