---
title: Development process
summary: "How the project is organised for contributors — human or agent: the roadmap as hand-off state, the effort ledger, commit and verification discipline, and the conventions that keep four interfaces consistent."
section: engineering
order: 8
level: 2
keywords: [process, roadmap, contributing, agents, conventions]
---
<!-- gyre docs: expanded Markdown of engineering/development-process.md (generated tables, verification numbers and chart data are written out). Base level of this page: L2 Practice; passages at another level are bracketed by 'begin level N' / 'end level N' comments and start with [LN Name]. Built from revision ceb1f4b0be. -->

<!-- begin level 1 (Essentials) -->
**[L1 Essentials] In brief: working on the code**

```bash
make bootstrap       # .venv with uv (handles CPUs without AVX2 and NVIDIA GPUs)
make test            # unit and integration tests        make test-service   # end-to-end service
make lint            # ruff                              make docs           # build the site into site/
make verify          # verification studies -> var/verification, rendered by the docs
make ci              # the whole pipeline with stage caching
```

Read `devlog/ROADMAP.md` first (decisions, milestones, status log, open items), then
`CLAUDE.md` (environment, conventions, definition of done). A numerical change is done when
the invariants, the affected verification studies and the derivative checks pass and the
documentation is updated. Gyre contains no optimizer and must not grow one.

<!-- end level 1 -->

## Hand-off state

Three files let anyone resume work cold:

* `devlog/ROADMAP.md` — mission, decision table, milestones, dated status log;
* `CLAUDE.md` — environment constraints, commands, conventions, definition of done;
* `devlog/time.jsonl` — the effort ledger (`python3 tools/worklog.py report`).

<!-- begin level 2 (Practice) -->
**[L2 Practice] Effort balance**

The project brief asked for effort split roughly 70 % core capability, 25 % testing and
verification, 10 % logistics and tooling (targets that sum to 105 %). The split is recorded
in an append-only ledger, part live and part entered afterwards. The measured split, the
rule by which it is compared with the target, and what limits its accuracy are on the
[effort ledger](effort-ledger.md) page, which is generated from the ledger.

<!-- end level 2 -->

## Definition of done for numerical changes

1. Unit invariants pass (free-stream preservation, conservation, entropy balance, split-argument
   blocks against a dense Jacobian, dot-product identity).
2. Affected verification studies pass (`gyre verify --only …`).
3. Derivatives are checked against finite differences.
4. Documentation is updated at the appropriate depth level and builds without broken links.

## Releasing a state

<!-- begin level 2 (Practice) -->
**[L2 Practice] Freeze, verify, record, deploy**

The documentation marks a verification record whose revision is neither the revision the
site was built from nor its parent. A release therefore has a fixed order:

1. **Freeze.** Tests green, code committed: revision X.
2. **Verify.** Run the whole suite on a clean checkout of X (a separate git worktree leaves
   the working tree free). Every record is then stamped X, not dirty.
3. **Record.** Commit the records as the direct child of X, together with prose that
   describes the run (its size and duration) if any.
4. **Deploy** from that commit: the served pages show each record as "parent revision of
   this build".

`make ci` does the same in one pipeline (tests, verification, documentation, image,
deployment on the CI port). Later commits that change only documentation text, evidence
records or bookkeeping leave the records valid: the pages then mark them "same code as this
build". A commit that changes code, tests, tools or the documentation theme makes every
record a mismatch until the suite has run again
([what makes a revision dirty](pipelines-and-provenance.md#provenance)).

<!-- end level 2 -->

## Conventions that prevent drift

<!-- begin level 2 (Practice) -->
**[L2 Practice] One path, generated references**

* New capability goes into the discipline or runner first, then REST, then MCP — never the
  other way round.
* Reference pages (case schema, presets, CLI, REST routes, MCP tools) are generated from the
  code at documentation build time (`gyre.docgen`).
* Verification pages render study files; text may interpret numbers but must not restate them
  by hand.
* Sample or synthetic data never appears outside the authoring guide, and is labelled there.

<!-- end level 2 -->

<!-- begin level 3 (Mechanics) -->
**[L3 Mechanics] Environment facts worth knowing**

* Tooling is project-local (`.tools/bin/uv`, `.venv/`); no system packages are required.
* CPUs without AVX2 need `jax < 0.12` (the bootstrap script detects this).
* Services bind `0.0.0.0`; port 8600 is the development deployment, 8601 the CI deployment.
* Scratch scripts must not shadow standard-library module names — a `bisect.py` next to a
  script breaks `import jax` through `random` → `bisect`.

<!-- end level 3 -->

<!-- begin level 4 (Theory) -->
**[L4 Theory] Lessons recorded during development**

* **A mesh generator bug looked like a solver bug.** Cosine clustering left zero parametric
  speed at the aerofoil trailing edge; the singular Jacobian there stalled Newton at a residual
  of $10^{-3}$ with a nonsensical entropy level. A mesh-quality check (minimum scaled Jacobian)
  found it immediately; such a check is now part of the mesh tests.
* **Measure the measurement — and test the explanation.** Several verification failures were
  caused by the study, not the code: an ill-conditioned finite difference, a step sweep that
  left the smooth regime, a mesh too coarse to represent the geometry. One plausible
  explanation ("time-integration error dominates at $p = 4$") was itself wrong and was caught
  only because the fix was verified to change the numbers — it did not.
* **Warm starts need their own convergence logic.** Re-solving from a converged neighbour first
  restarted pseudo-time continuation at low CFL, then chased a tolerance relative to its own
  tiny initial residual. Sessions now start in the Newton regime and converge relative to the
  cold-solve residual scale.
* **One definition of "which code is this".** Result files and the service reported the
  revision through one function; verification records and the documentation build through an
  older copy that called any uncommitted file dirty, the effort ledger and the records
  themselves included. A verification run on a clean checkout stamped every record after
  the first as dirty. The stamp now has a single source.
* **A strict failure policy finds what a permissive one hides.** When a linear solve that
  misses its tolerance became an error, the adjoint of the turbulent flat plate stopped: on a
  wall-resolved mesh block Jacobi alone had been returning derivatives from a solve that had
  not converged. The coarse multigrid level fixes it; the policy stays.
* **A threshold belongs to a regime.** The manufactured solution of the turbulence closure
  demanded the design rate and measured half an order less. The eddy viscosity had moved the
  mean flow into the regime where the laminar discretization converges at that rate too: the
  study now solves the laminar problem at the effective Reynolds number and requires the
  closure to change neither the order nor the error.
* **Reverse mode through a chunked loop stores the loop.** The matrix-free mesh deformation
  recomputes its weights chunk by chunk, but differentiating it in reverse mode kept the
  weights of every chunk, the dense matrix it was meant to avoid. Its transpose is written
  out by hand as a second chunked loop.
* **Compilation is the user-visible cost.** For small cases tens of seconds of JIT dwarf
  milliseconds of solve; this shaped sessions, the persistent cache, per-job device selection
  and test-suite structure.

<!-- end level 4 -->
