Skip to content

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:

§RuleWhy it exists
1.1Read before claimingDon’t quote a file path / field / migration name from memory. Always read the source.
1.2Grep before stating existenceBefore 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.3ADR before codeAny phase touching >1 file or introducing a new pattern needs an ADR first. The ADR is the spec; the build executes against it.
1.4One phase at a time, with a verification gateDefault to one logical step per turn, with pnpm typecheck as a gate.
1.4.1Deploy after every backend commitForced by Phase 0124 (10.5h outage) + Phase 0125 (3 stacked SQL bugs). The deploy is the verification.
1.4.2Verify CI green after every pushgh run list --workflow=ci.yml --limit 1. Forced by 25 commits of red CI in May 2026.
1.5No parallel narrativeNever maintain prose about something the code already describes. Module lists, schema fields, vertical lists — query the source, not a markdown file.
1.6Single source of truthEvery fact has exactly one canonical location. If two sources disagree, the canonical one wins; update the other.
1.7Commit message disciplineCommit messages are the durable engineering record. Match the existing repo’s bar (recent commits = the gold standard).
1.9Service-note accumulationWhen you touch a service module, update or extend its docs/services/<module>.md note. Without this, the notes drift.
1.10Plain English by defaultDavid 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:

  1. Status — Proposed / Accepted / Superseded by ADR-NNNN / Deprecated.
  2. Context — current state, problem, constraints, forces in tension. Honest — not a justification for an already-made decision.
  3. Considered options — 2+ real alternatives. Single-option ADRs are a smell; even “do nothing” is worth naming.
  4. Decision — what we chose + rationale + criteria.
  5. Consequences — positive / negative / neutral after-effects.
  6. Implementation — filled during the build: migrations, files touched, endpoints, schema changes, implementing commits, affected roles.
  7. Verification — filled after merge: verified-by, verified-date, verification method, deviations from decision, follow-up ADRs needed.
  8. Related — supersedes / related / external references.
  9. 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:

  • ProposedAccepted when decision locks in.
  • AcceptedSuperseded by ADR-NNNN when a later ADR replaces it. Don’t delete — link.
  • AcceptedDeprecated when 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 NN

Don’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:

FactCanonical source
Verticalsprisma/migrations/*_seed_canonical_verticals_and_suppliers/migration.sql
Schemaprisma/schema.prisma
API endpointsapps/api/src/modules/**/*.controller.ts
Cron schedulesgrep @Cron apps/api/src
Phase historydocs/PHASES.md
Architectural decisionsdocs/adr/
Service implementation detailsdocs/services/<module>.md (index: docs/services/INDEX.md)
Definition of Donedocs/DOD.md
Prompt patterns (for David)docs/PROMPTING.md
Current statusgit 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 key comment.
  • 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


Canonical sources

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.