Conventions
Why this chapter exists
The earlier chapters describe what MerchOS is. This one describes how we work on it. If you’re a new engineer (or a returning one after months away), the conventions here are what keep the system from drifting.
Most of these started as informal habits and got written down after one incident or another forced them. Each one is here because the cost of NOT having it surfaced as a real outage, a wasted day, or a documentation drift that made onboarding harder.
The behavioral rules (CLAUDE.md §1)
CLAUDE.md §1 lists 10 immutable behavioral rules. Read in full at session start.
The rules in plain English:
| § | Rule | Why it exists |
|---|---|---|
| 1.1 | Read before claiming | Don’t quote a file path / field / migration name from memory. Always read the source. |
| 1.2 | Grep before stating existence | Before saying “we have a service that does X,” grep for it. If not found: say “I don’t find it,” don’t guess. (Caught 2026-05-19 when the handbook’s Chapter 1 missed ADR-0104 split-tender.) |
| 1.3 | ADR before code | Any phase touching >1 file or introducing a new pattern needs an ADR first. The ADR is the spec; the build executes against it. |
| 1.4 | One phase at a time, with a verification gate | Default to one logical step per turn, with pnpm typecheck as a gate. |
| 1.4.1 | Deploy after every backend commit | Forced by Phase 0124 (10.5h outage) + Phase 0125 (3 stacked SQL bugs). The deploy is the verification. |
| 1.4.2 | Verify CI green after every push | gh run list --workflow=ci.yml --limit 1. Forced by 25 commits of red CI in May 2026. |
| 1.5 | No parallel narrative | Never maintain prose about something the code already describes. Module lists, schema fields, vertical lists — query the source, not a markdown file. |
| 1.6 | Single source of truth | Every fact has exactly one canonical location. If two sources disagree, the canonical one wins; update the other. |
| 1.7 | Commit message discipline | Commit messages are the durable engineering record. Match the existing repo’s bar (recent commits = the gold standard). |
| 1.9 | Service-note accumulation | When you touch a service module, update or extend its docs/services/<module>.md note. Without this, the notes drift. |
| 1.10 | Plain English by default | David doesn’t ask twice for jargon-free communication. Default register: short sentences, concrete nouns, no architecture-speak. |
Why §1.5 + §1.6 together matter
These two rules are the anti-drift system. The 2026-04-25 audit (docs/audit/2026-04-25-*.md) found that MerchOS had ~12 markdown files re-stating the same vertical list with subtle drift between them. Single-source-of-truth + no-parallel-narrative codify the fix: there is one canonical place for each fact, and prose that paraphrases the canonical place rots over time.
This handbook itself respects §1.5: it synthesizes, it doesn’t duplicate. Every chapter ends with a “Canonical sources” footer linking to the underlying record. If a chapter and its source disagree, the source wins; the chapter gets updated.
Why §1.10 matters
David’s spent years working in jargon-heavy engineering cultures and explicitly doesn’t want that culture inside MerchOS. The rule is operational: if I write “parallel-write bridge” when “keep both tables in sync” works, I’m making the system harder to onboard onto.
What jargon looks like in this repo:
- “parallel-write bridge” → “keep both tables in sync”
- “cardinality fan-out” → “the join produced duplicate rows”
- “schema retargeting” → “point the FK at the new table”
- “post-flight invariant” → “check at the end that everything mapped”
Code is precise; explanations don’t have to be.
The ADR pattern
Every architectural decision lives in an Architecture Decision Record under docs/adr/. 117 ADRs at the time of writing.
Template: docs/adr/0000-template.md. Sections:
- Status — Proposed / Accepted / Superseded by ADR-NNNN / Deprecated.
- Context — current state, problem, constraints, forces in tension. Honest — not a justification for an already-made decision.
- Considered options — 2+ real alternatives. Single-option ADRs are a smell; even “do nothing” is worth naming.
- Decision — what we chose + rationale + criteria.
- Consequences — positive / negative / neutral after-effects.
- Implementation — filled during the build: migrations, files touched, endpoints, schema changes, implementing commits, affected roles.
- Verification — filled after merge: verified-by, verified-date, verification method, deviations from decision, follow-up ADRs needed.
- Related — supersedes / related / external references.
- Notes — open questions, future-revisit candidates, lessons.
Acceptance criterion: an ADR is Accepted when it has §Decision written + at least one reviewer approves. Implementation doesn’t have to start immediately; many ADRs sit Accepted for months before the phase that builds them (ADR-0104 was Accepted 2026-05-14, shipped 2026-05-19).
Post-merge: the §Verification section must be filled. Implementing commits, deviations, follow-ups. This is the durable trail of how the decision actually landed vs how it was originally specified — invaluable when revisiting a year later.
Status transitions:
Proposed→Acceptedwhen decision locks in.Accepted→Superseded by ADR-NNNNwhen a later ADR replaces it. Don’t delete — link.Accepted→Deprecatedwhen the thing it specified is being torn out without a replacement.
Naming: <NNNN>-short-kebab-name.md. Number is monotonic. Don’t reuse.
Phase naming
Phases are the durable record of what shipped + when. docs/PHASES.md is the ledger. Append-only, monotonic numbering. Never reuse a number; when a phase supersedes/replaces an earlier phase, link it explicitly.
Format per entry:
### Phase NNNN — Title (date range)[ADR-NNNN link if applicable]. Body prose.- **ADRs:** [ADR-NNNN](adr/NNNN-name.md), …- **Commits:** `abcd123` … `wxyz789`- **Status:** Shipped | Deferred | Superseded by Phase NNDon’t use informal shorthand (“Phase 0”, “Phase 2a”, “Phase A.5”, etc.). Always use the next available 4-digit number from PHASES.md. Phase numbers cross both ADRs + the ledger; ambiguity here causes referential drift.
The phase number is set at the START of the phase, not the end. If a phase ends up superseded mid-flight, the entry still reserves its number with a “Superseded by Phase NN” status. The history is more useful than a clean monotonic count.
Single source of truth — the canonical map
Per CLAUDE.md §1.6:
| Fact | Canonical source |
|---|---|
| Verticals | prisma/migrations/*_seed_canonical_verticals_and_suppliers/migration.sql |
| Schema | prisma/schema.prisma |
| API endpoints | apps/api/src/modules/**/*.controller.ts |
| Cron schedules | grep @Cron apps/api/src |
| Phase history | docs/PHASES.md |
| Architectural decisions | docs/adr/ |
| Service implementation details | docs/services/<module>.md (index: docs/services/INDEX.md) |
| Definition of Done | docs/DOD.md |
| Prompt patterns (for David) | docs/PROMPTING.md |
| Current status | git log --oneline -50 |
If two sources disagree, the canonical one wins. Update the other (or delete it).
Definition of Done
docs/DOD.md is the per-phase verification checklist. Every non-trivial phase satisfies it before declared “shipped.” Trivial work (single-file copy edits, doc typos) skips the ceremony.
Highlights: pnpm typecheck clean, integration tests pass where applicable, ADR §Verification filled, PHASES.md entry written, service note updated, commit message at the standard, push + verify deploy.
Working agreement (CLAUDE.md §9)
- Read the four root docs at session start — CLAUDE.md, STACK.md, DESIGN.md, DECISIONS.md (the ADR index).
- Ask before inventing. If something isn’t covered by an existing ADR, propose one and get alignment before building.
- Stub third-party integrations when API keys aren’t available (Placid, TapStitch, Gelato dev). Scaffold with a working mock + clear
// TODO: requires API keycomment. - Phase discipline. Don’t build phases out of order. If something is queued for a later phase in PHASES.md, note it and move on.
- Flag decisions. Surface unresolved questions clearly rather than assuming.
- Commit message quality matches the repo’s bar.
What’s next
- Reference index — links to every ADR, service note, runbook, phase.
Canonical sources
CLAUDE.md— all behavioral rules + working agreement + the single-source-of-truth map.docs/adr/0000-template.md— ADR template.docs/adr/0117-engineer-onboarding-docs-site.md— this handbook’s own ADR + maintenance contract.docs/DOD.md— Definition of Done.docs/PHASES.md— phase ledger.docs/services/INDEX.md— service-notes index.docs/audit/— historical audits, including the 2026-04-25 reset that codified the current discipline.
Triggers for update
Update this chapter if you:
- Add or remove a CLAUDE.md §1 behavioral rule.
- Change the ADR template fields or status-transition rules.
- Change the canonical-source map.
- Change the Definition of Done.
- Change the phase-naming convention.