Skip to content

Git Workflow & Contribution Guide

This guide is the canonical reference for how code moves from a developer's laptop into production for the mr-mentor-backend repository (MAS-Mr-Mentor/mr-mentor-backend). It documents branch naming, commit-message conventions, the dated changelog discipline, the pull-request and @claude review flow, the CI gates that must pass, and exactly how a feature graduates from development to production via the automated Docker build + blue-green deploy. Read this before opening your first PR.

Status: documented from source on this branch. Everything below is derived from .github/workflows/*.yml, the changelog/ directory (202 dated entries + README.md), root roadmap/planning markdown, CI-CD.md, DOCKER_DEPLOYMENT.md, and package.json.


Overview

mr-mentor-backend is a single-repo, multi-product backend (Node 20 + Express + TypeScript + TypeORM + Redis/BullMQ + Socket.IO). Contribution is governed less by a written CONTRIBUTING.md (there is none) and more by a set of conventions baked into:

  • GitHub Actions — four workflows in .github/workflows/: build.yml, deploy-development.yml, deploy-production.yml, claude.yml.
  • A dated changelog conventionchangelog/YYYY-MM-DD-<sha>-<slug>.md, one file per significant commit, indexed by changelog/README.md.
  • Living roadmap docsROADMAP_COURSE_BATCH_TEST_CONNECTIONS.md, PHASE2_ROHAN_TASKS.md, WHATSAPP_LEADS_STRATEGY.md track in-flight work and ownership.

Who participates

Persona Responsibility in the flow
Developer / contributor Branches off development, commits, adds a changelog entry, opens a PR.
Reviewer Reviews the PR diff; may tag @claude to invoke the automated reviewer (claude.yml).
@claude bot The anthropics/claude-code-action GitHub App — runs on @claude mentions in issues, PR reviews, and comments.
Release / deploy owner Merges to main; this auto-triggers the production blue-green deploy.
claude-code-india GitHub account The only account with push rights to this repo (see Edge cases & gotchas).

Where it sits in the suite

This repo is the first service to deploy in the MAS suite (other services depend on its API). Its branches map directly to running environments: development → dev EC2 box, main → production (blue-green). See deployment-architecture and cicd-pipelines for the infrastructure detail this guide cross-references.


Key concepts & entities

This is a process guideline, not a code domain, so there are no TypeORM entities. The "entities" here are repository artifacts and their file paths.

Term Meaning Where it lives
Default integration branch development — where day-to-day feature branches merge first. git ref
Production branch main — merging here ships to prod. git ref
Staging branch staging — recognized by build.yml but not wired to a deploy workflow. git ref
Feature branch feature/<slug> or fix/<slug> (also feat/<slug>, ad-hoc name-slug). git ref
Changelog entry One markdown file per significant commit. changelog/YYYY-MM-DD-<sha>-<slug>.md
Changelog index Hand/auto-maintained table of all entries by year + category. changelog/README.md
Build workflow Builds + pushes the Docker image to GHCR. .github/workflows/build.yml
Dev deploy workflow Pulls the image onto the dev server via SSH + compose. .github/workflows/deploy-development.yml
Prod deploy workflow Blue-green zero-downtime deploy with rollback. .github/workflows/deploy-production.yml
Claude review workflow @claude automated reviewer/assistant. .github/workflows/claude.yml
Blue-green scripts Server-side deploy.sh, rollback.sh, health-check.sh. /home/ubuntu/blue-green-deployment/ on prod box
Health gate GET /api/health returning "success":true. src/routes/health.routes.ts, mounted in src/routes/index.ts

Architecture

The end-to-end pipeline: a push event triggers a single image build, and the build's completion (a workflow_run event) fans out to the environment-specific deploy workflow that matches the source branch.

flowchart TD
    Dev["Developer laptop"] -->|"git push feature branch"| GH["GitHub repo MAS-Mr-Mentor/mr-mentor-backend"]
    Dev -->|"open PR into development"| PR["Pull Request"]
    PR -->|"comment @claude"| CLAUDE["claude.yml (Claude Code Action)"]
    PR -->|"merge"| DEVBR["development branch"]
    DEVBR -->|"merge / promote"| MAINBR["main branch"]

    subgraph CI["GitHub Actions"]
        BUILD["build.yml: docker build + push to GHCR"]
        DEPDEV["deploy-development.yml"]
        DEPPROD["deploy-production.yml (blue-green)"]
    end

    DEVBR -->|"push event"| BUILD
    MAINBR -->|"push event"| BUILD
    BUILD -->|"image to GHCR ghcr.io/mas-mr-mentor/..."| GHCR["GHCR registry"]
    BUILD -->|"workflow_run success on development"| DEPDEV
    BUILD -->|"workflow_run success on main"| DEPPROD

    subgraph DEVSRV["Dev EC2 server"]
        DCOMPOSE["docker compose -f docker-compose.prod.yml up -d"]
    end
    subgraph PRODSRV["Prod EC2 server"]
        BG["deploy.sh blue/green container + nginx switch"]
        HC["health-check.sh + /api/health"]
    end

    DEPDEV -->|"scp .env from AWS Secrets Manager + SSH"| DCOMPOSE
    DEPPROD -->|"scp .env.original from AWS Secrets Manager + SSH"| BG
    BG --> HC
    GHCR -.->|"docker pull"| DCOMPOSE
    GHCR -.->|"docker pull"| BG

Data model

There is no database schema for this domain. The closest "data model" is the relationship between git refs, the build artifact, and the changelog records. The diagram below shows how those records relate.

erDiagram
    BRANCH ||--o{ COMMIT : "contains"
    COMMIT ||--o| CHANGELOG_ENTRY : "documented by"
    COMMIT ||--o{ PULL_REQUEST : "referenced by"
    PULL_REQUEST }o--|| BRANCH : "targets"
    COMMIT ||--|| DOCKER_IMAGE : "builds"
    DOCKER_IMAGE }o--|| ENVIRONMENT : "deploys to"
    CHANGELOG_ENTRY }o--|| CHANGELOG_INDEX : "listed in"

    BRANCH {
        string name "main development staging feature-*"
        string role "prod integration staging work"
    }
    COMMIT {
        string sha "7-char short sha"
        string message "conventional-ish"
    }
    CHANGELOG_ENTRY {
        string filename "YYYY-MM-DD-sha-slug.md"
        string date
        string description
        string breakingChanges
    }
    DOCKER_IMAGE {
        string tag "branch and branch-sha and latest"
        string registry "ghcr.io"
    }
    ENVIRONMENT {
        string name "development production"
        string secretId "mr-mentor-backend/{env}"
    }

Branch naming

Branch conventions, inferred from the merge entries in changelog/README.md (e.g. MAS-Mr-Mentor/feature/add-rating-filter, shubham21155102/feature/get-online-mentors, shubham21155102/fix/meeting-issues-and-improvements, shubham21155102/feat/jwt-auth-routes):

Branch Purpose Deploys to Notes
main Production-ready code. Production (blue-green) Push/merge auto-builds + deploys. Protected by convention — only claude-code-india can push.
development Default integration branch. Development server Open feature PRs here first. (Renamed from develop in commit 5c9bf2d.)
staging Pre-prod staging. none wired Recognized by build.yml (builds an image) but no deploy workflow consumes it. (inferred)
feature/<slug> New features. n/a e.g. feature/add-rating-filter, feature/get-online-mentors.
fix/<slug> Bug fixes. n/a e.g. fix/meeting-issues-and-improvements.
feat/<slug> Variant of feature prefix seen in early history. n/a e.g. feat/jwt-auth-routes.

Recommended convention going forward: feature/<kebab-slug> or fix/<kebab-slug>, branched from development. Forked-branch contributions (from personal accounts such as shubham21155102) are also part of the repo history.


Commit message style

Commit history shows a loosely-followed Conventional Commits style. There is no commit linter (the lint step was removed from CI — see commit ff3c3f7 "Remove linter step from CI/CD pipeline").

Observed prefixes (from changelog/ filenames and the index):

Prefix Use Example commit
feat: New feature feat: add rating filter to mentors API
fix: Bug fix fix: resolve Docker container naming conflict in deployment
chore: Tooling / runtime chore: Switch from Bun to Node.js runtime
refactor: Restructure without behavior change refactor: split CI/CD pipeline into separate build and deploy workflows
docs: Documentation docs: add cleanup API usage guide

Reality check: a large share of commits are plain imperative strings with no prefix (add slots, fix login error, release slot, db chnages). Some were even auto-generated by an LLM (commits literally titled 🧠 Generating commit message using Groq API...). Aim for the type: short imperative summary form, but the existing history is not strict.

Recommended format:

<type>: <imperative summary, lower-case, no trailing period>

<optional body: what & why, not how>

<optional footer: Breaking Changes / refs #issue>

The changelog discipline

This repo's most distinctive convention: every significant commit gets its own dated markdown file in changelog/. There are 202 such files plus changelog/README.md.

Filename format

changelog/YYYY-MM-DD-<short-sha>-<slug>.md

Examples: - 2025-09-25-aa58c78-feat-implement-jwt-authentication-routes.md - 2025-11-11-cfb28b0-feat-add-rating-filter-to-mentors-api.md - 2026-01-04-f956226-chore-switch-from-bun-to-node-js-runtime-for-better-socket-io-compatibility.md

The <slug> is the commit subject lower-cased with non-alphanumerics replaced by hyphens — i.e. it mirrors the commit message, which is why the commit message and the changelog slug are coupled. Merge commits get entries too (...-merge-pull-request-3-from-mas-mr-mentor-feature-add-rating-filter.md).

Entry body format

Each file follows this template (from 2025-09-25-aa58c78-feat-implement-jwt-authentication-routes.md):

# feat: Implement JWT authentication routes (aa58c78)

**Date:** 2025-09-25 18:32:41
**Commit:** aa58c789a8d308ba20e994eae22b6063c1d88811

## Files Changed
### Added
- `src/controllers/auth.controller.ts` - Authentication controller
### Modified
- `src/app.ts` - Updated app configuration

## Description
Implemented JWT-based authentication routes including login, register, and token validation.

## Breaking Changes
None

Required sections: a title with (short-sha), a **Date:** and full **Commit:** hash, a ## Files Changed block grouped by Added/Modified/Deleted, a ## Description, and a ## Breaking Changes (write None if not applicable).

The index: changelog/README.md

changelog/README.md aggregates all entries: - Per-year tables (### 2025, ### 2026) with | Date | Commit | Description | rows, the commit linking to its entry file. - Per-month archive lists. - A ## Categories section bucketing work into Features (feat), Bug Fixes (fix), Infrastructure, and Breaking Changes (with dates — e.g. "Runtime switched from Bun to Node.js (2026-01-04)", "Meeting duration changed to 15 minutes (2025-11-16)"). - A *Last updated:* footer.

When you add a changelog file, also add a row to the appropriate year table and month archive in README.md, and — if your change is breaking — record it under ### Breaking Changes.

No automated changelog generator script exists in the repo (no husky hook, no changelog npm script). The convention is maintained manually / by the contributor's own tooling. (inferred)


API surface

This domain has no application routes. The endpoints below are the ones the contribution/CI flow actually touches.

Method Path Auth/role Purpose
GET /api/health none Health gate the prod deploy curls for "success":true before/after switching nginx. Mounted via src/routes/index.ts (/api + health.routes).
/admin/queues Bull Board (UI) BullMQ queue monitoring UI; useful when verifying a deploy's workers came up. Mounted in src/routes/index.ts.
webhook GitHub push event GitHub Triggers build.yml on main, development, staging.
webhook GitHub workflow_run (completed) GitHub Triggers deploy-development.yml (on development) and deploy-production.yml (on main).
webhook GitHub issue_comment / pull_request_review / pull_request_review_comment / issues containing @claude GitHub App Triggers claude.yml.

User journeys

Journey 1: Standard feature contribution (dev path)

The everyday path: branch from development, commit with a changelog entry, open a PR, get it reviewed, merge — which auto-deploys to the dev server.

sequenceDiagram
    participant Dev as Developer
    participant Git as Local git
    participant GH as GitHub
    participant Rev as Reviewer
    participant Bot as claude.yml bot
    participant CI as build.yml
    participant DD as deploy-development.yml
    participant Srv as Dev EC2

    Dev->>Git: branch feature-slug off development
    Dev->>Git: commit code plus changelog entry file
    Dev->>GH: push feature branch and open PR into development
    Note over Dev,Rev: PR diff includes a new changelog file
    Rev->>GH: review the diff
    opt automated review
        Rev->>GH: comment with at-claude please review
        GH->>Bot: dispatch on at-claude mention
        Bot-->>GH: posts review comments
    end
    Rev->>GH: approve and merge into development
    GH->>CI: push event on development
    CI->>CI: docker build target prod
    CI-->>GH: push image to GHCR and emit workflow_run success
    GH->>DD: workflow_run completed on development
    DD->>DD: fetch env from AWS Secrets Manager mr-mentor-backend slash development
    DD->>Srv: scp docker-compose.prod.yml and .env then SSH
    Srv->>Srv: docker compose pull then up -d
    Srv-->>DD: container status and logs
    DD-->>GH: deployment summary

Journey 2: Graduating to production (blue-green deploy)

When development is verified, the changes are merged/promoted to main. A push to main builds a fresh image and triggers the zero-downtime blue-green deploy.

sequenceDiagram
    participant Rel as Release owner
    participant GH as GitHub
    participant CI as build.yml
    participant DP as deploy-production.yml
    participant Srv as Prod EC2
    participant NG as nginx

    Rel->>GH: merge development into main
    GH->>CI: push event on main
    CI->>CI: docker build and tag main and main-sha and latest
    CI-->>GH: push to GHCR and emit workflow_run success on main
    GH->>DP: workflow_run completed head_branch main conclusion success
    DP->>DP: fetch env from AWS Secrets Manager mr-mentor-backend slash production
    DP->>Srv: scp .env.original and SSH login to GHCR
    DP->>Srv: pre-deployment health check nginx postgres redis
    alt pre-check fails
        Srv-->>DP: error and exit
        DP-->>GH: deployment failed
    else pre-check passes
        DP->>Srv: run deploy.sh with image url
        Srv->>Srv: start standby container green if blue active
        Srv->>Srv: health-check.sh waits 30 to 40 seconds
        alt deploy.sh non-zero exit
            Srv->>Srv: dump container logs then rollback.sh
            DP-->>GH: failed and rolled back
        else healthy
            Srv->>NG: switch traffic to new container zero downtime
            DP->>Srv: post-deploy verify /api/health success true
            Srv-->>DP: db users count and redis PONG and nginx port match
            DP-->>GH: production deployment summary success
        end
    end

Journey 3: Adding the changelog entry alongside a commit

The changelog file is part of the same PR as the code. This is the micro-flow a contributor follows per significant change.

sequenceDiagram
    participant Dev as Developer
    participant Repo as Working tree
    participant Idx as changelog README.md

    Dev->>Repo: implement change and stage files
    Dev->>Repo: create changelog YYYY-MM-DD-sha-slug.md
    Note over Dev,Repo: title with short sha plus Date plus Commit hash
    Dev->>Repo: fill Files Changed and Description and Breaking Changes
    Dev->>Idx: add row to year table and month archive
    opt breaking change
        Dev->>Idx: add bullet under Breaking Changes with date
    end
    Dev->>Repo: commit everything together
    Note over Dev,Repo: commit slug matches changelog slug

Journey 4: Invoking the @claude automated reviewer

Any teammate can pull the Claude Code Action into a thread by mentioning @claude in an issue body/title, a PR review, or a comment.

sequenceDiagram
    participant User as Teammate
    participant GH as GitHub
    participant WF as claude.yml job
    participant Act as claude-code-action v1

    User->>GH: write at-claude in issue or PR review or comment
    GH->>WF: event fires issue_comment or pull_request_review etc
    WF->>WF: if condition checks body contains at-claude
    WF->>Act: run with CLAUDE_CODE_OAUTH_TOKEN model sonnet
    Act->>GH: checkout repo fetch-depth 1
    Act-->>GH: post review or changes with contents and PR and issues write

Journey 5: Manual deploy / rollback (break-glass)

Both deploy workflows expose workflow_dispatch, and the prod box keeps the previous container for instant rollback.

sequenceDiagram
    participant Op as Operator
    participant GH as GitHub Actions
    participant DP as deploy workflow
    participant Srv as Server

    alt manual redeploy
        Op->>GH: run workflow_dispatch with branch input
        GH->>DP: dispatch
        DP->>Srv: deploy image branch-sha
    else rollback after bad prod deploy
        Op->>Srv: SSH into blue-green-deployment dir
        Op->>Srv: run rollback.sh
        Srv->>Srv: switch nginx back to previous container
        Srv-->>Op: previous version live
    end

Background jobs & async

The workflows are event-chained rather than scheduled:

  • build.yml — trigger: push to main/development/staging, plus workflow_dispatch. Has concurrency: group docker-build, cancel-in-progress: true so a newer push cancels an in-flight build. Builds the prod Docker stage, pushes to GHCR with GHA layer cache (type=gha, mode=max).
  • deploy-development.yml — trigger: workflow_run of "Build and Push Docker Image" completing on development, plus workflow_dispatch. Runs only if conclusion == success.
  • deploy-production.yml — trigger: workflow_run completing on main with conclusion == success, plus workflow_dispatch. Uses GitHub Environment production.
  • claude.yml — trigger: @claude mentions across issue/PR events.

There is no schedule:/cron trigger and no queue involved in CI. (The app's own BullMQ workers — email, database, cleanup, kpi, resumeAnalysis — are runtime concerns, surfaced post-deploy at /admin/queues, not part of the git workflow.)


External integrations

Integration Used by Env / secret Failure / fallback
GHCR (GitHub Container Registry) build.yml push, both deploys pull GITHUB_TOKEN for push; GHCR_PAT (user MAS-intern) or GHCR_PULL_TOKEN (user mas-mr-mentor) for server-side pull login Dev workflow prefers long-lived GHCR_PAT so the server's docker login survives after the run; the ephemeral GITHUB_TOKEN fallback dies with the run and leaves dead creds.
AWS Secrets Manager both deploys build .env AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION (default ap-south-1); secret IDs mr-mentor-backend/development and mr-mentor-backend/production Workflow jq -e 'type == "object"' validates the secret is a JSON object before writing .env; aborts otherwise.
Dev/Prod EC2 over SSH appleboy/scp-action + appleboy/ssh-action *_SERVER_HOST, *_SERVER_USER, *_SSH_KEY, *_SSH_PORT (default 22) Prod pre-check verifies nginx/postgres/redis before deploying; aborts if any down.
Anthropic Claude Code Action claude.yml CLAUDE_CODE_OAUTH_TOKEN, model: claude-sonnet-4-6 Job's if: guard means it no-ops unless the body actually contains @claude.
nginx (prod) blue-green traffic switch /etc/nginx/sites-available/api.myanalyticsschool.com Post-deploy step warns (does not fail) on nginx/active-port mismatch.

Feature flags / config quirks

  • The original CI-CD.md describes a legacy PM2 + Bun deploy (ci-cd.yml, ecosystem.config.js, deploy.sh). That has been superseded by the Docker pipeline (refactor: split CI/CD pipeline into separate build and deploy workflows, commit 2c8d924) and the runtime switch from Bun to Node.js (f956226). Treat CI-CD.md as historical.
  • The lint and unit-test CI gates were removed from the pipeline (commits ff3c3f7 "Remove linter step", 5c96012 "remove unit test case step"). See the gotcha below.

Status lifecycles

A change flows through these states from branch to live in production.

stateDiagram-v2
    [*] --> Branched
    Branched --> Committed: commit code plus changelog
    Committed --> InReview: open PR into development
    InReview --> Changes: reviewer or at-claude requests changes
    Changes --> InReview: push fixes
    InReview --> MergedDev: approve and merge
    MergedDev --> BuildingDev: build.yml on development
    BuildingDev --> DeployedDev: deploy-development success
    BuildingDev --> BuildFailed: docker build fails
    DeployedDev --> PromotedMain: merge development into main
    PromotedMain --> BuildingProd: build.yml on main
    BuildingProd --> PreCheck: deploy-production pre-check
    PreCheck --> DeployingProd: nginx postgres redis healthy
    PreCheck --> Aborted: dependency down
    DeployingProd --> LiveProd: health check passes nginx switched
    DeployingProd --> RolledBack: deploy.sh fails so rollback.sh runs
    RolledBack --> DeployedDev: previous version restored
    LiveProd --> [*]
    BuildFailed --> Committed: fix and recommit
    Aborted --> [*]

Edge cases, limits & gotchas

  • Only claude-code-india can push. Per project memory, the only GitHub account with push access to MAS-Mr-Mentor/mr-mentor-backend is claude-code-india. Contributors from personal forks (e.g. shubham21155102) land via PRs; the actual merge/push that ships code is performed by that account.
  • Pushing to main ships to production immediately. There is no manual approval gate between the main build and the blue-green deploy beyond the GitHub Environment production (which may carry required reviewers — verify in repo settings). A merge to main auto-builds and auto-deploys. Be deliberate.
  • No CI test/lint gate. The lint step and unit-test step were explicitly removed from CI. npm test (Bun), npm run test:unit, npm run test:integration, and npm run lint exist in package.json but are not enforced by the pipeline — run them locally before you push. The only enforced gate is "the Docker prod image builds successfully," plus the prod pre/post health checks.
  • The deploy is gated on the build, by branch. deploy-development.yml only fires on workflow_run for branch development; deploy-production.yml only for main with head_branch == 'main' and conclusion == 'success'. A failed build never triggers a deploy.
  • staging builds but never deploys. build.yml will produce a staging image, but no deploy workflow consumes it. (inferred)
  • Concurrency cancels in-flight builds. build.yml uses a single docker-build concurrency group with cancel-in-progress: true — rapid successive pushes mean only the latest build survives.
  • Image tags. Every build pushes :<branch> and :<branch>-<shortsha>; main additionally gets :latest. The dev deploy pulls :<branch>; the prod deploy pulls :<branch>-<shortsha> (reconstructed from head_sha).
  • GHCR login longevity. Use the long-lived GHCR_PAT/GHCR_PULL_TOKEN paths; the ephemeral GITHUB_TOKEN leaves dead credentials in ~/.docker/config.json on the server after the run.
  • Secrets never live in the repo. .env is built at deploy time from AWS Secrets Manager (mr-mentor-backend/{development,production}). Add new env vars there (see the add-env-var skill), not in code. TypeORM synchronize: true means no migration files — schema changes apply on boot, so a bad entity change ships to the DB on deploy.
  • Changelog is manual. No generator script is checked in; if you skip the changelog entry, nothing fails CI, but you break the convention and the changelog/README.md index drifts.
  • Commit/branch hygiene is loose historically. Don't model new work on old typo commits (db chnages, add socker) or LLM-generated subjects; follow the recommended type: summary form.
  • @claude only acts on an explicit mention. The workflow's if: requires the literal string @claude in the relevant body/title; otherwise the job is a no-op.