fix(ci): run the full suites on trunk merge-queue branches - #74367
Conversation
The queue tests a PR on its own trunk-merge/** branch, and that branch opens as a draft PR. Every suite reads draft status as "narrow the tests", so the run that actually gates the merge was getting the reduced matrices: the snob-selected Django subset instead of the full sharding, a narrowed jest and storybook selection, no MCP integration tests, and no E2E at all, since that suite skips drafts outright. Treat a trunk-merge/** head as ready everywhere draft status decides scope. The selection jobs no longer run for it, so nothing narrows, and the heavy matrices do. The no-ci gate is exempted too, so a label copied onto the queue's PR cannot silence a whole workflow. Every change is additive: github.head_ref never starts with trunk-merge/ on a normal PR, and it is empty on push, so behavior is unchanged for everything else. That also keeps open PRs working without rebasing. Left alone deliberately: the backend coverage comment (draft == false) stays off, since commenting on the queue's throwaway PR is noise, and the E2E debounce only fires on synchronize, which the queue's own branch does not produce. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
😎 Merged successfully - details. |
The queue opens a PR per batch titled after its branch, like trunk-merge/pr-70624/<uuid>. That can never satisfy Conventional Commits, so the title check fails on every queue run and reports a red job against the batch. Skip it for trunk-merge/** heads. Nothing is lost: the title that matters belongs to the PR being queued and was already validated when that PR was opened or edited. The queue's PR is a throwaway that no one reads and that never merges through the GitHub button. Matched on the head ref prefix rather than the title, so a normal branch whose name merely mentions trunk-merge (this one, for instance) is still validated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
🤖 CI report✅ Bundle size — no changeUncompressed size of every built Total: 65.20 MiB · no change No file changed by more than 1000 B. Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report ✅ Eager graph — within budgetHow 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
🟢 Largest files eagerly shipped from
|
| 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 |
|---|---|
| 281.6 KiB | ../node_modules/.pnpm/posthog-js@1.407.5/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 |
| 235.5 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 226.5 KiB | ../node_modules/.pnpm/posthog-js@1.407.5/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.2 KiB | src/lib/api.ts |
| 94.0 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.18 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.18 MiB · 17 files | no change | ████░░░░░░ 38.2% of 5.72 MiB |
| Deferred (lazy) | 2.07 MiB · 33 files | no change | 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 |
|---|---|
| 717.2 KiB | dist/toolbar/toolbar-app-JFACJRNE.css |
| 546.0 KiB | dist/toolbar/chunk-chunk-ZPQNFZXD.js |
| 484.3 KiB | dist/toolbar/chunk-chunk-SMT7UAIH.js |
| 133.6 KiB | dist/toolbar/chunk-chunk-V2BZAD6S.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-T5KY5WYR.js |
| 71.0 KiB | dist/toolbar/toolbar-app-SJJ5SQBP.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-27JL52RE.js |
| 35.6 KiB | dist/toolbar/chunk-chunk-5LXGT3CN.js |
| 20.9 KiB | dist/toolbar/chunk-chunk-EK265TQY.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 — no change
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 1363.16 MiB · no change
⚠️ Playwright — 2 flaky
🎭 Playwright report · View test results →
- Delete a person property (chromium)
- Preflight live mode (chromium)
These issues are not necessarily caused by your changes.
Annoyed by this section? Help fix flakies and failures and it will go green!
.depot/workflows/ci-backend.yml is a hand-maintained shadow of canonical so the depot timing trial measures the same work, and ci-backend-shadow-drift.yml fails when canonical moves without it. The queue-branch changes only landed in canonical, so the shadow would have drifted and the check would have failed this PR. Same three sites, same polarity: the no-ci gate and the heavy matrices widen for a trunk-merge/** head, and select-tests stops running for one so nothing narrows. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Prompt To Fix All With AI### Issue 1
.github/workflows/ci-backend.yml:525
**Queue produces empty Django matrix**
When a draft `trunk-merge/` PR reaches this condition, `select-tests` is skipped and its unset mode is interpreted downstream as draft skip mode, producing an empty Django matrix; the Django job is then skipped and the required aggregator accepts that result, allowing the merge queue to pass without running Django tests.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "fix(ci): skip conventional-commit title ..." | Re-trigger Greptile |
|
Note 🤖 stamphog reviewed Gates denied this as CI infra territory (T2-never, deny-list infra_cicd), and there is an unresolved P1 review comment from Greptile claiming the queue's draft-narrowing fix can still produce an empty Django matrix that silently passes required checks — a real CI-safety concern on a workflow change that has not been addressed.
Gate mechanics and policy version
|
Skipping select-tests for a trunk-merge/** branch left MODE empty while IS_DRAFT was still true, because the queue's PR opens as a draft. build_django_matrix normalizes that combination to MODE=skip and emits include=[], the django job is gated off an empty matrix and skips, and check_required_result treats skipped as success. The result is a green "Django Tests Pass" on a run that executed no Django tests, which the merge queue would then merge. That is strictly worse than the narrowing this branch set out to fix: before, the queue at least ran the snob-selected subset. Report a trunk-merge/** head as not-draft to build_django_matrix. The empty MODE then falls through to the full matrix, which is what the queue has to run. Normal drafts are untouched and still skip to defer to their ready-for-review run. Only ci-backend had this trap. ci-frontend and ci-storybook fall back to their full matrix when the selection output is missing (`fromJson(outputs.matrix || <full>)`), so a skipped selector there fails safe already. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
/trunk merge |
|
/trunk merge |
|
/trunk merge |
Master landed the merge-queue rollout (#74065, #74367) and the impacted-targets upload (#74266) in the meantime, so: - trunk-impacted-targets.yml now takes master's version wholesale; it supersedes this branch's standalone draft with a compute/upload split and real lane computation. - Kept master's removal of the labeled/unlabeled triggers on top of this branch's removal of ready_for_review. - Kept master's hogli-lint not-a-required-gate markers and the non-pytest-Python wording in ci-mcp.
Problem
The merge queue tests a PR on its own
trunk-merge/**branch, and that branch opens as a draft PR. Every suite reads draft status as "narrow the tests", so the run that actually gates the merge was getting the reduced matrices.--findRelatedTestsselectionE2E is the one that matters most: the queue could merge a PR without ever running it.
The draft narrowing itself is correct and worth keeping. Drafts can't merge, so cheap feedback is the right trade. The bug is that the queue's branch looks like a draft while being the opposite: it's the last thing to run before master.
Separately, the queue's PR is titled after its own branch, so the Conventional Commits title check fails on every batch. Example run:
Changes
Treat a
trunk-merge/**head as ready everywhere draft status decides scope, and skip the title check for it. Fourteen conditions across seven files, sorted by what each one actually does rather than by pattern-matching ondraft:select-tests, frontend jest, storybook stories&& !startsWith(...)so they don't run, and nothing narrowschanges, MCP integration|| startsWith(...)so they do runno-cigatepr-housekeeping.ymllint-pr.depot/workflows/ci-backend.ymlNothing is lost by skipping the title check: the title that matters belongs to the PR being queued and was already validated when that PR was opened or edited. The queue's PR is a throwaway that never merges through the GitHub button.
Left alone deliberately: the backend coverage comment stays off (commenting on the queue's throwaway PR is noise), and the E2E debounce only fires on
synchronize, which the queue's branch doesn't produce.Note
Every change is additive.
github.head_refnever starts withtrunk-merge/on a normal PR and is empty onpush, so behavior is identical for everything else. That also means open PRs don't need to rebase to stay green, which is the compat trap workflow edits usually hit.All matching is on the head ref prefix, never the title, so a normal branch whose name merely mentions trunk-merge (this one, for instance) is still validated and still narrows on draft.
How did you test this code?
I can't exercise a
trunk-merge/**branch without a live queue run, so this is verified structurally rather than end to end:yaml.safe_load), and I checked all fourteen references land at the right polarity — negated at the three selection jobs, positive at the gates and matrices. Getting one backwards would silently do the opposite of the fix.startsWithpredicate against the real head ref from the failing run above plus normal branch names, confirming it skips only genuine queue branches.ci-backend-shadow-drift.ymlagainst master: both ci-backend files are in the diff, so the parity check passes.hogli lint:workflowspasses 6/6 across 105 workflows.hogli ci:preflight --strictpasses.No automated test covers this. The conditions are GitHub Actions expressions evaluated by the runner, and the only real signal is a queue run. Worth watching the first one and confirming the E2E and Django jobs actually appear on the
trunk-merge/**branch.Automatic notifications
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Claude Code (Opus 5) made these edits at my direction. I pointed out that the queue runs on draft branches while our suites narrow on draft status, then sent it the failing title-check run.
The work was mostly in classifying the sites rather than writing the change. A blind find-and-replace on
draftwould have been wrong in both directions: the selection jobs need the condition negated so they stop running, while the matrices need it widened so they start. Reading each one also turned up theno-cigate, which isn't test selection at all but would let a copied label silence an entire workflow on the queue's run.The failing run also settled an assumption the first version of this PR flagged as unverified — that the queue surfaces as real PRs rather than bare branch pushes. The title in that log confirms it, and gives the exact head-ref format the predicate matches.
Split out of #74266 (merge-queue lanes), which is unrelated and reviews separately.
Skills invoked:
/authoring-ci-workflows.