Skip to content

Commit 90f540f

Browse files
authored
Merge branch 'master' into codex/fix-replay-vision-slack-workspace
2 parents 363f530 + 40eeb71 commit 90f540f

2,171 files changed

Lines changed: 118990 additions & 24787 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.agents/skills/debugging-mcp-analytics/references/event-vocabulary.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,13 @@ Per-event additions: `$mcp_error_status` (upstream HTTP status), `$mcp_error_cod
9292
which keys the caller actually sent), and exec-mode calls carry `$mcp_exec_verb` (which dispatcher
9393
verb ran) and `$mcp_exec_target_tool` (the tool that `info`/`schema`/`call` named). Those four are
9494
stamped in `tool-executor.ts` but are **not registered in `posthog/taxonomy/taxonomy.py`**, so they
95-
have no descriptions in the property picker — they still query fine. `execute-sql` calls additionally emit a separate `$ai_generation` event
95+
have no descriptions in the property picker — they still query fine.
96+
A `learn` call also carries `exec_learn_kind` (`search`, `load`, `list` for `learn skills` and a bare `learn`, `describe`, `guide`),
97+
stamped before the availability check so a rejected skill command still records its form, plus
98+
the raw `exec_search_query` for `search` (and for a `load` that searches inside the skill with `-s`) and `exec_learn_target` (the qualified skill) for `load`.
99+
A successful call carries `mcp_result_empty: true` when the handler returned zero rows, and
100+
`mcp_discovery_hint` (`empty_state` or `related_capability`) when the response builder appended a
101+
hint footer (`services/mcp/src/lib/discovery-hints.ts`). `execute-sql` calls additionally emit a separate `$ai_generation` event
96102
carrying `$ai_trace_id`, `$ai_input`, `$ai_output_choices`, and `$ai_latency`.
97103

98104
## Exec-mode properties

‎.agents/skills/depot-ci/references/posthog-check-run-semantics.md‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,24 @@ In the following cancelled runs, an unstarted job received a `cancelled` check w
7373

7474
## Finding one event's Depot run
7575

76-
Check times and `pull_requests` did not pick out the Depot run in these cases, so the event goes into a check name. The Depot wait job's name ends with `(PR <number>, event <pull_request.updated_at>)`, and Depot renders expressions in job names into the check name. `.github/scripts/ci_backend_relay.py` builds the same name from the GitHub run's payload, reads that check, takes the Depot workflow id from its `details_url`, and reads the gate or migration check of that workflow only. Measured 2026-09-24 on PR 105886: Depot posted check `107656013423` named `… (PR 105886, event 2026-09-24T13:33:14Z)`, and GitHub relay job `107655803961` used `EVENT_AT: 2026-09-24T13:33:14Z` and relayed a successful gate.
76+
Check times and `pull_requests` did not pick out the Depot run in these cases, so the event goes into a check name. The Depot wait job's name ends with `(PR <number>, event <pull_request.updated_at>)`, and Depot renders expressions in job names into the check name. `.github/scripts/ci_backend_relay.py` builds the same name from the GitHub run's payload, reads that check, takes the Depot workflow id from its `details_url`, and reads the gate check of that workflow only. Measured 2026-09-24 on PR 105886: Depot posted check `107656013423` named `… (PR 105886, event 2026-09-24T13:33:14Z)`, and GitHub relay job `107655803961` used `EVENT_AT: 2026-09-24T13:33:14Z` and relayed a successful gate.
77+
78+
### Racing events
79+
80+
Two events of one commit that arrive within a second or two race the concurrency cancel on both engines, and neither engine reliably keeps the newer event. A stack push shows it: it force-pushes a pull request's head and its base about one second apart, and each push starts a pull request event. Measured 2026-09-25 on a stack push at 15:17Z:
81+
82+
- PR 106429: GitHub Actions cancelled the run of the newer event (15:17:40Z) and kept the older one (15:17:39Z). Depot kept 15:17:40Z and cancelled Depot run `rtl932mrbc` with "Cancelled due to the workflow concurrency policy".
83+
- PR 106435: GitHub Actions kept the newer event (15:17:41Z). Depot kept the older one (15:17:40Z) and cancelled `nzb661zql2`.
84+
- Neither cancelled Depot run posted a wait check, so each surviving GitHub relay found no run for its event and failed after the grace period, although Depot's run for the other event passed on the same commit.
85+
86+
GitHub Actions alone does the same. Among the 946 `ci-backend.yml` pull request runs created on 2026-09-25 between 11:30Z and 16:00Z, 56 same-commit pairs were created within 30 seconds of each other, 52 of them 0 to 2 seconds apart. GitHub kept the older run in 6 pairs, all 0 to 1 second apart. So after its grace period, the relay follows the Depot run of an event of the same pull request up to 2 seconds away (`RACING_EVENT_SECONDS`), newest first.
7787

7888
The newest check per name tells you the verdict. It does not tell you whether the PR can merge. A cancelled run on the same head keeps its checks, and when one of them is a required check, GitHub's merge box and Trunk keep the PR blocked until that run is rerun. `/merging-prs` has the recipe.
7989

90+
## pull_request_target checks
91+
92+
Depot can post a `pull_request_target` job's check on another pull request's head commit. A `pull_request_target` run's `github.sha` is the base branch head, so pull requests opened against the same master commit share it, and every misplaced check measured sat on a commit whose own run shared that base commit. Measured 2026-09-25 on a migration report workflow that ran on `pull_request_target`: 35 of its 42 checks sat on the wrong commit. For example, check `108123656729` on PR 106758's head `0295b92b41` links Depot workflow `ljv421dmlt`, whose run `vpg4b1kvh7` tested PR 106767's head `fb236b6c26`, and both runs had base `7af9307723`. The jobs' own writes, a check posted with an explicit `head_sha` and a comment on an explicit PR number, reached the right pull request. Post pull-request-facing results from a `pull_request` workflow, whose checks land on the right head.
93+
8094
## Depot CI CLI recipes
8195

8296
The CLI needs ids that the GitHub API does not carry. Start from a check run's `details_url`, which encodes the workflow id and job id.

‎.agents/skills/implementing-warehouse-sources/SKILL.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -643,9 +643,9 @@ Requirements and behavior:
643643

644644
- **The parent must be a selectable schema of the same source** — it has to produce its own Delta table.
645645
- **Soft dependency — the child falls back to the parent API.** Declare the parents by overriding `get_required_parent_schemas` on the source (wire it to `required_parents_from_endpoint_configs(ENDPOINTS, schema_name)`; add explicit entries for custom-iterator endpoints). That override is the only declaration: nothing surfaces the relationship through the API, so don't add a schema-payload field for it while the feature is unvalidated.
646-
Nothing in the API constrains the selection either: a child can be enabled without its parent, and a parent can be disabled or deleted while children sync. `_warehouse_parent_reuse_available` in `import_data_activity_sync` decides per run — a parent that is missing, disabled, not yet initially synced, or on any sync type other than merge or full refresh sends that run down the legacy parent-API path, so enabling the flag can never break a schema that syncs today. A parent that is merely mid-sync does not force the fallback, because `resolve_parent_table_ref` pins the read to the parent's last completed snapshot via Delta time travel.
646+
Nothing in the API constrains the selection either: a child can be enabled without its parent, and a parent can be disabled or deleted while children sync. `_warehouse_parent_reuse_available` in `import_data_activity_sync` decides per run — a parent that is missing, disabled, not yet initially synced, or on any sync type other than merge or full refresh sends that run down the legacy parent-API path, so opting a child in can never break a schema that syncs today. A parent that is merely mid-sync does not force the fallback, because `resolve_parent_table_ref` pins the read to the parent's last completed snapshot via Delta time travel.
647647
Never enable a parent as a side effect of enabling a child: parent syncs count toward the customer's billed rows.
648-
- **Feature-flagged.** The whole path is gated by the `warehouse-fanout-parent-reuse` flag (`is_fanout_warehouse_reuse_enabled`); with the flag off, opted-in endpoints silently keep the legacy parent-API path, so rollback is a flag flip.
648+
- **Small parents stay on the API.** A parent under `MIN_WAREHOUSE_PARENT_ROWS` (1,000 rows) is not worth opening: the Delta read has a fixed cost of a few seconds, more than paging a small listing, and that cohort measured slower when converted. The gate reads the parent table's `row_count`, so it applies per run without configuration. There is no feature flag any more; rollback is a revert.
649649
- **Strictly streaming — never materialize the parent table.** The reader scans one projected batch at a time with column projection pushed down to the parquet read. Do not add `to_table`, global sorts, or seen-set dedupe to it — parents can be arbitrarily large, and the whole pipeline exists to avoid full-dataset memory. If a caller's semantics depend on parent order (the API returned sorted rows), rework them into per-row filters over the unordered stream (see Sentry's `issue_tag_values` cutoff handling) instead of sorting.
650650
- **The usable sync types are an allow-list, not a deny-list.** Only merge and full refresh hold one row per key; append accumulates a row per sync and CDC keeps change history, so streaming either would fan the child out once per duplicate, and dedupe would need unbounded state. A new sync type has to opt in deliberately in `_parent_unusable_reason`.
651651
- **Values carry Delta physical types, not the API's JSON types.** A timestamp comes back as a datetime rather than an ISO string, a nested object as a dict. Because the API fallback engages per run, projecting such a field through `include_from_parent` makes the child's column type flip between runs and trips the merge's type-drift guards. Only project fields whose physical type matches what the API returned (an id string is safe), or normalize in the caller.

‎.agents/skills/run-posthog/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ The frontend uses kea-router. The mapping rule:
112112
| Edited path | Scene URL (under `/project/{team_id}/`) |
113113
| ---------------------------------------- | ------------------------------------------------------- |
114114
| `frontend/src/scenes/<name>/**` | usually `/<name>` (e.g. `insights/` → `/insights`) |
115-
| `frontend/src/scenes/activity/**` | `/activity/explore` (and other `ActivityTab`s) |
115+
| `frontend/src/scenes/activity/**` | `/activity/events` (and other `ActivityTab`s) |
116116
| `frontend/src/scenes/data-management/**` | `/data-management/<sub>` |
117117
| `frontend/src/scenes/settings/**` | `/settings/<section>` |
118118
| `frontend/src/scenes/authentication/**` | `/login`, `/signup`, `/preflight` (un-scoped) |

‎.agents/skills/stacking-prs/SKILL.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ gh stack sync --prune # also delete local branches for merged PRs
8282
- `gh stack view --short` shows status (`--json` for scripting); a `⚠` means that layer needs a rebase, which blocks merging. `gh stack checkout <stack-number|PR|URL>` pulls down and tracks a stack you don't have locally, including a teammate's.
8383
- `gh stack modify` interactively reorders, folds, drops, or renames layers. `gh stack unstack` removes the stack on GitHub (`--local` to only drop local tracking).
8484
- Batch work before syncing. Each sync force-pushes and re-runs a full CI matrix for every rebased layer, so sync when you need the rebase, not to track master.
85+
- A push that moves a layer and its base in one go sends that layer two `synchronize` events, one per moved ref (the same behavior [git-spice#966](https://github.com/abhinav/git-spice/issues/966) reports). Every workflow then starts twice, and a workflow with a concurrency group cancels the older run, so a `cancelled` row next to a passing one is this duplicate, not a test failure. To avoid it, push the layers one at a time, bottom first, about a minute apart: each layer then gets the extra run on its old head, and its own push supersedes that run cleanly.
86+
- A cancelled duplicate still blocks the merge, because GitHub keeps its checks next to the newer green ones. Rerun it with the "Cancelled runs on the head" recipe in `/merging-prs` instead of pushing again. When `Django Tests Pass` fails because Depot cancelled its run for the event, its log lists the retry steps: retry the Depot run, then `gh run rerun <run id> --failed` relays the new result.
8587
- Layer branches move without you: ReviewHog and other bots push fix commits straight onto PR branches. `gh stack sync` fetches first and pushes with `--force-with-lease`, so it refuses when a branch moved; treat that refusal as "someone committed here, go read it", not "retry". Before any manual `git push` or rebase of a layer, `git fetch origin` and fast-forward onto the remote head. Never plain force-push a layer branch.
8688
- The `ci:preflight` pre-push hook runs on these pushes like any other; never bypass it.
8789

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
name: styling-sidebar-products
3+
description: >
4+
How products and pages appear in the PostHog sidebar: which category they sit in, which icon and color they
5+
get, and which groups share a color gradient. Use when adding a product or data management page to the
6+
sidebar (treeItemsProducts or treeItemsMetadata in a products/*/manifest.tsx), giving an entry a new icon or
7+
color, moving an entry between sidebar categories, adding a category, or when a sidebar icon renders the
8+
wrong color, the wrong glyph, or differently once starred. Trigger terms: sidebar, nav, All products,
9+
Popular, category, iconType, iconColor, product icon, product color, gradient, starred icon.
10+
---
11+
12+
# Styling sidebar products
13+
14+
The sidebar reads every entry from the product manifests.
15+
An entry needs three things to look right: a category, an icon type that only it uses in the sidebar, and a color.
16+
Some categories share a gradient, so a new entry in one of them changes its neighbors' colors too.
17+
18+
## Where things live
19+
20+
| What | File |
21+
| ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- |
22+
| Entry definition (`path`, `category`, `iconType`, `iconColor`, `href`, `flag`) | `products/<product>/manifest.tsx`, under `treeItemsProducts` or `treeItemsMetadata` |
23+
| Icon glyph and default color per icon type | `iconTypes` in `frontend/src/layout/panel-layout/ProjectTree/defaultTree.tsx` |
24+
| The icon type union | `FileSystemIconType` in `frontend/src/queries/schema/schema-general.ts` |
25+
| Color values | `--color-product-<name>-light` and `-dark` in `frontend/src/styles/base.scss` |
26+
| Category names | `ProductItemCategory` in `schema-general.ts` |
27+
| Category order in the sidebar | `CATEGORY_ORDER` in `frontend/src/layout/panel-layout/navbar/tabs/productsCatalog.ts` |
28+
| Sidebar-only groupings (Popular, hidden tab pages, pinned rows) | `navProductsTabLogic.ts` and `productsCatalog.ts` in the same folder |
29+
| Custom icons that are components, like the Support badge | `ProjectTree/customIconRegistry.tsx` |
30+
31+
## Rules
32+
33+
**One icon per entry.** No two sidebar entries share a glyph.
34+
If the icon type you want is also used outside the sidebar, for example `event_definition` or `data_pipeline_metadata`, add a new icon type instead of changing the shared one.
35+
A new icon type goes into the `FileSystemIconType` union and the `iconTypes` map, and then `hogli build:schema` regenerates the Python enums.
36+
37+
**One color, set in both places.** Put the same color variable pair on the `iconTypes` entry and on the manifest entry's `iconColor`.
38+
The sidebar row uses the manifest color. Scene titles, search and other surfaces use the map color.
39+
When the two disagree, the product shows two colors across the app.
40+
Always give both a light and a dark variable, because a single value is reused for dark mode and reads poorly there.
41+
42+
**Starred entries follow the product.** A star stores only `iconType || type` and `href`.
43+
The file tree looks up the product by `href` (`getProductIcon` in `defaultTree.tsx`) and uses its icon and color, and it wraps custom icons in `ProductIconWrapper`.
44+
Do not add a per-surface icon override. Fix the manifest and the map, and the star follows.
45+
46+
**Pinned rows stay neutral.** Home, Self-driving and Activity and people have no color on purpose.
47+
48+
## Color families
49+
50+
Four categories use one gradient each. The steps follow the order the sidebar shows, which is alphabetical by label.
51+
52+
| Category | Gradient | Light end points |
53+
| -------------- | ----------------------------- | -------------------------------------- |
54+
| AI engineering | indigo to magenta | `rgb(99 102 241)` to `rgb(196 60 218)` |
55+
| CDP | golden yellow to burnt orange | `rgb(234 179 8)` to `rgb(194 65 12)` |
56+
| Schema | teal to deep blue | `rgb(20 184 166)` to `rgb(29 78 216)` |
57+
| Tools | red to magenta pink | `rgb(239 68 68)` to `rgb(217 40 160)` |
58+
59+
When you add, remove or rename an entry in one of these categories, regenerate the whole category, since every step moves:
60+
61+
```sh
62+
python3 .agents/skills/styling-sidebar-products/scripts/sidebar_gradient.py schema \
63+
actions annotations event-definitions mcp-servers sql-variables
64+
```
65+
66+
Pass the color variable names in sidebar order, and include entries behind a feature flag.
67+
Paste the output over the matching lines in `base.scss`.
68+
If an entry shared a variable with a product outside the group, give it its own variable first. `warehouse-destinations` is an example: it used to share with Data ops.
69+
The end points, including dark mode, live in the script. Change them there, not by hand in `base.scss`.
70+
71+
The other categories (Popular, Data, Monitoring, Product engineering, Messaging, Unreleased) use each product's own brand color.
72+
Pick a hue that differs from the entries right next to it in the same category.
73+
Avoid the gradient families above, so a product does not look like it belongs to AI engineering, CDP, Schema or Tools.
74+
75+
## Categories
76+
77+
Categories are values of `ProductItemCategory`, and the backend reads them through `products.json`.
78+
After renaming or adding one, update `CATEGORY_ORDER` in `productsCatalog.ts`, update the old nav's `CATEGORY_ORDER` in `ProjectTree/utils.tsx`, and update `test_get_products_by_category_has_expected_categories` in `posthog/test/test_products.py`.
79+
Then run `hogli build:schema` and `pnpm --filter=@posthog/frontend build:products`.
80+
81+
Popular is sidebar-only. It lives in `POPULAR_PRODUCT_PATHS` and does not change a product's real category.
82+
Pages that are tabs of another page, like the definitions tabs, stay in their manifest but are hidden in `navProductsTabLogic.ts`.
83+
84+
## Check it
85+
86+
- `hogli test frontend/src/layout/panel-layout` runs the sidebar logic tests. It also checks that every starred product renders the same icon and color as its row, in `ProjectTree/utils.test.ts`.
87+
- Render the `Layout/Products and files` story in light and dark (`Dark` story), and look at the whole category, not only the new entry. A gradient reads wrong when one step is off.
88+
- Storybook does not hot-reload `base.scss`. Restart it after changing a color, or it keeps rendering the old values.
89+
- Neighboring steps need visible contrast. When steps look alike, widen the end points in hue and lightness rather than adding more steps.

0 commit comments

Comments
 (0)