Trunk-Based Development skill for Codex
Merge daily, keep main releasable, revert first. Codex helps a team move from long-lived feature and develop branches to a single releasable main. It measures branch and PR age, sets up required checks and a merge queue that listens on merge_group, and splits large features with thin slices, flags, branch by abstraction and expand-contract migrations. A broken main gets reverted first and re-landed with a test.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- trunk-based-development Installs to ~/.agents/skills/trunk-based-development/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install trunk-based-development The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project trunk-based-development Installs to .agents/skills/trunk-based-development/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/trunk-based-development.zipunzip -o trunk-based-development.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) trunk-based-development PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
104-line POSIX script. No sudo, no telemetry. Read install.sh
Use it in Codex
Start a prompt with $trunk-based-development, or pick it from /skills.
You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.
What Codex reads before loading it
Sets up and practises trunk-based development - one always-releasable main branch, branches that live less than two days, small PRs, feature flags and branch by abstraction for unfinished work, required CI checks, GitHub merge queues and fast revert-first recovery. Use when a team wants to move off long-lived feature or develop branches, when PRs sit open for weeks and merge conflicts pile up, when configuring branch protection and merge queue for main, or when splitting a big feature so it can merge daily.
Use it when
- Merge conflicts and stale branches are slowing the team down
- You are configuring branch protection, required checks or a merge queue
- A large feature must merge incrementally without exposing it
- Main breaks often and nobody owns getting it green
Use something else when
- You need to cut and stabilise a versioned release: use release-management
- You want to manage chains of dependent PRs: use stacked-pull-requests
How to check the result
A test PR travels through the merge queue onto main with all required checks reported, and a direct push to main is rejected.
The mistake it prevents
Turning on a merge queue without adding the merge_group trigger to the CI workflow, so queued PRs never get their required checks.
The standard: main is always releasable, every engineer merges to it at least daily,
no branch lives longer than about two days, and a broken main is fixed or reverted
within minutes. Unfinished work hides behind flags or abstractions, not behind branches.
When it fits (and when to stop)
| Situation | Recommendation |
|---|---|
| Web services or apps deployed continuously | Trunk-based with flags; release from main |
| Mobile or desktop apps with store reviews | Trunk-based, plus short-lived release branches cut from main (see release-management) |
| Libraries supporting several major versions | Trunk for the newest line; maintenance branches per supported major |
| No automated tests and no CI | Build the test and CI gate first; trunk without a gate just breaks main faster |
| Regulated releases with manual QA sign-off | Trunk for development, release branches for the signed-off candidate |
Migration workflow
- [ ] 1. Measure today: open PR age, branch age, merge conflicts per week, main red time
- [ ] 2. Make CI on main fast (< 10 min) and trustworthy (quarantine flaky tests)
- [ ] 3. Protect main: required checks, required review, linear history, no direct pushes
- [ ] 4. Add a merge queue once more than a few PRs merge per day
- [ ] 5. Introduce a flag system for unfinished work (feature-flags)
- [ ] 6. Merge or retire long-lived branches one by one; delete develop
- [ ] 7. Adopt the revert-first rule for a red main
- [ ] 8. Re-measure after a month
Measure with gh (needs network and auth):
gh pr list --state open --json number,title,createdAt,author \
--jq '.[] | "\(.createdAt[:10]) #\(.number) \(.title)"' | sort | head -20
git for-each-ref --sort=committerdate refs/remotes/origin \
--format='%(committerdate:short) %(refname:short)' | head -30 # stalest branches first
Branch protection and CI gates
Configure on GitHub with a ruleset (Settings, Rules, Rulesets) or classic branch
protection. Changing repository settings affects the whole team, so show the user the
exact rules and get confirmation before applying them via gh api.
Recommended rules for main:
- Require a pull request; 1 approval (2 for high-risk paths via CODEOWNERS).
- Require status checks: build, unit tests, lint, typecheck. Keep the required set small and fast; slow suites (E2E, load) run post-merge or nightly and page on failure.
- Require branches to be up to date, or better, use a merge queue (below).
- Require linear history; allow squash merge (one commit per PR on
main). - Block force pushes and deletion.
Merge queue
A merge queue tests each PR on top of the PRs ahead of it, so main never receives a
combination that was not tested together. In GitHub Actions the workflow must also run on
the merge_group event, or queued PRs wait forever:
on:
pull_request:
merge_group:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: make ci # same command for PRs, the queue and main
Keeping branches short
A branch is short when its diff is reviewable in under 30 minutes and it merges within a day or two. Techniques, in order of preference:
- Slice vertically but thinly. Ship the database column, then the write path, then the
read path, then the UI, each as its own PR that leaves
mainworking. - Feature flag the user-visible part. Code merges daily; the flag stays off until the slice is complete.
- Branch by abstraction for replacing a component:
- introduce an interface in front of the old implementation (PR 1);
- build the new implementation behind it, unused or flagged (PRs 2 to n);
- switch callers via the flag, then delete the old implementation and, if pointless, the interface (final PRs).
- Expand and contract for schemas and APIs: add the new field, write both, migrate readers, stop writing the old one, drop it. Each step is a separate deploy.
- Dark launch: run new code in production on real traffic without exposing results, compare outputs, then switch.
Keep the local branch fresh to avoid surprise conflicts:
git fetch origin && git rebase origin/main # on your own unshared branch only
Never rebase or force-push a branch someone else is working on without asking.
Red main protocol
- Whoever notices posts in the team channel (ask the user before sending messages).
- If the cause is obvious and the fix is one small commit, fix forward within 10 minutes.
- Otherwise revert the offending PR immediately:
gh pr revert <number>orgit revert <sha>via a PR. Reverting is not blame; it restores everyone’s trunk. - The author re-lands the change with the fix and a test that would have caught it.
Flaky tests break trust in the gate faster than real failures. Quarantine a flaky test within a day (mark it non-blocking, file an issue with an owner), never let people retry until green as a habit.
Rules for agents working on trunk
- One task, one short branch, one PR. If the diff grows past roughly 400 changed lines of non-generated code, split it before asking for review.
- Every PR leaves
mainreleasable: tests pass, flags default off, migrations backward compatible with the currently deployed code. - Never push directly to
main, never bypass required checks, never force-push shared branches.
Gotchas
- Flags without cleanup turn trunk-based development into flag-based spaghetti; give each flag a removal date when it is created.
- “Short-lived” branches that are rebased daily for three weeks are long-lived branches. Measure age from first commit to merge, not from last rebase.
- A
developbranch kept “just for integration” doubles every merge and hides what is actually releasable. Delete it once flags cover unfinished work. - Required checks that take 40 minutes push people to batch work into big PRs. Speed up CI before tightening protection.
Output
## Trunk-based setup for <repo>
**Today:** median PR age 6 days, 9 branches older than 2 weeks, main red 4 h/week
**Protection on main:** <rules applied, after user confirmation>
**Required checks:** build, test, lint (p50 7 min)
**Merge queue:** enabled, workflow listens on merge_group
**Unfinished work strategy:** OpenFeature flags; branch by abstraction for payment client swap
**Branches to retire:** <list with owner and plan>
**Recovery rule:** revert first, re-land with a test
Verification
- A test PR passes through the queue and lands on
mainwith checks reported. - Direct push to
mainis rejected; a PR with failing checks cannot merge. - After a month: median PR age under 2 days and main red time trending down.
What the zip contains
SKILL.md plus the interface file below. Nothing runs on install.
agents/openai.yaml
Gives the Codex app a display name, a one-line description and a starter prompt for this skill.
interface:
display_name: "Trunk-Based Development"
short_description: "Merge daily, keep main releasable, revert first"
default_prompt: "$trunk-based-development We have a develop branch and PRs open for weeks - help us move to trunk-based development" About this skill
Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.