Skip to content

Conventions: language, commits, comments

This content is not available in your language yet.

This page is the contract for who reads what, in which language, and for the shape of a commit. It is written in English on purpose: from generation 6 on, everything addressed to a developer is English, and everything addressed to a user is Persian (decision 3.3 in plan-v6/README.md). The rest of docs/reference is migrated page by page (phase 9); until then a Persian page is not “wrong”, it is “not yet migrated”.

Text Reader Language Kept by
Code comments, JSDoc, eslint-disable reasons developer English darzsaz/no-persian-comment (ESLint), comment-pattern (Stylelint)
Commit subject and body developer English, Conventional Commits commitlint on commit-msg and in CI
docs/reference/*, CLAUDE.md developer Persian (see below) scripts/doc-claims.mjs keeps its claims true
CONTRIBUTING.md, this page newcomer English review
docs/journal/* — new entries developer English; old entries stay as written
Developer tooling output (scripts/*, guards, CI logs) developer English for new and touched scripts review
Identifiers, file names, CSS class names developer English (as today)
UI strings, print sheets, CLI --help, error messages user Persian source, English via the i18n catalog lingui/no-unlocalized-strings, i18n:check
User guide (apps/site/.../guide), landing page user Persian, with an English site
Plan folders (plan-v*/) repository owner Persian

Why English for developer text, with numbers: on 2026-09-15 the repository had 5,938 Persian comment lines out of 8,636, and 292 of the last 324 commit subjects were Persian. The code itself — identifiers, types, library APIs, the technical vocabulary of every dependency — is English, so every Persian comment switches script mid-line, is unreadable to any tool or contributor without Persian, and cannot be searched together with the code it explains. The user never sees a comment or a commit; the user does see the UI, which stays Persian.

Why the reference went the other way in phase 9.1: it had been drifting into two half-written copies, one Persian and one English covering eleven of twenty pages. Nearly half of it (darz-format.md) is generated from the schema’s Persian field descriptions, so translating it means editing the data model’s user-facing strings; the rest is read by the one person who maintains this repository, who reads Persian. One source beats two, and the audience-facing documents — the user guide and the landing page — stay in both languages. rules.en.md is the exception that proves it: it is generated from the en-US catalog and cannot drift.

A comment may quote Persian as a value: a UI string, a catalog name, a test fixture. The quote is the signal — «…», "…", '…' or `…`:

// The label is «کابینت ۲» in fa-IR and "Cabinet 2" in en-US; the number is user data.

Persian prose outside quotes is a Persian comment and is reported.

Format (Conventional Commits 1.0):

<type>(<scope>): <subject>
<body>
<footer>
  • type: feat, fix, refactor, perf, test, docs, build, ci, chore, style, revert — the set of @commitlint/config-conventional.
  • scope: one of core, geometry, i18n, report, ui, web, cli, site, docs, plan, tooling, deps, deps-dev, release. Several scopes are separated by a comma (fix(core,report): …); a change that touches everything has no scope.
  • subject: imperative, lower case, no trailing period, Latin script, header ≤ 100 characters. It says what changed, not which files.
  • body: what and why. If a road was tried and abandoned, it is written here — for the person who walks the same road three months later it is worth more than the fix. Lines ≤ 100 characters. Persian UI text may be quoted.
  • footer: BREAKING CHANGE: … when a file format, CLI flag or public API changes; Co-Authored-By: …; issue references.

Examples:

fix(geometry): open hinged fronts outward, not into the carcass
The swing sign was inverted in front-instances.ts since 52470cf: the axis was
written by hand as +1/-1 with no convention. The axis is now the cross product
of the up vector and the hinge edge; the angle comes from the hinge catalog
entry instead of a fixed 100°.
Tried first: flipping the sign only. That fixed base units and broke lift-up
doors, which rotate around a horizontal axis — hence the cross product.
chore(deps-dev): bump vitest from 4.1.11 to 4.1.12

Merge commits (Merge branch …) and reverts written by git are accepted as they are.

  • commit-msg hook: pnpm exec commitlint --edit (lefthook). Bypass for one commit only with LEFTHOOK=0, and only for a fixup that is squashed before push.
  • CI: every commit of the push or pull request that was made after the contract commit (d4ae1c4) is linted (commits job in ci.yml; the list comes from scripts/commit-range.mjs, by committer date, merges skipped). The pre-contract history — generation 5 and the wave-6 worktree commits merged in phase 0 — stays as written.
  • scripts/test/commitlint.test.mjs runs the configuration against sample messages so a config change that silently accepts Persian subjects turns the guard tests red.
  • Explain why, and what was tried and did not work — not what the code does (rule 4 of CLAUDE.md). Most comments in this repository are the minutes of a mistake.
  • English, see above. Existing Persian comments are counted in a shrink-only baseline (scripts/lint-baseline.json for ESLint, stylelint-suppressions.json for CSS): a new Persian comment in a file that has none fails lint; fixing one and not updating the baseline also fails (pnpm lint:baseline --update, node scripts/css-lint.mjs --update). The baseline only goes down; phase 8.8 of plan-v6 takes it to zero.
  • eslint-disable needs a reason after -- (require-description); the reason is English too.
  • CONTRIBUTING.md — the rules of the repository and who keeps them.
  • i18n.md — messages, catalogs, and why the core returns messages, not strings.
  • testing.md — the guard-of-guards tests under scripts/test/.
  • traps.md — every known trap and the guard that catches it.