diff --git a/skills/omnibus/auditing-experiments-flags/SKILL.md b/skills/omnibus/auditing-experiments-flags/SKILL.md index 61e41b50..db7d12fb 100644 --- a/skills/omnibus/auditing-experiments-flags/SKILL.md +++ b/skills/omnibus/auditing-experiments-flags/SKILL.md @@ -35,7 +35,7 @@ When the user asks for a comprehensive audit of both experiments and flags: 1. Fetch all experiments via `experiment-list` and all flags via `feature-flag-get-all`. 2. Run all experiment checks and all flag checks. 3. Apply [recurring patterns](./references/synthesis-patterns.md) to identify patterns across multiple findings. -4. If there are more than 5 entities with findings, output as a notebook artifact via `notebooks-create` for easier navigation. Otherwise report inline. +4. If there are more than 5 entities with findings, write them to a notebook for easier navigation. Otherwise report inline. Create the notebook from the project's own notebook tools. Run `search notebooks?-` to load them and read the titles. ## Output format diff --git a/skills/omnibus/building-workflows/references/graph-schema.md b/skills/omnibus/building-workflows/references/graph-schema.md index 862caf1e..96aaaec9 100644 --- a/skills/omnibus/building-workflows/references/graph-schema.md +++ b/skills/omnibus/building-workflows/references/graph-schema.md @@ -73,7 +73,7 @@ Discriminated on `config.type`: - `tracking_pixel` — a bodyless `GET`, so read query params: `"inputs": { "event": { "value": "{request.query.ph_event}" }, "distinct_id": { "value": "{request.query.ph_distinct_id}" } }`. Body references resolve empty here and the pixel still returns its 200 GIF, so every hit is dropped with no error to retry on. - `batch` — `{ "type": "batch", "filters": { "properties": [] } }`. The audience: person-property conditions and/or cohort references. **No event/action filters** (silently dropped, so rejected). Does not fire on enable — dispatch a one-off broadcast with `workflows-run-batch`, or make it **recurring** with `workflows-schedule-create` (attaches an RRULE schedule; each firing re-broadcasts to this same `config.filters.properties` audience). A recurring broadcast is a `batch` trigger plus a schedule. Editing `config.filters` pauses any schedule attached to it, since the confirmed recipient count no longer holds - preview again and re-create the cadence. - `schedule` — `{ "type": "schedule" }`. One person-less run per occurrence, for jobs that act on their own (for example a "Create AI task" step). Attach the cadence with `workflows-schedule-create`; no audience preview is needed. Changing this trigger to `batch` pauses any schedule already attached, because that cadence was never sized against an audience - re-create it after the blast-radius preview. -- `internal-event` (flag-gated) — `{ "type": "internal-event", "filters": { "source": "internal-events", "events": [{ "id": "", "type": "events" }], "properties": [] } }`. Fires once for each matching event on the internal-events stream; `filters.events` must name at least one allowed event id. Runs have no associated person, so person-dependent steps are unavailable. Test-run with an event named in `filters.events` and no person; any other event name is skipped at the trigger. The Slack trigger is this with `$slack_message_received` in `filters.events`: the run's event carries properties `integration_id`, `channel`, `channel_type`, `slack_team_id`, `user`, `bot_id`, `app_id`, `subtype`, `text`, `ts`, `thread_ts`, `is_thread_reply`, `is_ext_shared_channel`, and `slack_event` (the raw Slack payload), and a workflow leaving draft must include an exact `channel` property filter. The GitHub trigger uses `$github_event_received` and requires exact `repository` and `event_type` filters. +- `internal-event` (flag-gated) — `{ "type": "internal-event", "filters": { "source": "internal-events", "events": [{ "id": "", "type": "events" }], "properties": [] } }`. Fires once for each matching event on the internal-events stream; `filters.events` must name at least one allowed event id. Runs have no associated person, so person-dependent steps are unavailable. Test-run with an event named in `filters.events` and no person; any other event name is skipped at the trigger. The Slack trigger is this with `$slack_message_received` in `filters.events`: the run's event carries properties `integration_id`, `channel`, `channel_type`, `slack_team_id`, `user`, `bot_id`, `app_id`, `subtype`, `text`, `ts`, `thread_ts`, `is_thread_reply`, `is_ext_shared_channel`, and `slack_event` (the raw Slack payload). A workflow leaving draft must include one exact `channel` property filter whose value contains one or more channel IDs; several IDs match any listed channel. The GitHub trigger uses `$github_event_received` and requires exact `repository` and `event_type` filters. ### Trigger masking (throttling an event trigger) diff --git a/skills/omnibus/choosing-trend-or-slope-view/SKILL.md b/skills/omnibus/choosing-trend-or-slope-view/SKILL.md index f4a5d069..2dfbc29a 100644 --- a/skills/omnibus/choosing-trend-or-slope-view/SKILL.md +++ b/skills/omnibus/choosing-trend-or-slope-view/SKILL.md @@ -64,14 +64,13 @@ are the points you want compared (the slope uses the first and last interval). ## Important limits - **Two surfaces, one computation.** Both the inline slope (Max's `query-trends` - result card) and the saved-insight slope (`ChartDisplayType.SlopeGraph`, behind the - `slope-graph-insight` feature flag) show the same thing: the first interval's value - vs the last interval's value, at the chosen group-by interval. The grouping defines - the slope — group by month to compare the first vs last month, by day for the first - vs last day. Use the inline view for a quick before→after on a result you're already - looking at; reach for the saved display to persist it on a dashboard where the flag - is enabled. A still-accumulating final period is shown as-is with a dashed connector, - the same affordance the line chart uses for an incomplete tail. + result card) and the saved-insight slope (`ChartDisplayType.SlopeGraph`) show the + same thing: the first interval's value vs the last interval's value, at the chosen + group-by interval. The grouping defines the slope - group by month to compare the + first vs last month, by day for the first vs last day. Use the inline view for a + quick before-and-after on a result you're already looking at; use the saved display + to persist it on a dashboard. A still-accumulating final period is shown as-is with + a dashed connector, the same affordance the line chart uses for an incomplete tail. - For period-over-period on a single series (this month vs last), a line with `compareFilter: { "compare": true }` overlays the two periods; a slope is the better fit when comparing the endpoints of **many** series at once. diff --git a/skills/omnibus/copying-flags-across-projects/SKILL.md b/skills/omnibus/copying-flags-across-projects/SKILL.md index 4980de5c..38f51e36 100644 --- a/skills/omnibus/copying-flags-across-projects/SKILL.md +++ b/skills/omnibus/copying-flags-across-projects/SKILL.md @@ -36,22 +36,28 @@ Targets must be in the same organization as the source. Call `posthog:projects-g For a multi-target copy, the tool accepts up to 50 target project ids in a single call. Successes and failures are reported per target, so a partial failure does not block the rest. -### 3. Preview the source flag +### 3. Preview the source flag and its dependencies -Call `posthog:feature-flag-get-definition` on the source flag and present a concise summary to the user before copying: +Call `posthog:feature-flag-get-definition` on the source flag. Then always call `posthog:feature-flags-copy-dependencies-check` with `feature_flag_key`, `from_project`, and `target_project_ids`, rather than trying to detect dependencies first — it copies nothing, and returns an empty result with no warnings when the flag has none, so running it on every copy is cheap and never skips a real dependency. + +A false `can_copy_dependencies` does not always mean the copy is blocked. It is also false when there is nothing to copy. Report a problem only when `warnings` is non-empty. + +Present a concise summary to the user before copying: - Flag key, name, and active state in the source - Filter groups (rollout %, property filters, variant splits) - Any cohort references in `filters.groups[].properties[]` — these will be remapped server-side, but the user should know whether the target project already has matching cohorts - Whether the flag has encrypted payloads (`has_encrypted_payloads`) or is remote configuration (`is_remote_configuration`) - Whether scheduled changes exist (the user can opt to copy them in step 4) +- The dependency check's `copied_dependency_keys` (dependencies a copy would create in a target), `reused_dependency_keys` (dependencies a target already satisfies), and any `warnings` ### 4. Confirm copy options -Default to the safest combination and ask the user to override only if they explicitly want different behavior: +One confirmation covers all three options. Default to the safest combination and ask the user to override only if they explicitly want different behavior: - **`disable_copied_flag: true`** — the copied flag lands disabled in the target. Recommended by default; turning a flag on in a new project should be a deliberate, observed action. - **`copy_schedule: false`** — scheduled changes do not come along. Recommended by default; schedules are usually project-specific. +- **`copy_dependencies: false`** — dependency flags do not come along. Set it to `true` only when the user approves copying the keys step 3's check reported. If the user says "promote it as-is" or "turn it on in prod", switch `disable_copied_flag` to `false`. If they say "include the rollout schedule" or "with the scheduled rollout", switch `copy_schedule` to `true`. @@ -64,6 +70,7 @@ Call `posthog:feature-flags-copy-flags-create` with: - `target_project_ids`: the resolved list of target project ids - `disable_copied_flag`: from step 4 (default `true`) - `copy_schedule`: from step 4 (default `false`) +- `copy_dependencies`: from step 4 (default `false`). Omitting it silently skips dependency copying, even when the user approved it. ### 6. Report per-target outcome @@ -104,7 +111,8 @@ If any targets failed, ask the user whether to retry the failed ones, skip them, ## Available tools -- `posthog:feature-flags-copy-flags-create` — performs the copy. Required fields: `feature_flag_key`, `from_project`, `target_project_ids`. Optional: `disable_copied_flag`, `copy_schedule`. +- `posthog:feature-flags-copy-flags-create` — performs the copy. Required fields: `feature_flag_key`, `from_project`, `target_project_ids`. Optional: `disable_copied_flag`, `copy_schedule`, `copy_dependencies`. +- `posthog:feature-flags-copy-dependencies-check` — previews what a copy would do to the flag's transitive flag dependencies. Copies nothing. Call it before every copy; it returns an empty result when the flag has no dependencies. - `posthog:feature-flag-get-all` — find a flag by key/name in a given project when the user only gave a friendly name. - `posthog:feature-flag-get-definition` — fetch the full source flag (filters, variants, cohort references, encryption flags) so you can preview before copying. - `posthog:projects-get` — list projects in the active organization, used to resolve and validate target project ids. diff --git a/skills/omnibus/querying-posthog-data/references/example-error-tracking.md b/skills/omnibus/querying-posthog-data/references/example-error-tracking.md index 9320d731..2dcccea6 100644 --- a/skills/omnibus/querying-posthog-data/references/example-error-tracking.md +++ b/skills/omnibus/querying-posthog-data/references/example-error-tracking.md @@ -25,14 +25,14 @@ FROM argMaxState(properties.$exception_functions.-1, timestamp) AS function_state, argMaxState(properties.$exception_sources.-1, timestamp) AS source_state, argMaxState(properties.$lib, timestamp) AS library_state, - least(19, intDiv(dateDiff('seconds', toDateTime(toDateTime('2026-09-05 09:00:00.000000')), timestamp), greatest(1, intDiv(dateDiff('seconds', toDateTime(toDateTime('2026-09-05 09:00:00.000000')), toDateTime(toDateTime('2026-09-06 09:19:14.282325'))), 20)))) AS bin_idx, + least(19, intDiv(dateDiff('seconds', toDateTime(toDateTime('2026-09-06 11:00:00.000000')), timestamp), greatest(1, intDiv(dateDiff('seconds', toDateTime(toDateTime('2026-09-06 11:00:00.000000')), toDateTime(toDateTime('2026-09-07 11:51:49.000722'))), 20)))) AS bin_idx, count() AS occ, uniqState(nullIf(e.$session_id, '')) AS sessions_state, uniqState(coalesce(nullIf(toString(e.event_person_id), '00000000-0000-0000-0000-000000000000'), e.distinct_id)) AS users_state FROM events AS e WHERE - and(equals(e.event, '$exception'), isNotNull(e.properties.$exception_fingerprint), true, greaterOrEquals(e.timestamp, toDateTime(toDateTime('2026-09-05 09:00:00.000000'))), lessOrEquals(e.timestamp, toDateTime(toDateTime('2026-09-06 09:19:14.282325'))), or(greater(position(lower(e.properties.$exception_types), lower('constant')), 0), greater(position(lower(e.properties.$exception_values), lower('constant')), 0), greater(position(lower(e.properties.$exception_sources), lower('constant')), 0), greater(position(lower(e.properties.$exception_functions), lower('constant')), 0), greater(position(lower(e.properties.email), lower('constant')), 0), greater(position(lower(e.person.properties.email), lower('constant')), 0)), equals(properties.tag, 'max_ai')) + and(equals(e.event, '$exception'), isNotNull(e.properties.$exception_fingerprint), true, greaterOrEquals(e.timestamp, toDateTime(toDateTime('2026-09-06 11:00:00.000000'))), lessOrEquals(e.timestamp, toDateTime(toDateTime('2026-09-07 11:51:49.000722'))), or(greater(position(lower(e.properties.$exception_types), lower('constant')), 0), greater(position(lower(e.properties.$exception_values), lower('constant')), 0), greater(position(lower(e.properties.$exception_sources), lower('constant')), 0), greater(position(lower(e.properties.$exception_functions), lower('constant')), 0), greater(position(lower(e.properties.email), lower('constant')), 0), greater(position(lower(e.person.properties.email), lower('constant')), 0)), equals(properties.tag, 'max_ai')) GROUP BY fp_hash, bin_idx) AS ev diff --git a/skills/omnibus/querying-posthog-data/references/example-logs.md b/skills/omnibus/querying-posthog-data/references/example-logs.md index bbac59f1..9a41758a 100644 --- a/skills/omnibus/querying-posthog-data/references/example-logs.md +++ b/skills/omnibus/querying-posthog-data/references/example-logs.md @@ -31,7 +31,7 @@ SELECT FROM logs WHERE - and(and(greaterOrEquals(toStartOfDay(time_bucket), toStartOfDay(assumeNotNull(toDateTime('2025-12-09 00:00:00')))), lessOrEquals(toStartOfDay(time_bucket), toStartOfDay(assumeNotNull(toDateTime('2025-12-10 00:00:00'))))), 1, greaterOrEquals(timestamp, toDateTime('2026-09-05 09:19:15.045917')), indexHint(like(lower(body), '%timeout%')), ilike(toString(body), '%timeout%'), in(severity_text, tuple('warn', 'error', 'fatal'))) + and(and(greaterOrEquals(toStartOfDay(time_bucket), toStartOfDay(assumeNotNull(toDateTime('2025-12-09 00:00:00')))), lessOrEquals(toStartOfDay(time_bucket), toStartOfDay(assumeNotNull(toDateTime('2025-12-10 00:00:00'))))), 1, greaterOrEquals(timestamp, toDateTime('2026-09-06 11:51:49.799122')), indexHint(like(lower(body), '%timeout%')), ilike(toString(body), '%timeout%'), in(severity_text, tuple('warn', 'error', 'fatal'))) ORDER BY timestamp DESC, uuid DESC diff --git a/skills/omnibus/querying-posthog-data/references/example-session-replay.md b/skills/omnibus/querying-posthog-data/references/example-session-replay.md index b806cd48..5fe7299a 100644 --- a/skills/omnibus/querying-posthog-data/references/example-session-replay.md +++ b/skills/omnibus/querying-posthog-data/references/example-session-replay.md @@ -19,18 +19,18 @@ SELECT sum(s.console_error_count) AS console_error_count, max(s.retention_period_days) AS retention_period_days, plus(dateTrunc('DAY', start_time), toIntervalDay(coalesce(retention_period_days, 30))) AS expiry_time, - date_diff('DAY', toDateTime('2026-09-06 09:19:15.327807'), expiry_time) AS recording_ttl, - greaterOrEquals(max(s._timestamp), toDateTime('2026-09-06 09:14:15.327300')) AS ongoing, + date_diff('DAY', toDateTime('2026-09-07 11:51:50.070056'), expiry_time) AS recording_ttl, + greaterOrEquals(max(s._timestamp), toDateTime('2026-09-07 11:46:50.069514')) AS ongoing, round(least(greatest(multiply(divide(plus(plus(plus(divide(sum(s.active_milliseconds), 1000), sum(s.click_count)), sum(s.keypress_count)), sum(s.console_error_count)), plus(plus(plus(plus(sum(s.mouse_activity_count), dateDiff('SECOND', start_time, end_time)), sum(s.console_error_count)), sum(s.console_log_count)), sum(s.console_warn_count))), 100), 0), 100), 2) AS activity_score, coalesce(max(s.surfacing_score), 0.36) AS surfacing_score FROM raw_session_replay_events AS s WHERE - and(greaterOrEquals(s.min_first_timestamp, toDateTime('2026-09-03 00:00:00.000000')), lessOrEquals(s.min_first_timestamp, toDateTime('2026-09-06 09:19:15.327479'))) + and(greaterOrEquals(s.min_first_timestamp, toDateTime('2026-09-04 00:00:00.000000')), lessOrEquals(s.min_first_timestamp, toDateTime('2026-09-07 11:51:50.069704'))) GROUP BY session_id HAVING - and(greaterOrEquals(expiry_time, toDateTime('2026-09-06 09:19:15.327704')), equals(max(s.is_deleted), 0), greater(active_seconds, 5.0)) + and(greaterOrEquals(expiry_time, toDateTime('2026-09-07 11:51:50.069937')), equals(max(s.is_deleted), 0), greater(active_seconds, 5.0)) ORDER BY start_time DESC, session_id DESC diff --git a/skills/omnibus/querying-posthog-data/references/example-sessions.md b/skills/omnibus/querying-posthog-data/references/example-sessions.md index 9605623c..2e1fecb5 100644 --- a/skills/omnibus/querying-posthog-data/references/example-sessions.md +++ b/skills/omnibus/querying-posthog-data/references/example-sessions.md @@ -13,7 +13,7 @@ SELECT FROM sessions WHERE - and(less($start_timestamp, toDateTime('2026-09-06 09:19:21.250701')), greater($start_timestamp, toDateTime('2026-09-05 09:19:16.251095'))) + and(less($start_timestamp, toDateTime('2026-09-07 11:51:55.948743')), greater($start_timestamp, toDateTime('2026-09-06 11:51:50.949169'))) ORDER BY $start_timestamp DESC LIMIT 50000 diff --git a/skills/omnibus/signals-scout-session-replay/SKILL.md b/skills/omnibus/signals-scout-session-replay/SKILL.md index ea0fe621..97671b5d 100644 --- a/skills/omnibus/signals-scout-session-replay/SKILL.md +++ b/skills/omnibus/signals-scout-session-replay/SKILL.md @@ -295,7 +295,7 @@ Direct calls (read-only): - `query-session-recordings-list` — resolve `$session_id`s to watchable recordings (pass `session_ids` + a matching `date_from`); order by `console_error_count` or `activity_score` when shortlisting. - `session-recording-get` — one recording's metadata for a finding's example links. - `session-recording-summaries-list` / `session-recording-summary-get` — stored AI summaries (list filters: `session_ids`, `has_exceptions`, `outcome`; get returns segment-level detail). A 404 just means no summary exists — never trigger generation. -- `heatmaps-list` / `heatmaps-events` — spatial corroboration for a cluster. Feature-gated: skip silently if absent. +- `heatmaps-list` / `heatmaps-events` — spatial corroboration for a cluster. - `vision-scanners-list` / `vision-scanners-observations-list` / `vision-observations-list` / `vision-quota-retrieve` — scanner config, observation health, and quota. Feature-gated and often absent even where replay vision is in use — lead with `$recording_observed` SQL; these are the optional mechanism-confirmation layer. - `advanced-activity-logs-list` (`scopes: ["Team"]` + `start_date`/`end_date`) — dating recording-config changes against capture cliffs. - `read-data-schema` — confirm `$rageclick` / `$dead_click` / replay SDK properties exist before aggregating. Inbox & reviewer routing (mechanics in `authoring-scouts` → `references/report-contract.md`): diff --git a/skills/omnibus/skills-store/SKILL.md b/skills/omnibus/skills-store/SKILL.md index d146c73e..466ce4f6 100644 --- a/skills/omnibus/skills-store/SKILL.md +++ b/skills/omnibus/skills-store/SKILL.md @@ -25,6 +25,7 @@ PostHog is the primary store for team-shared skills — always use the PostHog M | `posthog:skill-file-delete` | Remove one bundled file from a skill | | `posthog:skill-file-rename` | Rename one bundled file (move without rewriting content) | | `posthog:skill-duplicate` | Duplicate an existing skill under a new name | +| `posthog:skill-rename` | Rename a skill, keeping its versions, files, and owners | | `posthog:skill-archive` | Archive all versions of a skill by name (cannot be undone) | Skills use progressive disclosure: discover by description, fetch the body only when relevant, and pull individual files on demand. Do not fetch every file eagerly. @@ -232,7 +233,7 @@ description: >- Use when the user asks to list, run, or manage PostHog skills, or references /phs, "ph skills", or "posthog skills". user-invocable: true -allowed-tools: mcp__posthog__skill-list, mcp__posthog__skill-get, mcp__posthog__skill-create, mcp__posthog__skill-update, mcp__posthog__skill-file-get, mcp__posthog__skill-file-create, mcp__posthog__skill-file-delete, mcp__posthog__skill-file-rename, mcp__posthog__skill-duplicate +allowed-tools: mcp__posthog__skill-list, mcp__posthog__skill-get, mcp__posthog__skill-create, mcp__posthog__skill-update, mcp__posthog__skill-file-get, mcp__posthog__skill-file-create, mcp__posthog__skill-file-delete, mcp__posthog__skill-file-rename, mcp__posthog__skill-duplicate, mcp__posthog__skill-rename --- # PostHog Skills Store diff --git a/skills/omnibus/working-with-skills/SKILL.md b/skills/omnibus/working-with-skills/SKILL.md index 5140083b..541cb256 100644 --- a/skills/omnibus/working-with-skills/SKILL.md +++ b/skills/omnibus/working-with-skills/SKILL.md @@ -58,6 +58,9 @@ Editing an existing skill? │ └─ Rename .......................... skill-file-rename └─ Wholesale bundle reset (rare!) ....... update(files=[...]) # replaces ALL files +Renaming the skill itself? + └─► skill-rename (keeps versions, files, and owners) + Want a fork as the starting point? └─► skill-duplicate (then update the copy)