Conventions
These rules apply to every change, in every service and app. Reviewers check them, and several are enforced by CI.
English and Arabic ship together
Every user-facing string ships in English and Arabic in the same change, on every surface: the web app, the Android apps and the Apple apps. A string in one language only is an unfinished change.
| Surface | Where strings live |
|---|---|
| Web | web/src/i18n/en.json and web/src/i18n/ar.json, used through t("key") |
| Android | res/values/strings.xml and res/values-ar/strings.xml in each module |
| Apple | the .xcstrings catalogs (for example apps/apple/Player/Localizable.xcstrings), with both en and ar |
| User guide | the help pages in web/src/help/, in both languages |
RTL must work:
- Use logical layout (leading and trailing,
startandend, CSS logical properties such asmargin-inline-start) instead of left and right. - Mirror directional icons, keep media controls and timelines in their natural direction, and test screens in Arabic.
- Text that mixes Arabic and Latin (titles, file names, numbers) must render with correct bidi; do not build sentences by concatenating fragments.
Tests ship with the code
A change to app or service code updates its tests in the same change and the same push. The affected-tests gate blocks a push whose changed sources have no changed tests, or whose tests still look for strings or identifiers the code removed. A test that fails after your change is stale, not flaky: fix it in the same change. See Running the tests.
Roles are enforced by the API
Every role rule (who may manage libraries, users, settings, devices, and so on) is enforced server-side in the API, and each rule has a test that calls the endpoint as a role that must be refused and checks the refusal. Hiding a button in the web or native UI is not a rule; it is a convenience on top of one. A new endpoint without a role check and a role test is incomplete.
No secrets in code
- Never commit secrets, credentials, tokens, private keys or signing material, in code, tests, fixtures, docs or CI files.
- Configuration comes from the environment or from files outside the repository;
.env.exampleshows names and safe defaults only. - Tests generate their own throwaway keys and use obvious dummy values.
- Docs use generic examples: no personal home paths, private network addresses or machine names.
More in Security rules for plugins and contributors.
Code style
- Go:
gofmt,go vetandgolangci-lint. One module per service (api/,streaming/,cloud/, eachcmd/binary). - Python:
ruffandruff format,mypy --strict(including tests). - Web: eslint with zero warnings,
tsc --noEmit, prettier. - Logs: structured logging; never concatenate runtime data into a log message
(
make lint-logchecks this). - Migrations: append-only, idempotent, with Postgres and SQLite versions together
(
make lint-migrations). - Design tokens: colours, spacing and type come from
web/design-system/tokens/; generatedTokens.swiftandTokens.ktstay in sync (make test-tokens).
make format formats everything; make lint runs every linter as CI does.
Commits
Commit subjects follow a conventional style: a type, an optional scope, and a short summary in plain words.
feat(apps): sign-in request banner and notification
fix(android): the waiting banner keeps the screens' state
test(tvos): the code screen names the approve step
docs(spec): Epic 50 home broadcasting
chore(devportal): format, and the changelog line
Common types: feat, fix, test, docs, chore, refactor, perf, ci. Common
scopes are the service or app (api, streaming, pipeline, web, android,
apple, tvos, cloud, devportal, spec).
- Keep each commit focused; explain why in the body when it is not obvious.
- When a change rightly needs no test change, add the trailer
No-Test-Change: <why>. - Reference the story (for example
Story 22.8) or issue the change belongs to.
See Pull requests for the PR itself.