oxfmt formats and oxlint lints, and both run in pnpm verify.
Naming
No speculative directories:
utils/, helpers/ and services/ exist because something real lives in them. A helper used by a second file moves to utils/<name>.util.ts; a single-use private helper stays where it is used.
One declaration per file
Every exported declaration lives in its own file, named for it. Where the kind is not a plain function, the file carries a kind suffix:index.tsis the only barrel name. A barrel declares nothing and names each symbol (export type { Token } from "./token.class"), neverexport *.- A folder is created for one artifact only when it has two or more non-test files.
- Scaffolded templates are the reference:
tools/scaffold/templates/packages/package/src. - A reviewer treats a file with two exported declarations, or a barrel that declares, as a finding. Changes to these conventions go through an ADR; there are no package-local exceptions.
Imports
The decorators preset turns
consistent-type-imports off because decorator metadata needs value imports (ADR 0005).
File contract
Every source and configuration file opens with a header before its imports. Plain JSON has no comments and is documented by its nearest README. Source files (.ts, .tsx), per ADR 0007:
pnpm-workspace.yaml, turbo.jsonc, lefthook.yml, preset configs) use the header the repository already carries, in their own comment syntax (#, //):
One formatter and YAML style
oxfmt is the only formatter, in the editor too: .vscode/settings.json sets oxc.oxc-vscode as the default formatter for TypeScript, JavaScript, JSON, JSONC, YAML, Markdown and CSS, formats on save, turns Prettier off and mirrors .editorconfig (LF, final newline, trimmed whitespace, ruler at 100). .vscode/extensions.json recommends the Oxc extension and marks Prettier and ESLint as unwanted.
Repo-owned YAML (lefthook.yml, .github/**, .mcp.yaml, .agents/*.yaml and the like) uses block style:
- Mappings are always block mappings. A flow mapping (
{ a: b }) is never written; the one exception is the empty{}(permissions: {}), which has no block form. - A flow sequence is allowed only for a short list of scalars that fits on one line (
branches: [main]). Anything longer or nested is a block sequence. - A trailing comment stays only when the line is still 100 characters or fewer; otherwise the comment moves to the line above.
pnpm-lock.yaml, pnpm-workspace.yaml (until STA-300), .specify/**, .agents/skills/** and .agents/reference/**. pnpm validate rejects a flow mapping and names the file and line.
TSDoc
Types carry the weight first: noany, generics have meaningful names, an exported type is its own documentation. Every exported symbol then gets TSDoc that says something the type does not.
Interfaces and types say what they represent and who produces and consumes them; each property says its meaning, unit or format, and who sets it. Boilerplate is a finding: a comment that restates the signature teaches readers to skip comments.
What must carry TSDoc
ADR 0007, amended on 2026-10-21, makes the block required on:- every exported symbol;
- every property and method signature of an exported interface or type literal;
- every public and protected member of an exported class (
privateand#members are internal); - every DI token made with
defineToken, whose block says what resolves to it and which package or app provides it.
@param name - meaning, never @param {type} name, and no typed @returns or @throws. TypeScript owns the types.
Worked example
@returns is kept here because the receipt’s meaning (first successful charge) is not in the type. Drop it when the
type already says everything.
Comments in code
Explain why, not what the next line says. Required before a workaround, a security-relevant check, a deliberate deviation and a non-obvious ordering. Forbidden: restating comments, commented-out code, and a TODO without an owner and a tracking reference.Tests
A test file’s header says what the suite proves. Eachit() title is a full sentence stating behaviour, not the function name.
Typed errors
A package never lets a rawError escape a public function. Every error it throws is a class under errors/ with:
- a stable, unique
code(a string literal in the class); - a message that names the offending thing (the token, driver or id involved);
- an optional
hintsaying what to do; - structural fields, never embedded secrets or values.
What enforces this
The planned
code-standards-steward splits, renames and moves files to meet the naming and barrel rules without changing behaviour; see the agent roster.