HOCO B2B
Ways of working

Git workflow

Branch model, naming, merge policy, Conventional Commits with the scopes of both repositories, hooks, short-lived branches and Prisma migrations — the same rules in hoco and hoco-mobile.

Branch model

BranchCreated fromMerges intoLifetimeDeployed toMerged by
master——PermanentProduction, from tags vX.Y.ZRepository owner only
devmaster (once)Cut into release/*Permanent; the default branchStaging, on every mergePR author, after the review rules
feature/<TASK-ID>-<desc>devdev (squash)≤ 3 working daysPreview, where one existsAuthor
fix/<TASK-ID or issue>-<desc>dev, or release/x.y.z during acceptanceWhere it came from (squash)≤ 1 working day as a target—Author
chore/<desc>, docs/<desc>devdev (squash)Short—Author
release/x.y.zdevmaster (merge commit), then back into devUntil x.y.z is in productionPre-production, on tags vX.Y.Z-rc.NRepository owner
hotfix/x.y.zThe production tag on mastermaster (merge commit), then back into dev and any open release/*HoursProductionRepository owner

The repository owner is Anis for hoco and Imad for hoco-mobile. Dependabot opens its own dependabot/… branches into dev.

Naming

Format: <type>/<TASK-ID>-<short-description>.

  • type is one of feature, fix, chore, docs, release, hotfix.
  • The task id is written exactly as in the plan (uppercase: SRV-14, MDA-05). A bug without a task uses its issue number (fix/57-…).
  • The description is 2 to 5 lowercase ASCII words in kebab-case; the whole name stays under 50 characters.
  • release/ and hotfix/ carry the version only.
Branch nameVerdict
feature/SRV-14-crm-moduleOK
feature/MDA-05-visit-flow (hoco-mobile)OK
fix/SRV-16-cancel-releases-stockOK
fix/57-checkout-roundingOK — issue #57, no task
docs/plan-week-updateOK
release/1.0.0, hotfix/1.0.1OK
anis-work, feature/crm, SRV-14, feature/SRV-14_CRM_ModuleRejected — no type, no task id, no description, or wrong case

Merge policy

  • Into dev: squash merge. The PR title becomes the commit subject on dev, so the PR title must itself be a valid Conventional Commit with the task id. The body keeps Closes #n.
  • release/x.y.z into master: merge commit (never squash), then the repository owner tags the merge commit with an annotated tag (git tag -a v1.0.0 -m "HOCO 1.0.0") and opens the back-merge PR master → dev, merged with a merge commit.
  • Fixes during acceptance branch from release/x.y.z and merge back into it. The repository owner merges release/x.y.z into dev after each -rc.N tag, so dev never lacks a fix.
  • hotfix/x.y.z: merge commit into master, tag, then back into dev (and into an open release/*, if any).
  • Syncing your own branch: rebase on origin/dev, then git push --force-with-lease. Never rebase dev, master, release/*, or a branch someone else has pushed to. Never merge dev into a feature branch, except when two people share it.
  • No cherry-picks between dev and a release branch, except a hotfix that must also reach an open release branch (git cherry-pick -x, to keep the reference).

A feature, end to end:

git switch dev && git pull --ff-only
git switch -c feature/SRV-14-crm-module
# … work, commit in small steps …
git fetch origin && git rebase origin/dev        # at least daily
git push -u origin feature/SRV-14-crm-module     # first push
git push --force-with-lease                      # after a rebase
gh pr create --base dev --title "feat(server): reseller lifecycle transitions [SRV-14]"

Conventional Commits

Format: <type>(<scope>): <subject> [TASK-ID]

  • The subject is imperative, starts lowercase, has no final period. The whole header is at most 100 characters (commitlint header-max-length).
  • The task id goes at the end in brackets: [SRV-16], or [SRV-16, SRV-17] for two tasks. It is omitted only for chores and bugs without a task.
  • The body says why when it is not obvious. Footers: Closes #42, Refs #57, BREAKING CHANGE: ….
  • Breaking changes add ! after the scope. For the API contract, a breaking change also follows the deprecation process — it is never shipped in place.
TypeUse for
featA new capability for a user or a consumer of the API
fixA bug fix
perfA performance improvement with no behaviour change
refactorRestructuring with no behaviour change
testTests only
docsDocumentation only (docs pages, the plan, comments)
styleFormatting only
buildBuild system, Dockerfile, dependencies of the build
ciGitHub Actions workflows
choreMaintenance: dependency bumps, tooling, regenerated code
revertReverting an earlier commit

Scopes in hoco:

ScopeCovers
serverapps/server (api and worker)
dashboardapps/dashboard (back-office)
webapps/web (reseller web)
docsapps/docs — the site and its pages
planapps/docs/content/plan/tasks.json — always in its own commit
contractspackages/contracts, and API reference pages aligned with it
dbpackages/db — schema, migrations, seeds
api-clientpackages/api-client
uipackages/ui
sharedpackages/shared
envpackages/env, .env.example
configpackages/config, root tooling (Biome, Turborepo, TypeScript, lefthook)
dockerdocker/, docker-compose.yml
depsDependency bumps (Dependabot uses it)
releaseVersion bumps and changelogs

Scopes in hoco-mobile:

ScopeCovers
resellerapps/reseller
delegateapps/delegate
corepackages/hoco_core
apipackages/hoco_api (regenerations)
uipackages/hoco_ui
l10npackages/hoco_l10n
offlinepackages/hoco_offline
mapspackages/hoco_maps
catalogpackages/hoco_catalog
workspaceRoot pubspec.yaml, Melos scripts, analysis options
android, iosPlatform folders, signing, store configuration
deps, releaseAs in hoco

commitlint enforces the format and the type. The scope list is enforced in review: since dev only receives squash commits, the PR title is what lands, and the reviewer renames an unknown scope before merging.

feat(contracts): add delegate visit check-in operations [SRV-03]
feat(server): implement cart.checkout with stock and credit checks [SRV-15]
fix(server): release reserved stock when an order is cancelled [SRV-16]
feat(server): after-sales claims and returns with automatic restock [SRV-20]
feat(db): add Tour, Visit and VisitReport models [SRV-21]
feat(dashboard): orders-to-confirm queue [DSH-06]
feat(dashboard): returns and after-sales pages [DSH-13]
docs(plan): mark SRV-03 done, re-estimate SRV-21
chore(deps): bump next to 16.4.3
refactor(env)!: rename S3_BUCKET to S3_BUCKET_PUBLIC

BREAKING CHANGE: update your local .env from .env.example.
feat(delegate): offline visit check-in with GPS tolerance [MDA-05]
feat(reseller): live order status and return requests [MRA-10]
fix(core): retry once after refreshing an expired session [MOB-02]
chore(api): regenerate client from contract 1.4.0 [MOB-03]

Commit hygiene

  • One concern per commit. Schema and contract commits come before the code that uses them.
  • Stage explicitly (git add <paths>) and read git diff --staged before committing.
  • Generated files are committed only where listed: the OpenAPI document (apps/docs/public/openapi/hoco-v1.json), Prisma migrations, the team-task pages (pnpm docs:generate), hoco_api and the build_runner outputs in hoco-mobile, lockfiles. Never committed: .env, the Prisma client (prisma/generated), .next, dist, APK/AAB files, keystores, service-account files.
  • Lockfiles change only with a dependency change; CI installs with --frozen-lockfile.
  • No commented-out code, no stray console.log or print, no TODO without an issue number (TODO(#57): …).
  • Line endings are LF everywhere (.gitattributes: * text=auto eol=lf). On Windows, also set git config --global core.autocrlf input.

Hooks

Hookhoco (lefthook, installed by pnpm install)hoco-mobile (lefthook, set up in MOB-01)
pre-commitBiome check --write on staged files, fixes re-staged; gitleaks protect --staged when gitleaks is installeddart format on staged Dart files; gitleaks
commit-msgcommitlint (@commitlint/config-conventional, header ≤ 100)The same commitlint configuration, so both repositories reject the same messages
  • Never commit with --no-verify. A wrong hook is fixed in a PR.
  • Hooks missing? Run pnpm exec lefthook install. Check with a commit message like wip: it must be rejected.
  • Install gitleaks locally (Scoop: scoop install gitleaks). The hook skips it when absent, but CI scans every PR anyway.

Keep branches short

  • Three working days at most from first commit to merge. A PR without activity for two working days is mentioned in the stand-up.
  • A task longer than two days ships as several PRs under the same task id, each leaving dev deployable. Typical order: contract → schema and migration → endpoints → back-office or app screens.
  • Unfinished work lands hidden: operations not yet called, back-office routes not in the navigation, app screens behind a release flag (feature flags). Anything visible but unfinished is behind a flag before the next release cut.
  • Rebase on dev at least once a day, and right away when a contract or migration PR lands.

Database migrations in branches

The schema is multi-file Prisma in packages/db/prisma/schema/; migrations live in packages/db/prisma/migrations/.

Change the schema on your branch.

Create one migration named after the task and topic, in snake_case:

pnpm --filter @hoco/db exec prisma migrate dev --name srv14_crm_core
# review or edit the SQL before applying (renames, backfills):
pnpm --filter @hoco/db exec prisma migrate dev --create-only --name srv14_crm_core

Commit the schema and the migration folder together: feat(db): reseller lifecycle tables [SRV-14], with the label migration on the PR.

Keep one migration per PR. While iterating, delete your own unmerged migration folders, reset your local database (prisma migrate reset) and create a single migration again before opening the PR.

If dev gained a migration while your branch was open: rebase, delete your migration folder, prisma migrate reset, then recreate your migration so it sits after theirs.

Rules:

  • A migration that has reached dev is never edited. Fix forward with a new one.
  • Expand, then contract. The new api container applies migrations when it starts, while the previous version may still be serving (architecture). A migration must therefore work with the code currently in production: add nullable columns and new tables first; rename, drop or set NOT NULL in a later release, after a backfill.
  • Destructive statements (DROP, ALTER COLUMN … TYPE, SET NOT NULL, a unique index on a populated table) are read line by line by the reviewer, and the PR explains the plan.
  • Reference data (wilayas, roles and permissions, settings defaults, reference lists) changes through idempotent seeds, never through manual SQL on an environment.
  • pnpm db:push is for a throwaway local database only, never for anything that will be committed.

The same rules in hoco-mobile

Topichocohoco-mobile
Branch modelAs aboveIdentical: master, dev, feature/MDA-05-visit-flow, release/1.0.0, hotfix/1.0.1
Task idsSRV, DSH, WEB, OPS, PRJ, and QA / LCH for server-side workMOB, MRA, MDA, and QA / LCH for mobile work
Merge policySquash into dev; merge commits into masterSame
TagsvX.Y.Z (server, back-office and web share one version); contracts-vX.Y.Z for the contractvX.Y.Z — both apps share one version line; an app that did not change is not uploaded
HooksBiome, gitleaks, commitlintdart format, gitleaks, commitlint
Generated codeOpenAPI document, team-task pageshoco_api, build_runner outputs (freezed, json_serializable, drift) — committed, CI fails if regeneration changes them
Schema migrationsPrisma, expand then contractdrift in hoco_offline: every change bumps schemaVersion with a tested migration step. The outbox must survive every app update — a lost outbox is lost field data
Build numbers—versionName from the tag, versionCode = CI run number (always increasing)

On this page