Skip to main content
Prerequisite: You should be familiar with basic Git operations and understand the Monorepo Architecture of our codebase.

Overview

Tuturuuu follows standardized conventions for Git commits and branch naming to improve collaboration, automate releases, and maintain a clean, navigable repository history. We use two main specifications:
  1. Conventional Commits for structured commit messages
  2. Conventional Branch for consistent branch naming
These conventions help automate our CI/CD workflows, generate changelogs, and make our development process more efficient.

Conventional Commits

What are Conventional Commits?

Conventional Commits is a specification for adding human and machine-readable meaning to commit messages. It provides a set of rules for creating an explicit commit history, making it easier to write automated tools on top of. The basic structure of a conventional commit is:
<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

Types

Tuturuuu uses the following commit types:
TypeDescriptionExample
featNew feature or enhancementfeat: add dark mode support
fixBug fixfix: prevent crash when user data is undefined
docsDocumentation changesdocs: update installation instructions
styleCode style changes (formatting, no code change)style: format code with prettier
refactorCode refactoringrefactor: simplify authentication logic
perfPerformance improvementsperf: optimize database queries
testAdd or fix teststest: add unit tests for auth middleware
buildChanges affecting build system or dependenciesbuild: update dependency to fix security issue
ciChanges to CI configuration files and scriptsci: simplify workflow branch filters
choreRoutine tasks, maintenancechore: update package.json metadata

Scopes

Scopes provide additional context about which part of the codebase is affected. In our monorepo structure, we often use package or app names as scopes:
feat(web): add new dashboard layout
fix(ui): correct button alignment in mobile view
chore(types): update supabase database types

Breaking Changes

Breaking changes must be indicated by adding a ! after the type/scope or by using a BREAKING CHANGE: footer:
feat(api)!: require authentication for all endpoints

BREAKING CHANGE: All API endpoints now require authentication tokens.

Examples

Here are some examples of conventional commits used in our codebase:
feat(web): add new calendar integration
fix(supabase): correct permission issue in auth policy
docs(readme): update development setup instructions
style: apply consistent formatting with prettier
refactor(utils): simplify date formatting functions
perf(queries): optimize workspace user loading
test(auth): add tests for token verification
build(deps): update Next.js to v14
ci(vercel): add automatic preview deployments
chore(release): publish packages

Conventional Branches

What are Conventional Branches?

Conventional Branch refers to a structured naming convention for Git branches that makes it easier to identify branches by type and purpose. The basic structure is:
<type>/<description>

Branch Types

Tuturuuu uses the following branch types:
TypeDescriptionExample
mainMain development branchmain
featureFor new featuresfeature/user-dashboard
featShort feature prefix also acceptedfeat/invite-link-permissions
fixShort bug-fix prefix also acceptedfix/invite-membership-check
bugfixFor bug fixesbugfix/login-error
hotfixFor urgent fixeshotfix/security-vulnerability
releaseFor preparing releasesrelease/v1.2.0
choreFor maintenance taskschore/update-dependencies
docsFor documentation-only workdocs/git-conventions-refresh
styleFor formatting or stylistic cleanupsstyle/biome-pass
refactorFor internal code restructuringrefactor/task-query-shell
perfFor performance-focused changesperf/workspace-user-search
dependabotAutomated dependency update branchesdependabot/npm_and_yarn/next
claudeAI-assisted scratch or experimental PRclaude/rate-limit-audit
Release Please bot branches are also accepted with the release-please--branches-- prefix. Do not create human-authored branches with that prefix.

Naming Rules

  1. Use lowercase alphanumeric characters and hyphens
  2. Keep names concise but descriptive
  3. Include ticket/issue numbers when applicable
  4. Avoid special characters (except hyphens)

Examples

feature/workspace-sharing
feat/public-course-sharing
fix/file-upload-error
bugfix/auth-vulnerability
release/v2.5.0
chore/update-react-18
docs/git-conventions-refresh
feature/issue-123-user-profile

Merge Commit Exception

Authored commits should follow Conventional Commits. The common exception is a Git-generated merge commit when syncing a long-lived branch, for example:
Merge branch 'main' into feat/studying-platform
Keep that merge summary when the goal is to preserve the branch’s merge history. Use a conventional commit message again for the next authored commit after the merge.

How These Conventions Improve Our Workflow

1. Automated Changelog Generation

Our conventional commits are used to automatically generate changelogs for releases. Different commit types are categorized accordingly:
# Changelog

## Features

- add dark mode support (#123)
- add calendar integration (#124)

## Bug Fixes

- prevent crash when user data is undefined (#125)

## Documentation

- update installation instructions (#126)

2. Semantic Versioning

Conventional commits help determine the next semantic version for packages:
  • fix: commits trigger a PATCH increment (1.0.0 → 1.0.1)
  • feat: commits trigger a MINOR increment (1.0.0 → 1.1.0)
  • Commits with BREAKING CHANGE trigger a MAJOR increment (1.0.0 → 2.0.0)

3. Automated Release PRs And Package Publishing

Release Please reads Conventional Commits from production, updates release-please-config.json packages through .release-please-manifest.json, and opens one combined release PR with package versions and changelogs. Package publish workflows do not generate versions; they publish only after a release-please version bump lands on production. Local manifests keep Tuturuuu workspace dependencies as workspace:*; npm release workflows rewrite the checked-out package manifest with scripts/ci/prepare-npm-package-manifest.js immediately before npm pack so published artifacts contain installable npm version ranges. Before that rewrite, scripts/ci/package-release-readiness.js waits for publishable Tuturuuu workspace dependency versions on npm, and publish jobs wait for their own version to become visible after npm publish. Dependency waits inspect the related release workflow for the same production SHA and fail fast when that workflow already failed. Workflow-published package manifests must carry provenance-compatible repository metadata for tutur3u/platform; otherwise npm rejects trusted publishes with E422. For example, release-types-package.yaml triggers from production changes to packages/types/package.json: When bringing the generated release PR branch back to main, run bun git-release-please from a clean main checkout. The helper fetches the latest release-please--branches--production branch, merges it without committing, syncs platform-version.txt into the platform badge constant and test expectation, runs bun ff, stages the resolved merge, then lets the normal commit hook run bun check and conditional bun check:mobile before the merge commit lands. If you are already inside a manual merge, run bun release:sync-platform-version to resolve the recurring TUTURUUU_PLATFORM_VERSION conflict before staging the merge.
on:
  push:
    branches: [production]
    paths:
      - "packages/types/package.json"
  # ...

jobs:
  check-version-bump:
    if: github.ref == 'refs/heads/production' && needs.check-ci.outputs.should_run == 'true'
    # ...

4. Better Code Reviews

With conventional commits and branches, it’s easier to understand the purpose of a pull request at a glance:
  • A PR from feature/user-dashboard with commits like feat: add user stats widget clearly indicates a new feature
  • A PR from bugfix/auth-issue with commits like fix: prevent token expiration error indicates a bug fix

5. Simplifying Navigation

Conventional branch names make it easier to navigate the repository history and find specific changes. For example:
# Find all feature branches
git branch --list "feature/*"

# Find branches related to authentication
git branch --list "*auth*"

Tools and Enforcement

We use a dedicated CI/CD check to enforce our Git conventions:

Branch Naming Check

We use a GitHub Action workflow to verify that branch names follow our convention:
name: Branch Name Check

on:
  push:
    branches-ignore:
      - main
      - production

jobs:
  check-branch-name:
    name: Check branch name
    runs-on: ubuntu-latest
    steps:
      - name: Check branch name
        run: |
          BRANCH_NAME=${GITHUB_REF#refs/heads/}
          if ! [[ $BRANCH_NAME =~ ^(feature|feat|fix|bugfix|hotfix|release|chore|docs|style|refactor|perf|dependabot|claude)/.+|^release-please--branches--.+ ]]; then
            echo "❌ Branch name '$BRANCH_NAME' doesn't follow the conventional branch format."
            echo "Branch name should be in format: type/description"
            echo "Allowed types: feature, feat, fix, bugfix, hotfix, release, chore, docs, style, refactor, perf, dependabot, claude"
            echo "Release Please bot branches are also allowed with the release-please--branches-- prefix."
            exit 1
          else
            echo "✅ Branch name follows convention: $BRANCH_NAME"
          fi

Best Practices

Writing Good Commit Messages

  1. Use the imperative mood (“add” not “added” or “adds”)
  2. Capitalize the first letter of the description
  3. Do not end the description with a period
  4. Keep the description under 72 characters
  5. Use the body to explain the what and why, not the how
Example of a well-formatted commit:
feat(auth): Add multi-factor authentication support

Implement TOTP-based multi-factor authentication to improve security.
The implementation follows the RFC 6238 standard.

Closes #123

Coordinating Commits In Shared Checkouts

When multiple agents or humans may commit in the same checkout, use the commit window before changing the staged set. The lock is advisory and lives under the ignored tmp/agent-coordination/ directory, so it protects the Git index and commit operation without becoming part of a commit. Claims default to 10 minutes and may only be 5-10 minutes, so claim only when ready to stage, inspect, and commit.
bun git-commit-window status
bun git-commit-window claim --owner "<agent/task>" --scope "type(scope): subject"
git add path/to/file-a path/to/file-b
git diff --cached --stat
git diff --cached --name-only
git commit -m "type(scope): subject"
bun git-commit-window release --token <token>
If another agent owns the window and it is appropriate to wait, use wait. The command sleeps until the active lock is released or expires, then claims the window before reporting that it is safe to proceed. The waiting period can be longer than the claim TTL, but the claimed window remains capped at 10 minutes:
bun git-commit-window wait --owner "<agent/task>" --scope "type(scope): subject"
Use --allow-staged only after inspecting existing staged files. The commit window does not grant ownership of files or permission to stage unrelated paths.

Branch Management

  1. Create branches from the latest main branch
  2. Keep branches focused on a single task or issue
  3. Regularly rebase long-lived branches on main to avoid merge conflicts
  4. Delete branches after they’ve been merged

Synchronizing Release Branches

Use the root command below when production should point at the same commit as main:
bun git-sync
The command fetches origin, creates a temporary detached worktree, refreshes main, and fast-forwards production to the current main commit from that temporary worktree before pushing those branches. It finishes by fetching again and verifying that local and remote main and production both resolve to the same commit. The checkout you started from is left untouched, so uncommitted work on another branch can keep running while the release refs are synchronized. If a branch that needs to move is already checked out in another worktree, Git will refuse to force-update that branch. Switch that worktree away from the branch or update it manually, then rerun bun git-sync. bun git-sync does not create commits for you. Commit all intended changes on main first; if production contains commits that are not already in main, reconcile the branch manually before rerunning the command. Use --only-branch to update one active sync branch while leaving the other untouched:
bun git-sync --only-branch production
main is still refreshed locally as the source commit, but only production is fast-forwarded and pushed. Use --only-branch main when you only want to pull and push main. The retired staging branch is no longer a supported --only-branch target. The Supabase staging environment still exists, but its workflow is driven from main. Use --no-push for local-only synchronization:
bun git-sync --no-push
bun git-sync --only-branch production --no-push
With --no-push, the command fetches, pulls with --ff-only, and fast-forwards the selected local branches, but it never pushes to origin. Final verification checks local refs only.

Pull Request Workflow

  1. Create a branch with the appropriate type based on the work
  2. Make commits using conventional commit messages
  3. Push the branch and create a pull request
  4. Use conventional commit style for the PR title
  5. After approval and merge, delete the branch

Conclusion

Following these Git conventions helps us maintain a clean, understandable repository history, automate release processes, and improve collaboration across the team. By standardizing both commit messages and branch names, we create a more efficient development workflow. For more information, refer to the official documentation for Conventional Commits and Conventional Branch.