fix(actions): compile CSS selectors that match zero events - #80653
fix(actions): compile CSS selectors that match zero events#80653posthog[bot] wants to merge 8 commits into
Conversation
The selector compiler turned four common CSS selector shapes into a regex that no real element can match, so autocapture actions saved cleanly and then matched zero events with no error. - Read a class before an attribute as a class, not the tag name. - Read every [attr="value"] pair in one part, with a non-greedy quote-balanced value. - Insert a wildcard between descendant parts so a space combinator matches through intermediate elements. - Widen the segment character class to accept braces, quotes, and commas in values. - Surface a selector_warning at write time when a selector uses unsupported CSS that compiles to a matcher that matches nothing. Refs #15480 Generated-By: PostHog Code Task-Id: 0de188ef-82b7-4fdf-b50a-f34a53c3bf05
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
🤖 CI report
|
| File | Size | Δ vs base |
|---|---|---|
exporter/src/exporter/scenes/ExporterDashboardScene.js |
4.0 KiB | 🟢 -286.1 KiB (-98.6%) |
exporter/src/exporter/scenes/ExporterNotebookScene.js |
3.32 MiB | 🔺 +258.1 KiB (+8.2%) |
posthog-app/_parent/products/workflows/frontend/Workflows/WorkflowScene.js |
91.6 KiB | 🟢 -102.8 KiB (-52.9%) |
exporter/_parent/products/workflows/frontend/Workflows/Workflow.js |
95.1 KiB | 🔺 +95.1 KiB (new) |
posthog-app/_parent/products/error_tracking/frontend/scenes/ErrorTrackingIssueScene/ErrorTrackingIssueScene.js |
54.4 KiB | 🟢 -48.6 KiB (-47.2%) |
render-query/src/render-query/render-query.js |
21.75 MiB | 🔺 +28.5 KiB (+0.1%) |
posthog-app/_parent/products/actions/frontend/pages/Action.js |
9.3 KiB | 🟢 -22.2 KiB (-70.5%) |
posthog-app/src/scenes/authentication/signup/Signup.js |
19.8 KiB | 🔺 +19.8 KiB (new) |
posthog-app/_parent/products/signals/frontend/inbox/InboxScene.js |
720.1 KiB | 🔺 +17.2 KiB (+2.4%) |
posthog-app/src/scenes/authentication/invite-signup/InviteSignup.js |
16.3 KiB | 🔺 +14.6 KiB (+825.9%) |
posthog-app/_parent/products/customer_analytics/frontend/CustomerAnalyticsScene.js |
191.7 KiB | 🔺 +13.4 KiB (+7.5%) |
posthog-app/_parent/products/skills/frontend/CommunitySkillsScene.js |
10.5 KiB | 🔺 +10.5 KiB (new) |
posthog-app/src/scenes/authentication/login/Login.js |
9.4 KiB | 🔺 +7.5 KiB (+405.6%) |
posthog-app/_parent/products/ai_observability/frontend/AIObservabilityScene.js |
155.2 KiB | 🔺 +6.0 KiB (+4.0%) |
posthog-app/_parent/products/review_hog/frontend/CodeReviewScene.js |
68.7 KiB | 🔺 +5.6 KiB (+8.9%) |
posthog-app/src/scenes/experiments/Experiment.js |
253.9 KiB | 🔺 +5.3 KiB (+2.1%) |
posthog-app/_parent/products/replay_vision/frontend/replay_scanners/ReplayScanner.js |
123.7 KiB | 🔺 +4.7 KiB (+3.9%) |
posthog-app/src/scenes/authentication/verify-email/VerifyEmail.js |
6.4 KiB | 🔺 +4.6 KiB (+261.5%) |
posthog-app/_parent/products/workflows/frontend/Workflows/Workflow.js |
4.3 KiB | 🔺 +4.3 KiB (new) |
exporter/_parent/products/customer_analytics/frontend/components/AccountRelationshipsInput/CyclotronJobInputAccountRelationships.js |
3.6 KiB | 🔺 +3.6 KiB (new) |
posthog-app/_parent/products/logs/frontend/scenes/LogsAlertDetailScene/LogsAlertDetailScene.js |
29.6 KiB | 🔺 +3.4 KiB (+12.8%) |
exporter/_parent/products/customer_analytics/frontend/components/AccountPropertiesInput/CyclotronJobInputAccountProperties.js |
3.3 KiB | 🔺 +3.3 KiB (new) |
exporter/_parent/products/alerts/frontend/views/EditAlertModal.js |
114.6 KiB | 🟢 -3.1 KiB (-2.6%) |
posthog-app/_parent/products/logs/frontend/LogsScene.js |
40.2 KiB | 🔺 +2.7 KiB (+7.1%) |
exporter/_parent/products/conversations/frontend/components/SlaBusinessHours/CyclotronJobInputBusinessHours.js |
2.7 KiB | 🔺 +2.7 KiB (new) |
posthog-app/src/scenes/authentication/signup/SignupContainer.js |
removed | 🟢 -1.8 KiB (-100.0%) |
posthog-app/src/scenes/notebooks/NotebookPanel/NotebookPanel.js |
18.7 KiB | 🔺 +1.7 KiB (+9.7%) |
posthog-app/src/scenes/notebooks/NotebookScene.js |
24.6 KiB | 🔺 +1.7 KiB (+7.3%) |
posthog-app/src/scenes/notebooks/NotebookCanvasScene.js |
17.0 KiB | 🔺 +1.7 KiB (+10.8%) |
posthog-app/src/scenes/persons/PersonScene.js |
37.0 KiB | 🔺 +1.5 KiB (+4.2%) |
posthog-app/src/scenes/data-pipelines/batch-exports/BatchExportScene.js |
70.9 KiB | 🔺 +1.3 KiB (+1.9%) |
exporter/_parent/products/conversations/frontend/components/Assignee/CyclotronJobInputAssignee.js |
1.3 KiB | 🔺 +1.3 KiB (new) |
posthog-app/_parent/products/data_catalog/frontend/DataCatalogMetricScene.js |
20.7 KiB | 🔺 +1.2 KiB (+6.3%) |
exporter/_parent/products/conversations/frontend/components/TicketTags/CyclotronJobInputTicketTags.js |
1.1 KiB | 🔺 +1.1 KiB (new) |
Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report
✅ Eager graph — within budget
How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.
| Root | Eager (shipped) | Δ vs base | Budget |
|---|---|---|---|
entry (logged-out pages, app bootstrap)src/index.tsx |
1.27 MiB · 22 files | 🔺 +4.6 KiB (+0.4%) | ███░░░░░░░ 28.1% of 4.51 MiB |
authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
8.49 MiB · 3,136 files | 🔺 +125.7 KiB (+1.5%) | █████████░ 87.4% of 9.71 MiB |
🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
Largest files eagerly shipped from src/index.tsx
| Size | File |
|---|---|
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 24.6 KiB | ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js |
| 6.3 KiB | ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js |
| 4.5 KiB | ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js |
| 3.9 KiB | ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js |
| 1.4 KiB | ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js |
| 1.3 KiB | src/RootErrorBoundary.tsx |
| 912 B | ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js |
| 789 B | src/scenes/ChunkLoadErrorBoundary.tsx |
| 762 B | src/index.tsx |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 285.3 KiB | ../node_modules/.pnpm/posthog-js@1.410.1/node_modules/posthog-js/dist/rrweb.js |
| 267.7 KiB | ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js |
| 238.4 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 231.5 KiB | ../node_modules/.pnpm/posthog-js@1.410.1/node_modules/posthog-js/dist/module.js |
| 154.3 KiB | ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js |
| 126.8 KiB | ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js |
| 105.1 KiB | src/lib/api.ts |
| 94.6 KiB | ../packages/quill/packages/quill/dist/index.js |
| 93.3 KiB | ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js |
| 90.6 KiB | ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js |
Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479
✅ Toolbar bundle — eager 2.20 MiB within budget
What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.
| Metric | Size | Δ vs base | Budget |
|---|---|---|---|
| Eager (shipped) entry + static imports |
2.20 MiB · 17 files | 🔺 +4.6 KiB (+0.2%) | ████░░░░░░ 38.5% of 5.72 MiB |
| Deferred (lazy) | 2.08 MiB · 33 files | 🟢 -16 B (-0.0%) | n/a — loads on demand |
Loader dist/toolbar.js |
1.1 KiB | no change | █░░░░░░░░░ 5.8% of 19.5 KiB |
Largest eagerly-shipped chunks
| Size | File |
|---|---|
| 730.2 KiB | dist/toolbar/toolbar-app-VJGUZRB4.css |
| 553.2 KiB | dist/toolbar/chunk-chunk-SFO2HVBJ.js |
| 484.6 KiB | dist/toolbar/chunk-chunk-65GDEPQ6.js |
| 133.6 KiB | dist/toolbar/chunk-chunk-SRZJE47C.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-T5KY5WYR.js |
| 71.2 KiB | dist/toolbar/toolbar-app-LDPRNTFK.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-27JL52RE.js |
| 35.6 KiB | dist/toolbar/chunk-chunk-L4WRPCJH.js |
| 20.9 KiB | dist/toolbar/chunk-chunk-XLGVFZIK.js |
| 12.2 KiB | dist/toolbar/chunk-chunk-PIK3PADE.js |
Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile
✅ Dist folder size — 🔺 +11.27 MiB (+0.8%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 1432.88 MiB · 🔺 +11.27 MiB (+0.8%)
⚠️ Playwright — 1 failed, 1 flaky
🎭 Playwright report
❌ 1 failed test:
- View source toggle enters edit mode and shows query editor (chromium)
- Deleting an insight from dashboard redirects back (chromium)
These issues are not necessarily caused by your changes.
Annoyed by this section? Help fix flakies and failures and it will go green!
⚠️ Backend snapshots — 1 updated (1 modified, 0 added, 0 deleted)
Query snapshots: Backend query snapshots updated
Changes: 1 snapshots (1 modified, 0 added, 0 deleted)
What this means:
- Query snapshots have been automatically updated to match current output
- These changes reflect modifications to database queries or schema
Next steps:
- Review the query changes to ensure they're intentional
- If unexpected, investigate what caused the query to change
⚠️ Backend coverage — 94.0% of changed backend lines covered — 7 uncovered
🧪 Backend test coverage
Patch coverage — changed backend lines (products + core): ███████████████████░ 94.0% (130 / 137)
| File | Patch | Uncovered changed lines |
|---|---|---|
products/surveys/backend/api/survey.py |
50.0% | 3319–3321 |
products/actions/backend/api/action.py |
86.7% | 84–86, 100 |
🤖 Agents: add a test covering the lines above, or note why under "How did you test this code?". Machine-readable gap list: the patch-coverage artifact on this run (gh run download 31718781561 -n patch-coverage), or the coverage-data block at the end of this comment.
Per-product line coverage (touched products)
| Product | Coverage | Lines |
|---|---|---|
platform_features |
██░░░░░░░░░░░░░░░░░░ 12.1% |
7 / 58 |
batch_exports |
████████░░░░░░░░░░░░ 39.2% |
8,862 / 22,581 |
warehouse_sources_queue |
█████████░░░░░░░░░░░ 45.9% |
90 / 196 |
demo |
███████████░░░░░░░░░ 56.3% |
1,497 / 2,661 |
data_tools |
█████████████░░░░░░░ 66.2% |
90 / 136 |
tasks |
██████████████░░░░░░ 70.1% |
37,694 / 53,745 |
ai_gateway |
███████████████░░░░░ 75.0% |
9 / 12 |
signals |
█████████████████░░░ 82.8% |
27,737 / 33,513 |
apm |
█████████████████░░░ 83.9% |
1,222 / 1,457 |
cdp |
█████████████████░░░ 84.2% |
3,914 / 4,649 |
data_modeling |
█████████████████░░░ 86.4% |
8,752 / 10,130 |
wizard |
█████████████████░░░ 86.7% |
1,090 / 1,257 |
actions |
█████████████████░░░ 86.8% |
746 / 859 |
notebooks |
█████████████████░░░ 87.1% |
8,492 / 9,747 |
cohorts |
██████████████████░░ 87.6% |
6,527 / 7,451 |
product_tours |
██████████████████░░ 87.9% |
1,303 / 1,482 |
data_warehouse |
██████████████████░░ 88.2% |
11,965 / 13,565 |
managed_warehouse |
██████████████████░░ 88.8% |
6,536 / 7,364 |
business_knowledge |
██████████████████░░ 89.0% |
4,384 / 4,928 |
exports |
██████████████████░░ 89.1% |
7,529 / 8,449 |
engineering_analytics |
██████████████████░░ 89.4% |
6,839 / 7,649 |
dashboards |
██████████████████░░ 89.5% |
6,216 / 6,949 |
visual_review |
██████████████████░░ 89.5% |
5,870 / 6,558 |
alerts |
██████████████████░░ 90.4% |
4,542 / 5,027 |
mcp_analytics |
██████████████████░░ 90.4% |
3,968 / 4,388 |
conversations |
██████████████████░░ 90.5% |
18,399 / 20,324 |
links |
██████████████████░░ 90.6% |
183 / 202 |
streamlit_apps |
██████████████████░░ 90.7% |
2,630 / 2,901 |
error_tracking |
██████████████████░░ 91.2% |
11,233 / 12,323 |
stamphog |
██████████████████░░ 91.3% |
4,505 / 4,936 |
canvas |
██████████████████░░ 92.2% |
2,678 / 2,904 |
slack_app |
██████████████████░░ 92.5% |
11,333 / 12,254 |
managed_migrations |
███████████████████░ 92.6% |
1,556 / 1,681 |
early_access_features |
███████████████████░ 92.9% |
1,347 / 1,450 |
notifications |
███████████████████░ 92.9% |
1,060 / 1,141 |
ai_observability |
███████████████████░ 92.9% |
17,232 / 18,548 |
web_analytics |
███████████████████░ 92.9% |
16,337 / 17,578 |
mcp_store |
███████████████████░ 93.0% |
7,125 / 7,665 |
surveys |
███████████████████░ 93.2% |
5,921 / 6,354 |
posthog_ai |
███████████████████░ 93.2% |
1,326 / 1,422 |
reminders |
███████████████████░ 93.4% |
468 / 501 |
marketing_analytics |
███████████████████░ 93.5% |
16,404 / 17,550 |
approvals |
███████████████████░ 93.5% |
3,491 / 3,734 |
product_analytics |
███████████████████░ 93.6% |
7,124 / 7,611 |
legal_documents |
███████████████████░ 93.8% |
1,628 / 1,736 |
review_hog |
███████████████████░ 94.1% |
9,319 / 9,906 |
endpoints |
███████████████████░ 94.3% |
8,814 / 9,349 |
workflows |
███████████████████░ 94.4% |
8,046 / 8,526 |
tracing |
███████████████████░ 94.4% |
2,678 / 2,837 |
skills |
███████████████████░ 94.7% |
3,869 / 4,085 |
data_quality |
███████████████████░ 95.3% |
1,457 / 1,529 |
logs |
███████████████████░ 95.4% |
12,097 / 12,686 |
messaging |
███████████████████░ 95.6% |
3,423 / 3,580 |
replay_vision |
███████████████████░ 95.7% |
19,934 / 20,834 |
experiments |
███████████████████░ 95.8% |
28,862 / 30,123 |
annotations |
███████████████████░ 96.2% |
732 / 761 |
revenue_analytics |
███████████████████░ 96.3% |
1,887 / 1,960 |
customer_analytics |
███████████████████░ 96.5% |
12,779 / 13,248 |
user_interviews |
███████████████████░ 96.5% |
2,638 / 2,734 |
feature_flags |
███████████████████░ 96.5% |
18,320 / 18,977 |
access_control |
███████████████████░ 96.7% |
873 / 903 |
growth |
███████████████████░ 96.7% |
6,494 / 6,717 |
warehouse_sources |
███████████████████░ 97.5% |
391,490 / 401,537 |
data_catalog |
████████████████████ 97.8% |
2,746 / 2,808 |
analytics_platform |
████████████████████ 98.1% |
2,254 / 2,298 |
metrics |
████████████████████ 98.2% |
2,491 / 2,536 |
pulse |
████████████████████ 98.4% |
2,017 / 2,049 |
live_debugger |
████████████████████ 99.2% |
613 / 618 |
field_notes |
████████████████████ 99.4% |
158 / 159 |
Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.
Generated-By: PostHog Code Task-Id: 0de188ef-82b7-4fdf-b50a-f34a53c3bf05
🦔 ReviewHog reviewed this pull requestFound 0 must fix, 1 should fix, 0 consider. Published 1 finding (view the review). |
|
ReviewHog Alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏 |
There was a problem hiding this comment.
ReviewHog Report
Changes
Issues: 3 issues
Files (7)
posthog/models/event/event.pyposthog/models/property/util.pyproducts/actions/backend/api/action.pyproducts/actions/frontend/generated/api.schemas.tsproducts/actions/frontend/generated/api.zod.tsservices/mcp/src/api/generated.tsservices/mcp/src/generated/actions/api.ts
The selector compiler's wildcards spanned the whole elements_chain, so a descendant part could match text inside an unrelated element: `form button.btn` matched a chain whose only "form" was inside an href, and a class and an attribute from one selector part could land on different elements. Scope those wildcards to a single element. A semicolon only separates elements outside a quoted value, so the run is quote-aware: an inline style="display: flex; gap: 4px" carries its own, and treating that as a boundary stops #id and :nth-child(n) selectors matching at all. Also: - Emit attributes in the order the chain serializes them, so an id combined with an attribute can match. - Parse :nth-of-type(n), and :nth-child(n) alongside it, so the selectors the toolbar generates stop compiling to a matcher nothing satisfies. - Warn on attribute operators and on a pseudo-class folded into a class name, and stop warning on the universal selector. - Keep selector_warning out of the surveys payload, which is cached and served to every SDK client. - Skip the compile cache for outsized and non-string selectors. - Apply the same attribute parsing in the Node legacy-webhook matcher, and stop its pseudo-class loop spinning on :nth-child(0). Generated-By: PostHog Code Task-Id: 378e62f3-abd4-4bc3-a6dc-6bb106ebff6e
|
Reviewed this closely. The four target shapes are genuinely fixed, but the new Matching
Parser
selector_warning (covers ReviewHog's three findings)
Node matcher
Other
Not included: Known gaps, all pre-existing: the trailing character class omits Tests: 265 passed with 36 snapshots; repo-wide |
|
React Doctor found 85 issues in 34 files · 14 errors & 71 warnings. Errors
71 warnings
21 more warnings not shown. Reviewed by React Doctor for commit |
👀 Auto-assigned reviewersThese soft owners were skipped because they only have minor changes here. Nothing blocks merge, so self-assign if you'd like a look:
Soft owners come from each directory's |
There was a problem hiding this comment.
ReviewHog Report
Changes
Issues: 1 issue
Files (10)
nodejs/src/cdp/legacy-webhooks/action-matcher.tsposthog/models/event/event.pyposthog/models/property/util.pyproducts/actions/backend/api/action.pyproducts/actions/frontend/generated/api.schemas.tsproducts/actions/frontend/generated/api.zod.tsproducts/desktop/packages/api-client/src/generated.tsproducts/surveys/backend/api/survey.pyservices/mcp/src/api/generated.tsservices/mcp/src/generated/actions/api.ts
Generated-By: PostHog Desktop Task-Id: f36c0d1f-90f5-4c14-9492-1890fc06cee2
…s-selectors-that-a778a4
…s-selectors-that-a778a4
The quoted-value pattern now treats \" as part of the value, the way split_chain_regex does, so an attribute value containing a quote no longer breaks matching for its whole element. The tail of each selector part skips within the element instead of through an allowlisted character class. A sibling class the selector never names, like w-1/2 or !mt-0, no longer blocks the match — the silent zero-match this PR exists to fix, reachable with plain Tailwind class names. Generated-By: PostHog Desktop Task-Id: 3ea65d95-a5e7-4c8b-b094-3a529544de62
HostHog preview —
|
Adds manage.py audit_action_selectors: classifies every action selector, measures old-vs-new compiler match counts over recent $autocapture events, buckets each selector by whether PR #80653 changes its behavior, and can apply measured-safe '>'-to-space rewrites behind explicit flags. Vendors frozen copies of both compilers so counts stay computable before and after the merge. Generated-By: PostHog Desktop Task-Id: 95dbb679-5167-42a9-b7e0-e8ade8fb7485
|
This PR hasn't seen activity in a week! Should it be merged, closed, or further worked on? If you want to keep it open, please remove the |
TL;DR
You track clicks by giving an action a CSS selector like
.btn[disabled]. The compiler that turns a selector into an event matcher mangled several common shapes, so those actions counted zero clicks and showed no error. This fixes the compiler and adds a warning for CSS it cannot support.Problem
An autocapture action using a class-plus-attribute, compound-attribute, or descendant CSS selector saved cleanly, showed no error, and then matched zero events forever. The selector compiler turned those shapes into a regex no real element can match, and the failure was silent at both save time and query time — so a team sees an action stuck at zero and concludes nobody clicked.
Also fixed:
:nth-child(n):nth-of-type(n), the shape PostHog's ownelementToSelectoremits, never parsed correctly.Why: silent zero-match is the worst failure mode here — nothing surfaces the problem, so the data just looks like an absence of user behaviour.
Changes
[attr="value"]pair in a part is read (previously two collapsed into one nonsense attribute); both positional pseudo-classes parse.build_selector_regex: a descendant combinator now steps whole elements, and each part's conditions are confined to a single element. Attributes are ordered the way the chain serializes them, so#submit[type="button"]can match.selector_warningper step when a selector uses CSS the compiler cannot support (a pseudo-class, an attribute operator), which otherwise compiles to a matcher that matches nothing.:nth-child(0)and unsupported pseudo-classes.Refs #15480 (non-alphanumeric CSS classes) — this fixes the attribute, descendant, and
!-class shapes from that report but not the dot-in-class case.Reviewer notes
Four things worth knowing before reading the diff:
This is a fix on top of a fix. The first commit corrected the four broken shapes but traded the silent zero-match bug for a silent over-match one — the descendant wildcard was unanchored, so
form button.btnmatched chains with noformancestor at all (matching "form" inside anhref), and within one part a class and an attribute could match on different elements. The second commit confines matching to element boundaries. Review the two together.The fix reaches queries immediately, destinations later.
selector_to_exprrecompiles the regex on every query, so insights and trends — the reported symptom — are fixed on deploy for all existing actions. Real-time matching reads persistedAction.bytecode, refreshed only on save, so destinations and hog-function filters keep the old semantics until an action is next edited. Actions will temporarily disagree between insight numbers and destination firing.A backfill is deliberately out of scope. Recompiling persisted bytecode is when previously-silent destinations start firing at full autocapture rate, so it needs its own change with batching and a ramp.
The part tail is no longer an allowlist (eb7be9e). Any class on the matched element with a character outside the old allowlist —
w-1/2,!mt-0,@md:grid— used to compile into a matcher that silently matched nothing, whether the selector named that class or merely a sibling. The tail now skips within element boundaries instead. The same commit makes the quoted-value patterns accept the\"escaping the chain serializer emits, so a quote inside an attribute value no longer breaks matching for its element.Where the risk is. 17 files, but only three change behaviour:
event.py(the parser),util.py(the regex builder), andaction-matcher.ts(the Node equivalent). Three more are the visible footprint of that change — the compiled-regex assertions intest_property.pyandtest_action_to_expr.py, and thetest_web_goals.ambrsnapshot. The remaining eleven are the additiveselector_warningfield, its two serializers, its tests, and seven generated client/schema files that carry no logic. Read the first three closely; the rest are consequence.This changes matching semantics on core autocapture in both directions — tightening within a part, loosening across descendants — so some currently-matching actions will match less. The snapshot updates in
test_property.pyandtest_web_goals.ambrare the visible edge of that.How did you test this code?
mypyclean,ruffclean. Web-analytics snapshot regenerated from a real run.Automatic notifications
Docs update
None. No documented workflow changes.
🤖 Agent context
Autonomy: Fully autonomous
Created with PostHog Desktop from this inbox report.