# Code style and quality checks

> The quality runner's gate and report commands, what each one checks, and the conventions RosieOS code follows for constants, units, provenance, errors and tests.

URL: https://advancedmetalresearch.com/docs/contributing/code-style
Section: RosieOS docs / Contributing
Last updated: 2026-10-10

RosieOS has one quality runner, `quality/quality.mjs`, plus conventions that the code follows everywhere. Run the gate before you open a pull request:

```bash
npm ci --prefix coordination/v1       # installs the pinned npm tools the runner uses
node quality/quality.mjs check
```

Each step prints a status line: `passed`, `rejected`, `capability_unavailable` (the tool is missing) or `report_completed_not_qualified`. The runner exits 1 if any step was rejected or unavailable.

## Gate commands

These are the checks CI runs (`quality-pilot.yml`). They cover the TypeScript sources the runner owns, in `coordination/v1`, and they use the tools pinned in that directory's lockfile: Oxfmt, Oxlint, ast-grep and tsc.

| Command | What it does |
|---|---|
| `node quality/quality.mjs check` | The CPU-only CI gate: the runner's own tests, then `format-check`, `lint` and `typecheck`, then the unit and schema tests. The default command. |
| `node quality/quality.mjs format` | Rewrites formatting with Oxfmt. The only command that edits files. |
| `node quality/quality.mjs format-check` | Oxfmt in check mode. |
| `node quality/quality.mjs lint` | Oxlint, plus the tested ast-grep rules in `quality/sgconfig.yml`. |
| `node quality/quality.mjs typecheck` | Generates Worker types, then runs tsc. |

The runner uses explicit allowlists. It never formats evidence, CAD, generated files, vendor code or legacy trees.

## Report commands

These are not CI gates. Run them on the component you change. A missing tool or a nonzero tool exit still fails the run, and none of them applies fixes.

| Command | Scope | Tool |
|---|---|---|
| `node quality/quality.mjs python` | `weld_planner/v1` `python/`, `tests/`, `tools/` | Ruff 0.15.6 through `uvx` (format check and lint), with the project's Ruff settings |
| `node quality/quality.mjs go` | `gofmt` over tracked `steamdeck/real/v4` sources, then golangci-lint 2.13.2 with `quality/golangci.yml` | Go toolchain of that module |
| `node quality/quality.mjs rust` | `daemon/v1`: `cargo fmt --check`, then Clippy on all targets | Rust 1.95.0 through `rustup` |
| `node quality/quality.mjs workflows` | Every workflow | actionlint 1.7.12 |
| `node quality/quality.mjs shell` | `dev-stack.sh` | ShellCheck, installed by you |
| `node quality/quality.mjs cpp <compile-db-dir> <source>` | One `.cpp` file under `motion-server/joint-trajectory/v1/src`, with a real `compile_commands.json` | clang-format and clang-tidy, with `quality/clang-format.yml` and `quality/clang-tidy.yml` |

Clippy can exit 0 with warnings, so the Rust step reports `report_completed_not_qualified`, never `passed`. Read its output. The C++ configuration is opt-in and is not a repository-wide style.

## Conventions

These are visible throughout the code. Follow them in new code.

### Every constant carries its evidence

A tunable number states, in the comment above it, whether it was measured and where, or that it is unverified:

rt-core/tools/rtctl/hostcheck.go:

```go
// Measured repository dependency: host/ethercat-foundation.sh pins IgH 1.6.9.
const hostcheckIgHVersion = "1.6.9"

// Unverified inspection ceiling: 1000 ns CLOCK_MONOTONIC resolution.
const hostcheckTimerResolutionNS = 1000
```

If you change a constant, update its measurement or mark it unverified.

Configuration follows the same rule. Every numeric robot fact and every safety exception in a config file needs a `_source` ("cited: …") or `_unverified` ("unverified: …") sibling, and the compiler refuses one without it. See [provenance siblings](https://advancedmetalresearch.com/docs/reference/configuration#templates).

### Units are in the name

Fields and variables carry their unit: `cycle_ns`, `lease_ms`, `velocity_rad_s`, `acceleration_rad_s2`, `xyz_m`, `rpy_rad`, `following_error_counts_max`. The public rt-core API uses rad or m, rad/s or m/s, and ns on the host's `CLOCK_MONOTONIC`. When a unit changes at a boundary, name both sides.

### Docstrings state the contract

A doc comment says what the code guarantees and the failure it exists to prevent, not what the next line does:

rt-core/tools/rtctl/command.go:

```go
// DefaultRoot finds config/drives beside the build directory or above the
// working directory, preventing unrelated launch directories from selecting a
// different drive config tree.
```

### Fail closed, with a named reason

- Reject unknown fields, duplicate keys, trailing data and malformed values. Do not fall back to a default when an input is invalid.
- Refuse with a stable, named reason (`robot_description_mismatch`, `resource_unknown`) and a detail that says what to change.
- Never hide an error by dropping failure handling or weakening a test. A suppression comment must name the rule and the reason.

### Tests assert absolute units

Assert the contract in fixed units, such as "within 0.5 mm" or "under 250 ms". Do not compute a threshold from the thing under test (for example from a trajectory's own time step): a threshold that moves with the behaviour still passes when the behaviour collapses.

### Generated code is never edited

Change the contract in `rt-core/protocol/` and regenerate. See [generated code](https://advancedmetalresearch.com/docs/contributing/repo-layout#generated).

### Keep formatting separate

Use one formatter per language, and keep formatting-only commits apart from behaviour changes.

> [!NOTE] Linters and formatters prove none of these: installation, controller admission, collision freedom or physical execution. Do not describe a clean check as qualification.

## Sources

Written from these files in the RosieOS repository (https://github.com/advanced-metal-research/RosieOS):

- `quality/quality.mjs:1-110`
- `quality/sgconfig.yml`
- `quality/golangci.yml`
- `quality/clang-format.yml`
- `quality/clang-tidy.yml`
- `.github/workflows/quality-pilot.yml:1-33`
- `rt-core/tools/rtctl/hostcheck.go:21-27`
- `rt-core/tools/rtctl/command.go:141-144`
- `rt-core/tools/rtctl/robot_definition.go:66-160`
- `rt-core/tools/rtctl/recover_encoder.go:13-19`
- `rt-core/adapters/rosie/control/http.go:139-148`
- `rt-core/config/templates/machine.json; policy wording from CONTRIBUTING.md (README-type source, narrative only)`
