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.

  • Engineering Workflow
  • $trunk-based-development
  • 157 lines
  • 2 files
  • ~1,700 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- trunk-based-development

Installs to ~/.agents/skills/trunk-based-development/ for every project on this machine.

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

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.

~/.agents/skills/trunk-based-development/SKILL.md

--- name: trunk-based-development description: Sets up and practises trunk-based development - one always-releasable main branch, branches that live less than two days, small PRs, feature flags… license: MIT ---

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)

SituationRecommendation
Web services or apps deployed continuouslyTrunk-based with flags; release from main
Mobile or desktop apps with store reviewsTrunk-based, plus short-lived release branches cut from main (see release-management)
Libraries supporting several major versionsTrunk for the newest line; maintenance branches per supported major
No automated tests and no CIBuild the test and CI gate first; trunk without a gate just breaks main faster
Regulated releases with manual QA sign-offTrunk 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:

  1. 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 main working.
  2. Feature flag the user-visible part. Code merges daily; the flag stays off until the slice is complete.
  3. 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).
  4. 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.
  5. 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

  1. Whoever notices posts in the team channel (ask the user before sending messages).
  2. If the cause is obvious and the fix is one small commit, fix forward within 10 minutes.
  3. Otherwise revert the offending PR immediately: gh pr revert <number> or git revert <sha> via a PR. Reverting is not blame; it restores everyone’s trunk.
  4. 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 main releasable: 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 develop branch 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 main with checks reported.
  • Direct push to main is 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.yamlgenerated from this page

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.

esc