Skip to content

Insert Stage 9 (Solver & Run Integrity), renumber Stages 9-15 to 10-16 - #91

Merged
AdamClemens merged 1 commit into
mainfrom
docs/insert-stage-9-solver-run-integrity
Sep 12, 2026
Merged

AdamClemens merged 1 commit into
mainfrom
docs/insert-stage-9-solver-run-integrity

Conversation

@AdamClemens

Copy link
Copy Markdown
Owner

An end-to-end audit on 2026-09-12 found four defects behind a green make ci — 1209 tests, 99% coverage, all fifteen structural checks passing. This PR is the stage that owns fixing them, not the fixes.

What the audit found

Two of the four falsify use cases Stage 4 wrote down for itself:

  • Prescribe a wall's value or its gradient, or make a boundary periodic, and have the interior schemes honour it.
  • Solve a linear system, and be told when it did not converge instead of receiving a plausible wrong answer.
Defect Measured
F1 Solid walls are permeable to advection — FirstOrderUpwindAdvection takes a boundary face's transporting velocity from the owner cell, not the prescribed value Sealed box loses 14.27% of a purely advected tracer in 400 steps (diffusion zeroed); wall-normal velocity reaches 0.32 against a peak speed of 0.75
F2 A diverged run reports success — DivergenceDidNotConvergeError is swallowed by rendercanvas's log_exception pyflow exited cleanly, exit 0, after 22 of 40 frames failed. On glfw the raise kills the reschedule, so --max-frames hangs
F3 Nothing checks the configured timestep against the stability limit Refining the shipped cavity to 64×64 diverges at step 17, max|u| = 6.4e+04 against a lid speed of 1
F4 BoundaryFaceConfig.velocity is validated, documented, and read by no engine code Two docstrings claim it drives the divergence operator; it reaches no scheme at all

Why a new Stage rather than reopening Stage 4

Three placements were considered and all three are recorded in the roadmap rather than smoothed over. Reopening Stage 4 (the Stage 8 precedent) would drag a rendering-loop fix into a stage that predates the render loop by three stages. Folding into Better Numerics would mean drafting criteria against a Goal of improve accuracy without changing architecture — criteria that would not test what the work claims, which is the failure stage-specification.md exists to prevent.

Placed before Better Numerics by dependency, not preference: Stage 10's own Criterion 7 locates Rayleigh-Bénard onset between a heated lower wall and a cooled upper one. That measurement is not meaningful while those walls leak.

What's here

  • Stage 9 with seven completion criteria written before its first task, drafted from the Goal rather than from the tasks, each qualifier its own bullet with its own named check.
  • Fifth divergence entry + Was/Is now table; Stages 9–15 renumbered to 10–16 across the roadmap's 42 occurrences and 15 files outside it, each checked individually rather than substituted blindly.
  • docs/CHANGELOG-DESIGN.md deliberately untouched (append-only; it still says "3D at Stage 10" from 2026-08-18), as are the Was/Is now tables and the prose describing what each divergence found. What is renumbered is every live forward pointer.
  • Backlog §15 for what this stage deliberately defers, each with an *Unblock condition:*.

Two things found stale on the way past, fixed rather than flagged

  • The first divergence's "Stage 10 below therefore carries an explicit architectural caution" had been stale through three renumberings — it omitted the Stage's name, so there was nothing for a grep to check the number against. Now named, which is what makes the next sweep able to see it.
  • docs/practices.md's own "Stage 13 (Three Dimensions)" illustration, which that rule explicitly requires to track the live number.

One new standing rule

"When a test is found weak, ask why its fixture had to be that shape." The Stage 4 exit audit found the closed-domain advection-conservation scenario vacuous by mutation, correctly recorded it, and correctly added a periodic scenario to carry the criterion. What nobody asked was why the fixture needed every boundary cell's velocity to be exactly zero — it needed it because of F1. The weak test was a symptom sitting next to its own cause, and the audit that found the weakness stopped one question short.

Verification

make ci green in full locally. No source behaviour changes in this PR — the four fixes land as TASK-052/053/054 on their own branches.

🤖 Generated with Claude Code

An end-to-end audit on 2026-09-12 found four defects behind a green
`make ci` (1209 tests, 99% coverage, all fifteen structural checks
passing). Two of them falsify use cases Stage 4 wrote down for itself:

- Solid walls are permeable to advection. `FirstOrderUpwindAdvection`
  takes a boundary face's transporting velocity from the owner cell
  rather than the prescribed value, so a sealed box loses 14.27% of a
  purely advected tracer in 400 steps with diffusion zeroed.
- A diverged run reports success. `DivergenceDidNotConvergeError` raised
  inside the frame callback is swallowed by rendercanvas's own
  `log_exception`, so `pyflow run` prints "pyflow exited cleanly" and
  exits 0 after 22 of 40 frames failed. On glfw the same raise kills the
  reschedule, so a --max-frames run hangs instead.

And two more: nothing checks a configured timestep against the stability
limit (refining the shipped cavity to 64x64 diverges at step 17, silently),
and `BoundaryFaceConfig.velocity` is a validated, documented configuration
field no engine code reads -- two docstrings claim it drives the
divergence operator, and it reaches no scheme at all.

This commit is the stage, not the fixes. Placed before Better Numerics by
dependency rather than preference: Stage 10's own Criterion 7 locates
Rayleigh-Benard onset between heated walls, which is not meaningful while
those walls leak. Fifth divergence of the same shape as the first, third
and fourth; seven completion criteria written before the first task, per
the standing rule.

The renumbering sweep covers 42 occurrences in the roadmap and 15 files
outside it, each checked individually rather than substituted blindly.
`docs/CHANGELOG-DESIGN.md` is append-only and is deliberately untouched,
as are the Was/Is now tables and the prose around each describing what
that divergence found. What is renumbered is every live forward pointer.

Two things found stale on the way past, both fixed here rather than
flagged: the first divergence's "Stage 10 below therefore carries an
explicit architectural caution" had been stale through three
renumberings, because it omitted the Stage's name and so had nothing for
a grep to check against; and `docs/practices.md`'s own "Stage 13 (Three
Dimensions)" illustration, which that rule requires to track the live
number.

One new standing rule, `docs/practices.md`: when a test is found weak,
ask why its fixture had to be that shape. The Stage 4 exit audit found
the closed-domain advection-conservation scenario vacuous by mutation
and correctly replaced it -- but nobody asked why the fixture needed
every boundary cell's velocity to be exactly zero. It needed it because
of the defect above. The weak test was a symptom sitting next to its own
cause.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@AdamClemens
AdamClemens merged commit 81e3c57 into main Sep 12, 2026
2 checks passed
@AdamClemens
AdamClemens deleted the docs/insert-stage-9-solver-run-integrity branch September 12, 2026 22:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant