Skip to content

fix(ci): run the full suites on trunk merge-queue branches - #74367

Merged
trunk-io[bot] merged 4 commits into
masterfrom
chore/trunk-merge-full-suites
Jul 29, 2026
Merged

fix(ci): run the full suites on trunk merge-queue branches#74367
trunk-io[bot] merged 4 commits into
masterfrom
chore/trunk-merge-full-suites

Conversation

@gantoine

@gantoine gantoine commented Jul 28, 2026

Copy link
Copy Markdown
Member

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.

Suite What the queue's run was doing
Django snob-selected subset, ≤3 shards instead of the full sharding
Jest narrowed --findRelatedTests selection
Storybook narrowed visual-regression matrix
MCP integration tests skipped entirely
E2E skipped entirely — that suite drops all drafts

E2E 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:

No release type found in pull request title
"trunk-merge/pr-70624/f3dbe3c2-9d05-41e1-ae82-1e6222a6e33b"

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 on draft:

Category Sites Change
Selection jobs backend select-tests, frontend jest, storybook stories && !startsWith(...) so they don't run, and nothing narrows
Heavy matrices backend matrices, E2E changes, MCP integration || startsWith(...) so they do run
no-ci gate backend, frontend, mcp, storybook exempted, so a label copied onto the queue's PR can't silence a workflow
PR title check pr-housekeeping.yml lint-pr skipped — the queue's title can never be conventional
Depot shadow .depot/workflows/ci-backend.yml same three backend sites mirrored

Nothing 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_ref never starts with trunk-merge/ on a normal PR and is empty on push, 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:

  • All seven files parse (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.
  • Ran the startsWith predicate against the real head ref from the failing run above plus normal branch names, confirming it skips only genuine queue branches.
  • Simulated ci-backend-shadow-drift.yml against master: both ci-backend files are in the diff, so the parity check passes.
  • hogli lint:workflows passes 6/6 across 105 workflows.
  • hogli ci:preflight --strict passes.

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

  • Publish to changelog?
  • Alert Sales and Marketing teams?

🤖 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 draft would 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 the no-ci gate, 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.

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>
@gantoine gantoine added the skip-agent-review Save $$$, skip auto agent reviews (Greptile) — use for trivial or chore PRs label Jul 28, 2026
@gantoine gantoine self-assigned this Jul 28, 2026
@trunk-io

trunk-io Bot commented Jul 28, 2026

Copy link
Copy Markdown

😎 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>
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

Bundle size — no change

Uncompressed size of every built .js bundle, compared against the base branch.

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 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.24 MiB · 22 files no change ███░░░░░░░ 27.6% of 4.51 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
8.09 MiB · 3,014 files no change ████████░░ 83.3% 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
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 →

⚠️ 2 flaky tests:

  • 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!

@gantoine
gantoine marked this pull request as ready for review July 28, 2026 21:09
Copilot AI review requested due to automatic review settings July 28, 2026 21:09

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team July 28, 2026 21:09
.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>
Comment thread .github/workflows/ci-backend.yml
@greptile-apps

greptile-apps Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Fix All in Claude Code

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

@gantoine gantoine added the stamphog Request AI approval (no full review) label Jul 28, 2026
@stamphog

stamphog Bot commented Jul 28, 2026

Copy link
Copy Markdown

Note

🤖 stamphog reviewed 9a707abf38747baa8fbe2fd73c8b58d00c6e4da1 — verdict: REFUSED

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.

  • 👍 on the PR from greptile-apps[bot], hex-security-app[bot].
  • Gates denied: CI/deploy workflow change matched the infra_cicd deny-list and was classified T2-never
  • Unresolved Greptile inline comment on .github/workflows/ci-backend.yml:525 alleges the queue can produce an empty Django matrix and pass without running Django tests — needs to be addressed or refuted before merge
  • CI workflow changes need independent human assurance; author is on owning team @PostHog/team-devex but the outstanding correctness concern blocks approval regardless
Gate mechanics and policy version
Gate Result
prerequisites all clear
deny-list matches: infra_cicd
size 31L, 7F substantive — within ceiling
tier classified as T2-never: T2-never (31L, 7F, two-areas, fix)
stamphog 2.0.0b3 .stamphog/policy.yml @ 1ebb50f · reviewed head 9a707ab

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Jul 28, 2026
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>
@gantoine
gantoine enabled auto-merge (squash) July 28, 2026 22:38
@gantoine
gantoine disabled auto-merge July 28, 2026 22:38
@gantoine

Copy link
Copy Markdown
Member Author

/trunk merge

@gantoine

Copy link
Copy Markdown
Member Author

/trunk merge

@gantoine

Copy link
Copy Markdown
Member Author

/trunk merge

@trunk-io
trunk-io Bot merged commit 377daf2 into master Jul 29, 2026
1579 of 1900 checks passed
@trunk-io
trunk-io Bot deleted the chore/trunk-merge-full-suites branch July 29, 2026 02:17
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 29, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-29 02:41 UTC Run
prod-us ✅ Deployed 2026-07-29 02:56 UTC Run
prod-eu ✅ Deployed 2026-07-29 02:55 UTC Run

gantoine added a commit that referenced this pull request Jul 30, 2026
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-agent-review Save $$$, skip auto agent reviews (Greptile) — use for trivial or chore PRs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants