Skip to main content

Code Management

This page documents how code is managed in RoundTrip — from writing a commit to deploying to production. It covers branching strategy, commit conventions, pull request flow, ADO pipeline architecture, Cloudflare Pages deployment, and the self-hosted build agent.

Every developer on the team must follow these conventions. Deviating from them causes pipeline failures, merge conflicts, and in at least one case has overwritten the production frontend deployment.


Repositories

RepoContentsADO Path
RoundTripAPI.NET 10 API — all backend codetraxs/RoundTrip/RoundTripAPI
RoundTripWebReact 19 frontend — all frontend codetraxs/RoundTrip/RoundTripWeb
roundtrip-internalThis ops site (Docusaurus)traxs/RoundTrip/roundtrip-internal

Both application repos follow identical branching and pipeline conventions.


Branch Strategy

main

│ ← Production deployments only
│ ← Protected: requires PR + approval
│ ← Auto-deploys to production on merge

development

│ ← Integration branch for all feature work
│ ← Auto-deploys to dev environment on merge
│ ← Feature branches merge here first

feature/TRA-NNN-description
fix/TRA-NNN-description

│ ← One branch per Linear ticket
│ ← Created from development
│ ← PR → development when complete

Branch Naming

TypePatternExample
Featurefeature/TRA-NNN-descriptionfeature/TRA-280-free-text-parts
Bug fixfix/TRA-NNN-descriptionfix/TRA-286-hold-reason-values
Chorechore/descriptionchore/update-dependencies
Docsdocs/descriptiondocs/update-infrastructure-guide

Always create your branch from development — never from main:

git checkout development
git pull origin development
git checkout -B feature/TRA-NNN-description

Commit Conventions

RoundTrip uses Conventional Commits. Every commit message must follow this format:

type(scope): description

Examples:
feat(TRA-280): add free-text part logging on PWA
fix(TRA-286): correct hold reason enum values to match backend constraint
test(TRA-280): add unit tests for ad hoc part usage handler
chore: update Tailwind to v4.1
refactor(TRA-261): extract role from ITenantContext
TypeWhen to Use
featNew feature or behaviour
fixBug fix
testTests added or updated
choreDependencies, tooling, config — no behaviour change
refactorInternal restructure — no behaviour change
docsDocumentation only

One commit per Linear ticket. Do not squash multiple tickets into one commit or split one ticket across multiple commits.


Pull Request Flow

┌─────────────────────────┐
│ feature/TRA-NNN │
│ Write code + tests │
│ Commit per convention │
└────────────┬────────────┘
│ PR opened

┌─────────────────────────┐
│ development │◄── Integration branch
│ │
│ CI Build runs: │
│ • dotnet build │
│ • dotnet test │
│ • npm run build │
│ │
│ On success: │
│ • Deploy to dev env │
│ • Playwright E2E │
└────────────┬────────────┘
│ PR opened (manual)
│ Pete approves

┌─────────────────────────┐
│ main │◄── Production branch
│ │
│ CI Build runs: │
│ • dotnet build │
│ • dotnet test │
│ • npm run build │
│ │
│ On success: │
│ • Deploy to production │
└─────────────────────────┘

PR Rules

  • PRs are merged sequentially — never merge multiple feature branches simultaneously. This causes merge conflicts and pipeline race conditions.
  • Tests must pass before any PR is merged — do not merge a red build
  • Tests must be written in the same session as the feature — do not open a PR without tests
  • One PR per ticket — keep PRs small and focused
  • PR template must be completed — both repos have .azuredevops/pull_request_template.md

PR Template Checklist

The PR template covers:

  • Linear ticket linked
  • API handler pattern checklist (SignalR, CancellationToken, domain exceptions)
  • Security checklist (no secrets committed, tenant scoping, endpoint roles)
  • Database checklist (migrations handled, CHECK constraints)
  • Frontend architecture checklist (API calls in lib/api/, hooks in features/*/hooks/)
  • Tests written and passing

ADO Pipeline Architecture

Organization: traxs (Visual Studio Online)
Self-hosted agent: laptop in pool traxs-self-hosted

┌──────────────────────────────────────────────────────────────────┐
│ ADO PIPELINE OVERVIEW │
│ │
│ RoundTripAPI RoundTripWeb │
│ ───────────── ────────────── │
│ │
│ azure-pipelines.yml azure-pipelines.yml │
│ Trigger: push to main Trigger: push to main │
│ ┌─────────────────────┐ ┌──────────────────────────┐ │
│ │ 1. dotnet restore │ │ 1. npm ci │ │
│ │ 2. dotnet build │ │ 2. npm run build │ │
│ │ 3. dotnet test │ │ 3. wrangler pages deploy │ │
│ │ 4. dotnet publish │ │ --project roundtrip │ │
│ │ 5. Deploy to │ │ --branch main │ │
│ │ app-roundtrip │ └──────────────────────────┘ │
│ │ -production │ │
│ └─────────────────────┘ azure-pipelines-dev.yml │
│ Trigger: push to development │
│ azure-pipelines-dev.yml ┌──────────────────────────┐ │
│ Trigger: push to development │ 1. npm ci │ │
│ ┌─────────────────────┐ │ 2. npm run build │ │
│ │ 1. dotnet restore │ │ 3. wrangler pages deploy │ │
│ │ 2. dotnet build │ │ --project roundtrip │ │
│ │ 3. dotnet test │ │ -dev │ │
│ │ 4. dotnet publish │ │ --branch development │ ⚠ │
│ │ 5. Deploy to │ └──────────────────────────┘ │
│ │ app-roundtrip │ │
│ │ -dev │ azure-pipelines-internal.yml │
│ └─────────────────────┘ (ops.roundtrips.app) │
│ ┌──────────────────────────┐ │
│ │ 1. npm ci │ │
│ │ 2. npm run build │ │
│ │ 3. wrangler pages deploy │ │
│ │ --project roundtrip │ │
│ │ -internal │ │
│ │ --branch main │ │
│ └──────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘

Pipeline Variables

Both API pipelines require these ADO pipeline variables:

VariableUsed For
AZURE_WEBAPP_PUBLISH_PROFILEApp Service deployment credentials

Both Web pipelines require:

VariableUsed For
CLOUDFLARE_API_TOKENWrangler authentication
CLOUDFLARE_ACCOUNT_IDWrangler target account

Cloudflare Pages Deployment

:::danger Critical — Read Before Touching Pipeline Files The dev and production Cloudflare Pages deployments are distinguished only by the --branch flag passed to Wrangler. Using the wrong branch flag in the wrong pipeline overwrites the wrong environment. This has happened and took production down. This is the root cause of TRA-195. :::

Cloudflare Pages Projects:

roundtrip (PRODUCTION)
Production branch setting: main
Custom domain: roundtrips.app
Deployed by: azure-pipelines.yml in RoundTripWeb
Wrangler command: wrangler pages deploy build --project-name roundtrip --branch main

roundtrip-dev (DEVELOPMENT)
Production branch setting: development ← not "main"!
Custom domain: dev.roundtrips.app
Deployed by: azure-pipelines-dev.yml in RoundTripWeb
Wrangler command: wrangler pages deploy build --project-name roundtrip-dev --branch development

roundtrip-internal (OPS SITE)
Production branch setting: main
Custom domain: ops.roundtrips.app
Deployed by: azure-pipelines-internal.yml in roundtrip-internal
Protected by: Cloudflare Zero Trust Access (email OTP)

Cloudflare Pages Branch Mapping Rule

The roundtrip-dev project's production branch is set to development in the Cloudflare Pages project settings. This is intentional and must not be changed. It ensures that dev.roundtrips.app serves the development branch deployment.

CORRECT:
roundtrip-dev project → production branch: "development"
azure-pipelines-dev.yml → --branch development ✅

WRONG (causes production overwrite):
azure-pipelines-dev.yml → --branch main ❌
(deploys to roundtrip-dev project's "main" which bleeds into production)

If dev.roundtrips.app is serving stale content, check the Cloudflare Pages project settings branch mapping first before investigating the pipeline.


Self-Hosted Build Agent

All pipelines run on a self-hosted Azure DevOps agent named laptop in the traxs-self-hosted pool.

Why Self-Hosted?

  • Microsoft-hosted agents have a 60-minute timeout that is insufficient for some builds
  • Local .NET SDK and Node versions are guaranteed to match the development environment
  • No per-minute billing for pipeline minutes

Agent Requirements

The agent machine must have installed:

ToolRequired For
.NET 10 SDKAPI build and test
Node.js 20.xFrontend build
Wrangler CLICloudflare Pages deployment
Azure CLIApp Service deployment
DockerIntegration tests (Testcontainers)

Agent Health

If pipelines are failing with agent connectivity errors:

  1. Check the machine running the agent is on and not sleeping
  2. Open Azure DevOps → Organization Settings → Agent Pools → traxs-self-hosted → Agents
  3. Verify laptop shows as Online
  4. If offline, open a terminal on the agent machine and restart the agent process

Target Pipeline Structure (Planned)

The current pipeline structure is functional but missing some automation. This is the target state:

Current state:
✅ Production API pipeline (auto-deploy on main merge)
✅ Production Web pipeline (auto-deploy on main merge)
✅ Playwright pipeline (manual trigger only)
⏳ Dev API pipeline (created, may need verification)
⏳ Dev Web pipeline (created, may need verification)
❌ Tests not running automatically in any pipeline
❌ No ADO approval gate on production deployment
❌ Playwright not integrated into CI

Target state:
✅ Production API pipeline
✅ Production Web pipeline
✅ Dev API pipeline (auto-deploy on development merge)
✅ Dev Web pipeline (auto-deploy on development merge)
✅ Unit + integration tests run on every PR
✅ Playwright E2E runs against dev after dev deployment
✅ ADO approval gate required before production deployment
✅ Failed tests block merge

Tickets to create in Linear:

  • Add unit + integration test step to API pipelines
  • Add approval gate to production deployment
  • Integrate Playwright into dev pipeline post-deployment

Linear Issue Tracking

All work — features, bugs, chores, documentation — is tracked as tickets in Linear using the TRA-NNN numbering series.

ConventionDetail
Every branch references a ticketfeature/TRA-280-description
Every commit references a ticketfeat(TRA-280): description
Ticket updated before closingStatus, comments, decisions documented
Tests written before closingNever mark Done without tests
Architecture decisions documentedAdd a comment to the ticket with the decision and rationale

Making a Change — End to End

Here is the complete workflow for a typical feature ticket from start to merge:

# 1. Start from development
git checkout development
git pull origin development

# 2. Create feature branch
git checkout -B feature/TRA-280-free-text-parts

# 3. Write code + tests
# ... development work ...

# 4. Run tests locally before committing
dotnet test --filter "Category!=Integration"

# 5. Commit
git add .
git commit -m "feat(TRA-280): add free-text part logging on PWA"
git commit -m "test(TRA-280): add handler and domain tests for ad hoc part usage"

# 6. Push and open PR → development
git push -u origin feature/TRA-280-free-text-parts
# Open PR in ADO: feature/TRA-280-free-text-parts → development

# 7. CI builds and deploys to dev
# Verify on dev.roundtrips.app

# 8. Open PR: development → main
# Complete PR template checklist
# Pete approves

# 9. CI builds and deploys to production
# Verify on roundtrips.app

# 10. Mark ticket Done in Linear