Working agreements
Source:
g2labs-in/billuu-docs@9f1dcd7·content/team/working-agreements.md· updated 2026-09-16
Where the rules live
CONTRIBUTING.md in brewlabs-billu-backend is the source for everything on this page. Read it before your first pull request. AGENTS.md in the same repo is the short version for coding agents, and README.md points at both.
The frontend repo has no CONTRIBUTING.md and no pull request template. It has one CI workflow and nothing else in .github/.
Repos and people
| Repo | What it is | Where the work happens |
|---|---|---|
g2labs-in/brewlabs-billu-backend | Express API, Sequelize, PostgreSQL | main |
g2labs-in/brewlabs-billu-frontend | React web app | new_design_implentation, because main is 18 commits behind it |
g2labs-in/billuu_landingPage | Next.js marketing site | tracked in the same issue flow |
Three people maintain the code.
| Person | Handle | Owns |
|---|---|---|
| Abhilash | @anonymousabhi13 | Backend and frontend, the API contract, the schema, the order path. The only repo admin |
| Aakash | @Aakash-chouksey | Infrastructure and CI, the build, the deploy, repo configuration |
| Anshul | @geekyyanshul | Full stack, working through the backlog |
Ask on the issue, not in a direct message, so the answer stays on the record.
Branches
Trunk based. main is production. Branch from main, never push to main, and keep unrelated changes out of the branch.
| Prefix | Use |
|---|---|
feat/<issue>-<slug> | new behaviour |
fix/<issue>-<slug> | a bug from an issue |
chore/<issue>-<slug> | tooling, dependencies, cleanup, no behaviour change |
hotfix/<issue>-<slug> | production is broken now, merge back the same day |
The issue number is part of the branch name. Real branches in the backend repo look like chore/16-point-at-the-queue and feat/8-client-order-id.
Commits
Conventional Commits, matching the history.
fix(orders): accept a client supplied order idfeat(inventory): record stock movements against the ledgerchore(ci): run lint before the smoke chainScope is the area of the codebase. One logical change per commit. The body explains why, and names the evidence for anything the message claims. Commit subjects in the real history carry the pull request number as well, for example chore(repo): untrack the env files and write down how we work here (#14).
Issues
Issues live in GitHub Issues, in the repo the work belongs to. AGENTS.md states this and names the label namespaces type/, area/, priority/ and size/.
Open an issue from a form. Blank issues are disabled in the backend repo. Each form asks for the fields that make the work checkable.
| Form | Label it applies | Required fields |
|---|---|---|
| Task | type/task | context, what to build, acceptance criteria, how to verify, depends on, area, size |
| Bug | type/bug | what happened, what should have happened, steps to reproduce, request, evidence, severity |
| Feature | type/feature | the problem, proposed behaviour, acceptance criteria, how we will verify it |
The area/ values on the forms are area/api, area/db, area/auth, area/orders, area/inventory and area/infra. The size/ values are size/s, size/m and size/l, where s is about a day and l is a week or more.
Before you pick something up, read the pinned queue issue. It is the ordered list of what is next across both repos, so it answers “frontend or backend first” without a meeting.
There is a stale document to be aware of. docs/agents/issue-tracker.md still describes the older process, one markdown file per issue under .scratch/<feature-slug>/ with a Status: line. AGENTS.md says the tracker is GitHub Issues now and that .scratch/ is history. The old files are still in the repo at .scratch/simplify/issues/.
Triage labels
The five canonical role labels live in docs/agents/triage-labels.md. In this tracker they keep their default names.
| Label | Meaning |
|---|---|
needs-triage | a maintainer has to evaluate the issue |
needs-info | waiting on the reporter |
ready-for-agent | fully specified, ready for an agent to pick up |
ready-for-human | needs a human to implement it |
wontfix | will not be actioned |
Pull requests
Fill in the template. .github/pull_request_template.md asks for the user visible change, the evidence, and the risk line. None of those are optional.
| Section | What goes in it |
|---|---|
Closes # | the issue the PR closes |
| What changes for the user | one short paragraph. What an outlet, an operator or the next engineer notices |
| How to verify | the commands you ran and what they printed. A claim without output is not evidence, and the template shows the shape it wants |
| Risk | checkboxes for schema changes, order and payment and identity changes, API contract changes, new environment variables, and tenant data access |
| Reviewer notes | where you want a second look, and what you already ruled out |
Keep the PR reviewable in one sitting. Squash merge, and the branch is deleted on merge.
Review and merge
One approval from someone who is not the author. Who must give it depends on the change.
| Change | Approval must come from |
|---|---|
| Schema, migrations, orders, payments, auth, permissions | Abhilash or Aakash. Anshul reviews these and asks questions, he does not clear them alone |
| Docs, CI, configuration | Aakash merges those himself |
| Everything else | any one of the three, other than the author |
CODEOWNERS requests the right reviewers automatically. It covers /src/architecture/, /src/utils/databaseExecutor.js, /migrations/, /src/modules/orders/, /models/ and /control_plane_models/ for both Abhilash and Aakash, and /.github/, /Dockerfile, /docker-compose.yml and /nginx.conf for Aakash alone.
Never merge your own PR into main, and never merge a red check to fix it after.
Nothing on the server enforces any of this. CONTRIBUTING.md records why, the org is on the free plan and the repo is private, so the repo has no branch protection, no required checks and no required reviews. CODEOWNERS requests reviewers and blocks nothing. A missing lock is not permission.
When main is broken, revert first and understand second. git revert <sha> on a branch, open the PR, merge it. Production deploys from main, so a red main is a deploy you cannot make.
CI gates
The backend workflow is brewlabs-billu-backend/.github/workflows/ci.yml. It runs on every push and every pull request, and it has three jobs.
The checks job, displayed as Syntax:
| Order | Step |
|---|---|
| 1 | actions/checkout@v4 |
| 2 | actions/setup-node@v4, Node 20, npm cache |
| 3 | npm ci |
| 4 | npm run check:syntax |
| 5 | npm run check:secrets, step name “No credentials in tracked files” |
The integration job, displayed as Bootstrap + API smoke, with a postgres:15-alpine service and the test environment variables set at job level:
| Order | Step |
|---|---|
| 1 | actions/checkout@v4 |
| 2 | actions/setup-node@v4, Node 20, npm cache |
| 3 | npm ci |
| 4 | npm run test:bootstrap |
| 5 | npm run test:smoke |
| 6 | npm run test:admin |
| 7 | npm run test:inventory |
| 8 | npm run test:accounting |
| 9 | npm run test:staff |
The e2e job, displayed as Frontend E2E. It declares needs: integration, so it waits for that job, and it runs only when the repository variable E2E_ENABLED is true.
| Order | Step |
|---|---|
| 1 | actions/checkout@v4 |
| 2 | actions/setup-node@v4, Node 20, npm cache |
| 3 | npm ci |
| 4 | checkout g2labs-in/brewlabs-billu-frontend into frontend/, using the E2E_FRONTEND_TOKEN secret |
| 5 | npm ci in frontend/ |
| 6 | npx playwright install --with-deps chromium in frontend/ |
| 7 | node src/scripts/init-database.js --wipe |
| 8 | start node app.js in the background, then poll http://127.0.0.1:8000/health until it answers |
| 9 | npm run test:e2e -- --project=chromium in frontend/ |
| 10 | upload frontend/playwright-report/ as an artifact, on failure only |
checks and integration start at the same time. e2e is the only job with a dependency.
The frontend workflow is brewlabs-billu-frontend/.github/workflows/ci.yml. One job, displayed as Build, on every push and pull request.
| Order | Step |
|---|---|
| 1 | actions/checkout@v4 |
| 2 | actions/setup-node@v4, Node 20, npm cache |
| 3 | npm ci |
| 4 | npm run build |
| 5 | npm run lint, step name “Lint” |
That file exists only on new_design_implentation. The frontend main has no workflow.
There is no deploy workflow on either main. The workspace checkouts on older branches still carry .github/workflows/deploy.yml. It runs lint, unit, integration and security tests on Node 18, pushes an image to ghcr.io and deploys over SSH. Do not read it as the current pipeline. Deployment is Dokploy, see below.
Checks you can run before you push
npm run check:syntax # parse every source filenpm run check:secrets # fail when a credential is tracked, or sits in a tracked filenpm run test:smoke # API smoke test, needs a local databaseIf you touched the schema, the order path, inventory or payments, run the smoke chain locally before you push. The chain is test:bootstrap, test:smoke, test:admin, test:inventory, test:accounting, test:staff, in that order. The five smoke scripts share scripts/lib/smoke-harness.js, which reads DATABASE_URL from the environment and refuses to run against anything that is not localhost. Export it first. npm run test:smoke passes against a local database in about 13 seconds.
Three groups of declared scripts cannot run today.
| Script | Why it fails |
|---|---|
npm run lint, npm run lint:fix | eslint is not a dependency. README.md lists npm run lint under Checks anyway |
npm run test:unit, test:integration, test:security, test:regression | they point at a tests/ directory that is not in the repo |
npm run test:automation, test:automation:fix, test:onboarding, test:db | they point at tests/automation/runTests.js. The file is at src/tests/automation/runTests.js |
CONTRIBUTING.md says this plainly. Until those scripts are repaired, the smoke chain is the gate.
One more path to know before you ship a schema change. CONTRIBUTING.md and CODEOWNERS both name migrations/ at the repo root. That directory is not tracked on main. The runner and the tenant migrations live in src/migrations/, and the numbered tenant migrations are in src/migrations/tenant/.
Deploy
Dokploy watches the backend repo on the billuu-app VM and injects the environment. Set variables in Dokploy, never in a tracked file. The production database is external, so a deploy does not move data.
If your change needs a migration, run it deliberately and say so in the pull request.
The infrastructure notes live in g2labs-in/linode-infra.
When you are stuck
Ask on the issue. Blocked for more than half a day, say so on the issue and take the next one.
Sources
brewlabs-billu-backend/CONTRIBUTING.mdbrewlabs-billu-backend/README.mdbrewlabs-billu-backend/AGENTS.mdbrewlabs-billu-backend/package.jsonbrewlabs-billu-backend/docs/agents/issue-tracker.mdbrewlabs-billu-backend/docs/agents/triage-labels.mdbrewlabs-billu-backend/docs/agents/domain.mdbrewlabs-billu-backend/.github/CODEOWNERSbrewlabs-billu-backend/.github/pull_request_template.mdbrewlabs-billu-backend/.github/ISSUE_TEMPLATE/config.ymlbrewlabs-billu-backend/.github/ISSUE_TEMPLATE/task.ymlbrewlabs-billu-backend/.github/ISSUE_TEMPLATE/bug_report.ymlbrewlabs-billu-backend/.github/ISSUE_TEMPLATE/feature_request.ymlbrewlabs-billu-backend/.github/workflows/ci.ymlbrewlabs-billu-backend/.github/workflows/deploy.ymlbrewlabs-billu-backend/src/migrations/,src/migrations/tenant/directory listingsbrewlabs-billu-frontend/.github/workflows/ci.ymlbrewlabs-billu-frontend/package.json- workspace root
docs/agents/issue-tracker.md - backend git history on
main,origin/mainandorigin/chore/16-point-at-the-queuefor branch, commit and PR numbering examples