GitHub Branch Protection

This repository uses two protected long-lived branches:

Feature work should happen on short-lived branches and merge into development only after CI passes. Release promotion should happen through a release/* branch, not by opening development or a feature branch directly against main.

Multi-Agent Branch Flow

Use this flow when multiple agents are working at the same time:

  1. Start each agent from the latest origin/development.
  2. Give each agent a short-lived branch with a bounded scope.
  3. Open each agent PR into development, not main.
  4. Require build-test to pass before merging into development.
  5. Resolve review comments and conversations before merge.
  6. Queue auto-merge after opening the PR so GitHub merges it when required checks and conversations are clear.
  7. Promote development to main with a separate release/* PR.

Agents should not commit directly to main. Direct pushes to development should be reserved for owner-controlled setup work, emergency repair, or branch-gate maintenance.

For agent PRs into development, use squash auto-merge with branch cleanup:

gh pr merge <pr-number> --squash --delete-branch --auto

Auto-merge does not bypass branch protection. GitHub waits for the required build-test status check, an up-to-date branch, and resolved review conversations before merging. If CI fails, review threads remain unresolved, the branch conflicts, or the PR is draft, the PR stays open.

Main Promotion Flow

main is production. Most work should never target it directly.

Normal release flow:

  1. Merge feature, docs, and hardening work into development.
  2. Create a release branch from the current development tip:

    git fetch origin main development
    git switch -c release/YYYY-MM-DD origin/development
    git merge -s ours --no-edit origin/main
    git push -u origin release/YYYY-MM-DD
    gh pr create --base main --head release/YYYY-MM-DD
    

    The -s ours merge records the current main tip as release ancestry while keeping the tested development tree. This prevents GitHub from replaying old development commits in future promotion PRs after a prior squash-style promotion.

  3. The PR to main must pass build-test and main-promotion-gate.
  4. Merge the release PR with a merge commit. Do not squash release promotions.

Patch exception:

  1. Create a hotfix/* or patch/* branch from origin/main.
  2. Label the PR patch or hotfix.
  3. Pass build-test and main-promotion-gate.
  4. Backport or merge the fix into development immediately after the production patch.

Do not open development directly against main. Do not open codex/*, feature, or cleanup branches directly against main unless they are explicit patch branches following the exception above.

Required CI Check

The required status check for development is:

build-test

It is defined in .github/workflows/development-targeted.yml for development PRs and pushes.

The required status checks for main are:

build-test
main-promotion-gate

build-test is defined in .github/workflows/ci.yml for main PRs and runs the full test suite. main-promotion-gate is defined in .github/workflows/main-promotion-gate.yml and rejects direct feature-branch or direct development PRs to main.

Enable these settings for both long-lived branches:

For development, require build-test and keep linear history enabled so feature PRs squash cleanly into the integration branch.

For main, require build-test and main-promotion-gate. Disable required linear history so release promotion PRs can use merge commits. Squash-merging a release promotion can make main and development file-identical while leaving their histories unrelated, which causes the next promotion PR to replay old development commits.

At the repository level, enable auto-merge and delete feature branches after merge. These settings apply to short-lived PR branches; branch protection still prevents deleting development or main.

For solo development, approving reviews can stay disabled or be set to zero required approvals. If more contributors are added, require at least one approving review and dismiss stale approvals on new commits.

GitHub CLI Setup

After the checks have appeared in GitHub at least once, apply branch protection:

Development:

gh api \
  --method PUT \
  "repos/paulboutin/aegis/branches/development/protection" \
  --input - <<'JSON'
{
  "required_status_checks": {
    "strict": true,
    "contexts": ["build-test"]
  },
  "enforce_admins": true,
  "required_pull_request_reviews": {
    "required_approving_review_count": 0,
    "dismiss_stale_reviews": true,
    "require_code_owner_reviews": false,
    "require_last_push_approval": false
  },
  "restrictions": null,
  "required_linear_history": true,
  "allow_force_pushes": false,
  "allow_deletions": false,
  "required_conversation_resolution": true
}
JSON

Main:

gh api \
  --method PUT \
  "repos/paulboutin/aegis/branches/main/protection" \
  --input - <<'JSON'
{
  "required_status_checks": {
    "strict": true,
    "contexts": ["build-test", "main-promotion-gate"]
  },
  "enforce_admins": true,
  "required_pull_request_reviews": {
    "required_approving_review_count": 0,
    "dismiss_stale_reviews": true,
    "require_code_owner_reviews": false,
    "require_last_push_approval": false
  },
  "restrictions": null,
  "required_linear_history": false,
  "allow_force_pushes": false,
  "allow_deletions": false,
  "required_conversation_resolution": true
}
JSON

Bootstrap note: .github/workflows/main-promotion-gate.yml must exist on main before GitHub can require the main-promotion-gate status check for future PRs. Merge the guard through the release flow once, then update main protection to require the new check.

GitHub’s settings UI can be used instead: Repository Settings -> Rules -> Rulesets, or Repository Settings -> Branches -> Branch protection rules.