Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,10 @@ Thumbs.db
# for isolated subagent work; each is its own nested git repo, not
# content this repository should ever track.
.claude/worktrees/

# `pyflow record`'s own default output (TASK-045, `RecordingConfig.
# output_dir`, `src/pyflow/configuration/schema.py`) -- a user following
# README's own documented example from the repository root gets
# `checkpoints/` at the root with no `--output-dir` given; these are
# real binary run output, never content this repository should track.
checkpoints/
74 changes: 67 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,21 +131,35 @@ need to find it.

## Current Phase

Stage 8 — Recording & Playback -- not yet started (Stage 7, Rendering
Annotations, closed 2026-09-03 at its exit audit; Stage 8 was inserted
ahead of Better Numerics on 2026-09-07, which is why that stage is now
numbered 9 -- `docs/planning/roadmap.md`'s own "Fourth divergence"
entry). Its live status, generated from the roadmap rather than
restated here:
Stage 8 — Recording & Playback -- in progress: its first task
(TASK-045, periodic checkpointing via headless `pyflow record`) landed
2026-09-07; replay and playback (TASK-046/047) are not yet drafted
(Stage 7, Rendering Annotations, closed 2026-09-03 at its exit audit;
Stage 8 was inserted ahead of Better Numerics on 2026-09-07, which is
why that stage is now numbered 9 -- `docs/planning/roadmap.md`'s own
"Fourth divergence" entry). Its live status, generated from the roadmap
rather than restated here:
[Stage 8 in the status report](docs/planning/status.md#stage-8----recording--playback).

**This sentence said "not yet started" for the same reason a fourth
time here**: TASK-045 landed the same day this stage was inserted, and
the first draft of this update again left the word stale, exactly the
pattern the paragraph below already names for Stage 7. `make
check-status` did not catch it this time either, and for a related but
distinct reason -- Stage 8's own `Status as of` heading initially used
free text that satisfied `check_stages.py`'s looser "starts with
'Status as of'" match but not `generate_status_report.py`'s stricter
template, so the status line was invisible to the checker rather than
merely agreeing with a stale prose claim. See
`docs/planning/roadmap.md`'s own Stage 8 Status section for that fix.

**This sentence said "Stage 7 -- not yet started" for three days after
that stage's only task landed**, and `make check-status` did not catch
it: that check compares the stage this section *names* against the
roadmap's first stage not marked complete, and Stage 7 had no status
line at all, so both agreed on the number while the prose was wrong
about what had happened to it. Recorded because this section has now
gone stale at three consecutive stage boundaries.
gone stale at four consecutive stage boundaries.

**Stage 5 is the MVP** (`docs/implementation/mvp.md`): PyFlow solves
incompressible Navier-Stokes end to end, and the Lid-Driven Cavity
Expand Down Expand Up @@ -217,6 +231,52 @@ completion criteria (`docs/planning/roadmap.md`):
added 93 step definitions, 28% of the repository's whole step
vocabulary, which is evidence against its own claim rather than for
it.
**Stage 8 (Recording & Playback) is in progress, one of its three
planned pieces built.** TASK-045 (2026-09-07) adds `pyflow record`/
`pyflow resume`: `record` steps a simulation forward with no rendering
window at all, writing a self-contained checkpoint file at frame 0,
every `recording.checkpoint_interval` frames (100 by default), and at
the final frame -- a bounded, resumable seek index across the whole run,
not one file per frame; `resume` continues an existing recording from
its own last checkpoint, with no `--config` at all (the checkpoint
carries its own). Deterministic windowed replay and a playback path with
pause/variable speed (TASK-046/047) are not built yet -- neither command
renders anything. Try it against the Heat Diffusion demo:

```bash
uv run python -m pyflow record --config examples/golden-demos/heat_diffusion.yaml --max-frames 200
# recorded 3 checkpoint(s) to checkpoints, frames [0, 100, 200]
# wrote 3 checkpoint(s) to checkpoints
```

`checkpoints/checkpoint_00000200.pt` is a plain `torch.save`d file --
inspect one directly without any PyFlow-specific tooling:

```bash
uv run python -c "
import torch
c = torch.load('checkpoints/checkpoint_00000200.pt', weights_only=True)
print(c['frame_count'], list(c['fields']), c['fields']['tracer'].shape)
"
# 200 ['tracer'] torch.Size([192])
```

Now continue that same recording to frame 500, with nothing but the
checkpoint just written -- no config file, no `--config` flag:

```bash
uv run python -m pyflow resume --checkpoint checkpoints/checkpoint_00000200.pt --max-frames 500
# resumed from frame 200, recorded 3 checkpoint(s) to checkpoints, frames [300, 400, 500]
# wrote 3 checkpoint(s) to checkpoints
```

`resume` reproduces exactly the trajectory an uninterrupted `record`
straight to frame 500 would have (`tests/unit/
test_recording_determinism.py`'s own bit-identical, mutation-tested
claim) -- the prescribed state it doesn't checkpoint (mesh geometry, any
constant prescribed velocity) is deterministically re-derived from the
checkpoint's own embedded config rather than approximated.

Stage 9 (Better Numerics) follows Stage 8 (Recording & Playback, added
2026-09-07) -- better advection and diffusion
schemes, and with them the quantitative Rayleigh-Bénard comparison Stage
Expand Down
42 changes: 24 additions & 18 deletions docs/architecture/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,24 +36,30 @@ Grounded directly in `bootstrap.py`, `engine/simulation.py`,
`engine/collocated_field.py` -- read those files, not this note, for
anything beyond orientation.

**One of its four sections still carries a `Planned` subsection for a
mechanism that doesn't exist yet** (checkpointing simulation state,
Section 3), per the maintainer's direction that an unbuilt piece gets a
placeholder and a backlog anchor, not silence or a fabricated mechanism.

**Its anchor is no longer a task, and how that happened is the useful
part.** This paragraph used to say the subsection was "anchored to the
specific roadmap task that will build it (TASK-034)", with that task's
own roadmap entry carrying a matching note asking for `sequences.md` to
be updated in the same change. **TASK-034 landed on 2026-08-29 and
deliberately did not build checkpointing** -- Stage 5 Completion
Criterion 4 excludes it in as many words -- so the placeholder stayed
accurate while its anchor pointed at a closed task, and the same pass
left `sequences.md` with no sequence for `navier_stokes_step`, which is
what TASK-034 *did* build. A task anchor does not cover "the task landed
but did not build the thing" (`docs/practices.md`, "A checkable trigger
still needs somebody to check it"). The subsection now says plainly that
no task is assigned; whoever writes one re-reads it in the same change.
**None of its four sections carries a `Planned` subsection any longer**
(added 2026-09-07, TASK-045, Stage 8 (Recording & Playback)) -- the last
one, checkpointing simulation state in Section 3, is now a real sequence
grounded in `src/pyflow/checkpoint.py`/`recording.py`/`simulation_run.py`.
Deterministic windowed replay and the playback path (Stage 8's own other
two bullets, TASK-046/047) are still unbuilt, but Section 3 now says so
in its own closing paragraph rather than under a `Planned` heading, since
what recording alone built is real and belongs on the page as such.

**Section 3's anchor history is worth keeping, because it is why the
subsection existed to be finished at all.** It used to say the
subsection was "anchored to the specific roadmap task that will build it
(TASK-034)", with that task's own roadmap entry carrying a matching note
asking for `sequences.md` to be updated in the same change. **TASK-034
landed on 2026-08-29 and deliberately did not build checkpointing** --
Stage 5 Completion Criterion 4 excludes it in as many words -- so the
placeholder stayed accurate while its anchor pointed at a closed task,
and the same pass left `sequences.md` with no sequence for
`navier_stokes_step`, which is what TASK-034 *did* build. A task anchor
does not cover "the task landed but did not build the thing"
(`docs/practices.md`, "A checkable trigger still needs somebody to check
it"). Re-anchoring it to "unassigned" rather than deleting the note --
found by the same audit -- is what let TASK-045 find and close it for
real, eight days later.

**Re-read `sequences.md` end to end at every stage boundary, not only
when a task it names is touched** (added 2026-09-03, Stage 7 (Rendering
Expand Down
Loading
Loading