Skip to content

chore(ci): route merges through the trunk merge queue - #74065

Merged
gantoine merged 7 commits into
masterfrom
chore/merging-prs-skill
Jul 28, 2026
Merged

chore(ci): route merges through the trunk merge queue#74065
gantoine merged 7 commits into
masterfrom
chore/merging-prs-skill

Conversation

@gantoine

@gantoine gantoine commented Jul 27, 2026

Copy link
Copy Markdown
Member

Problem

We're turning on Trunk's merge queue. Two gaps to close before that's usable.

First, gh pr merge and the GitHub merge button become dead ends. Agents don't know that. Left alone they'll try to merge directly, hit the branch ruleset, and either give up or start improvising. And "merge it" is really two jobs: enqueue, then babysit the PR until Trunk lands it or kicks it out.

Second, Trunk can't place a PR into a queue lane without an impacted-targets upload for its head SHA. Without one, PRs never enter the queue at all. This bit posthog/code, where fork PRs silently sat outside the queue and had to be merged by hand.

Changes

Impacted-targets upload. .github/workflows/trunk-impacted-targets.yml POSTs to Trunk's setImpactedTargets on every PR event. It's a port of the workflow already running in posthog/code, including the fork-auth handling: fork pull_request runs get no repo secrets, so they authenticate with x-forked-workflow-run-id and Trunk verifies the run id against the head SHA. Internal PRs use x-api-token.

It reports impactedTargets: "ALL" rather than a computed list. That's always correct - it can never under-report and let the queue merge conflicting PRs in parallel. It buys no parallelism, it just gets every PR into the queue. Narrowing it to real target names later is a follow-up, and ci-paths-filter.yml already computes something close to what that would need.

Two deviations from the posthog/code version, both because this repo's conventions differ:

  • It's a workflow_call reusable workflow folded into pr-housekeeping.yml, not its own dispatch. That parent exists precisely to keep small always-fire PR workflows from each burning a run against the 500-runs/10s dispatch cap. The parent's concurrency group is already keyed on PR number, which also happens to be what the posthog/code version worked around by hand (fork PRs frequently share a head_ref, so keying on branch name lets one PR cancel another's upload).
  • The step no-ops with a warning annotation when TRUNK_API_TOKEN is absent on an internal PR, so this can land before the secret does.

The merging-prs skill covers the whole merge loop: preflight the PR, enqueue with a /trunk merge comment, confirm Trunk picked it up, watch until it lands, handle the kick-out case by reading the Trunk bot's comment. A failed impacted-targets upload is the first thing it checks when a PR doesn't appear in the queue.

AGENTS.md gets the rule itself, since not every agent will load the skill, plus a line in the stacked-PRs section - restacking force-pushes, and that silently drops a queued branch.

Warning

Also worth confirming: the AGENTS.md line names the check run Trunk Merge Queue (master) exactly. The skill matches on a Trunk Merge Queue prefix so it tolerates any suffix, but the AGENTS.md line is the thing to re-check against the real queue config.

How did you test this code?

bin/hogli lint:workflows and actionlint (1.7.12, the pinned CI version, with SHELLCHECK_OPTS=--severity=error) both pass on the two changed workflows. hogli ci:preflight --strict passed on push.

I did not exercise the upload or the merge flow end to end - the queue isn't wired up on this repo yet, and I'm not going to POST to Trunk's API from a local shell to find out. The payload shape and auth headers are copied from the workflow currently running in posthog/code, not written from the docs.

No tests added. There's nothing here a test would catch that the two linters don't.

Automatic notifications

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

Docs update

Not applicable - this is agent-facing repo documentation and CI wiring, not user docs.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

I (actually Claude, via Claude Code) started from a draft skill written against a main-default repo, then ported the impacted-targets workflow off posthog/code once it came up that the queue needs it.

Skills invoked: /writing-skills before writing the skill, /authoring-ci-workflows before the workflow. The latter is why this is a reusable workflow under pr-housekeeping.yml rather than a standalone dispatch, and why the fork handling is shaped the way it is.

Decisions worth flagging for review:

  • I considered gating the whole thing on a repository variable, the way /gating-production-deploys gates deploys on CD_DEPLOY_ENABLED. Rejected it: that skill notes gating pull_request builds on a variable breaks contributor CI, which suggests variables don't reach fork PR runs. Since forks are the case this workflow most needs to serve, a vars gate would defeat the point. Hence the token-presence check instead, which only covers internal PRs, and the merge-ordering warning above for the fork half.
  • The draft skill had an allowed-tools list scoped to a handful of gh invocations. I dropped it - it would have blocked the skill's own "fix the failure, push, re-enqueue" path, and most skills in .agents/skills don't restrict tools. The gh pr merge prohibition stays as a hard rule in the prose, and the ruleset backs it up regardless.
  • The draft's polling loop was a foreground sleep 60. Claude Code blocks foreground sleep, so it would have stalled. Rewrote it as a state-transition script for the Monitor tool or background Bash, printing only on change and exiting on MERGED or CLOSED.
  • On skill placement: /writing-skills turned out to be aimed at product skills under products/*/skills/, which get published to PostHog/skills for customer-facing agents. That's a different thing from the repo-engineering skills in .agents/skills this PR adds. Naming and description conventions still applied, but the tooling it describes (hogli lint:skills, hogli build:skills) doesn't cover .agents/skills. Might be worth splitting that guidance.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gantoine gantoine self-assigned this Jul 27, 2026
@gantoine gantoine added the skip-agent-review Save $$$, skip auto agent reviews (Greptile) — use for trivial or chore PRs label Jul 27, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gantoine gantoine changed the title chore(agents): add merging-prs skill for the trunk merge queue chore(ci): route merges through the trunk merge queue Jul 27, 2026
@gantoine
gantoine marked this pull request as ready for review July 28, 2026 15:41

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 15:42
@gantoine
gantoine removed the request for review from a team July 28, 2026 15:42
Comment thread .agents/skills/merging-prs/SKILL.md
@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
.agents/skills/merging-prs/SKILL.md:91
**Failed queue check never terminates**

When Trunk fails the queue check while leaving the PR open, this loop records the failure but exits only for `MERGED` or `CLOSED`, causing failure handling to be delayed until the one-hour monitor timeout.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "Merge remote-tracking branch 'origin/mas..." | Re-trigger Greptile

Comment thread .github/workflows/trunk-impacted-targets.yml
gantoine and others added 2 commits July 28, 2026 13:17
A PR that fails the queue check stays OPEN, so the watch loop's
MERGED|CLOSED exit never fired and failure handling waited out the
one-hour monitor timeout. Exit on a terminal non-success conclusion
too, picking the newest queue check so a stale failed run from an
earlier enqueue can't trigger a false exit.

Also paginate the check-runs calls — a head SHA here carries 200-350
check runs and the unpaginated default returns only the first 30, which
would hide the queue check entirely.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Comment thread .agents/skills/merging-prs/SKILL.md Outdated
@veria-ai

veria-ai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

PR overview

All previously flagged issues have been addressed. No open security concerns remain on this pull request.

Security review

No open security issues remain on this pull request.

Fixed/addressed: 1 · PR risk: 0/10

gantoine and others added 2 commits July 28, 2026 13:48
Step 4 piped every recent comment body into context unfiltered, at the
one point in the skill where the agent is primed to edit, push and
re-enqueue. Anyone able to comment on a PR could forge a Trunk failure
report and steer those actions.

Filter to the trunk-io[bot] identity (GitHub forbids brackets in human
usernames, so the login is not registrable by a person), and state that
the body is diagnostic data rather than instructions, with the check run
and workflow logs as the authority for what failed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The check run is the authoritative source: only an app with checks:write
can write one, so it cannot be forged, while a PR comment can be posted
by anyone with read access. Read conclusion, details_url and output off
the queue check the monitor already polls, and assert the writing app is
trunk-io.

Note Trunk's MCP investigate-ci-failure as optional enrichment. It is
not a dependency — it needs a workflow URL the check run already gives
us, returns nothing when a job fails before tests run, and only has data
while TRUNK_UPLOAD_ENABLED is on.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@gantoine
gantoine merged commit bc6cfe1 into master Jul 28, 2026
174 checks passed
@gantoine
gantoine deleted the chore/merging-prs-skill branch July 28, 2026 18:17
@deployment-status-posthog

deployment-status-posthog Bot commented Jul 28, 2026

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-07-28 18:56 UTC Run
prod-us ✅ Deployed 2026-07-28 19:13 UTC Run
prod-eu ✅ Deployed 2026-07-28 19:15 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