Skip to content

First PR

Every change to a paper-board service repo goes through a pull request. We use trunk-based development: short-lived feature branches off main, squash-merged after CI and CodeRabbit pass. This page covers the full loop from branch creation to merge.

<type>/<short-slug>

Types mirror Conventional Commits: feat, fix, chore, docs, refactor, test.

Examples:

Terminal window
git checkout -b feat/add-session-timeout
git checkout -b fix/migrator-advisory-lock
git checkout -b docs/agents-api-reference

Keep slugs lowercase, hyphen-separated, under 40 characters.

Exception: paper-board/agent-manager (the coordination repo) allows direct push to main. All other repos require PRs.

Every commit message must follow Conventional Commits:

<type>(<scope>): <description>
[optional body]
[optional footer: Co-Authored-By, Closes #N]

The commitlint pre-commit hook AND the ci / pr-title GitHub Action both enforce one of these types in every commit subject / PR title:

TypeWhen to useCHANGELOG (release-please)
featNew feature or behaviorFeatures
fixBug fixBug Fixes
perfPerformance improvementPerformance Improvements
revertRevert of a previous commitReverts
refactorCode restructure with no behavior change(no entry)
testTest additions or changes(no entry)
docsDocumentation only(no entry)
choreMaintenance (deps, generated files)(no entry)
ciCI configuration(no entry)
buildBuild system, package metadata(no entry)

Breaking changes: add ! after the type (feat!:) and include a BREAKING CHANGE: footer.

PR titles must follow the hybrid Conventional Commits + Jira format <type>(<scope>)<!?>: <description> (PB-N) enforced by the ci / pr-title GitHub Action. The (PB-N) suffix is required; PRs without it fail CI and cannot merge.

Terminal window
gh pr create --title "feat(agents): add session timeout (PB-42)" \
--body "$(cat <<'EOF'
Adds configurable idle timeout to agent sessions.
Closes PB-42
EOF
)"

The Closes PB-42 trailer in the body is parsed by Atlassian Smart Commits and auto-transitions the Jira Story to Done when the PR is merged.

sequenceDiagram
autonumber
participant Dev as Developer
participant CI as GitHub Actions
participant CR as CodeRabbit
participant User as Maintainer
Dev->>CI: git push → PR opened
CI->>CI: lint + test + build
CI-->>Dev: checks settle (green or red)
CR->>CR: async review (semantic + structure)
CR-->>Dev: review posted
Dev->>Dev: triage findings: Major vs Minor/Nit
Dev->>Dev: fix Major findings in one new commit
Dev->>CI: git push (one commit per review round)
CI-->>Dev: green
CR-->>Dev: re-review posted
Dev->>CR: reply in-thread per finding with fix SHA
User->>User: merges PR (squash)

Poll until all checks settle:

Terminal window
gh pr checks <PR-number>

On failure: read the log, fix the root cause, push a new commit. Never use --no-verify to bypass pre-commit hooks. Never force-push.

CodeRabbit posts a structured review asynchronously. Use the dual-signal pattern to detect when it has finished:

Terminal window
# Signal 1: check the status rollup
gh pr view <PR-number> --json statusCheckRollup \
| jq '.statusCheckRollup[] | select(.context == "CodeRabbit")'
# Signal 2: check the reviews API (CR may post review before flipping the check)
gh api repos/paper-board/<repo>/pulls/<PR-number>/reviews \
| jq '.[] | select(.user.login == "coderabbitai[bot]")'

Wait until both signals show the review is complete. CR posts an issue-comment of “No actionable comments” when it finds nothing major.

CodeRabbit tags each finding as potential issue, nitpick, or informational.

TagAction
potential_issueMust fix before merge
nitpickMay defer — reply in-thread with a one-line reason
InformationalNo action required

Treat any finding tagged potential_issue as a Major finding — fix it.

Fix all Major findings in a single new commit (one commit per review round, not one per finding). Then reply to each finding in-thread (not at top-level):

@coderabbitai addressed in <SHA> — <short explanation>

Note: the mention is @coderabbitai (not @coderabbitai[bot]). The bot handle and the mention handle are different. For Greptile findings, use @greptileai.

The maintainer (not you) merges via squash merge. Do not run gh pr merge yourself unless explicitly authorized.

After merge, the Jira Story auto-transitions to Done and the branch is deleted.

These are never negotiable:

  • No git push --force or git push --force-with-lease.
  • No git commit --no-verify or git push --no-verify.
  • No gh pr merge unless explicitly authorized.
  • One commit per review round — do not squash previous rounds into history.
  • Never amend a commit that has been pushed to a PR branch.

commitlint rejects your message: the most common cause is a missing scope or wrong type. Check commitlint.config.js in the repo root for the allowed scopes.

CI fails on golangci-lint: run golangci-lint run ./... locally and fix the reported issues before pushing.

markdownlint fails: run markdownlint '**/*.md' locally. The most common issues are trailing spaces, missing blank lines around headings, and bare triple-backtick code blocks (always add a language hint).

CodeRabbit review is stuck on PENDING: wait up to 5 minutes; CR is eventually consistent. If it is still PENDING after 10 minutes, check the statusCheckRollup via the reviews API (Signal 2 above) — CR sometimes posts the review body before flipping the check status.

  • Glossary — precise definitions for every paperboard term.
  • System overview — understand the service topology before opening your first feature PR.