Skip to content

Log format number, stored groups checked on read, and a linear Client.wait - #185

Merged
SimonHeybrock merged 5 commits into
mainfrom
log-format-version-and-small-fixes
Nov 12, 2026
Merged

SimonHeybrock merged 5 commits into
mainfrom
log-format-version-and-small-fixes

Conversation

@SimonHeybrock

@SimonHeybrock SimonHeybrock commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Step 0 of #184: three small changes that do not depend on any open design decision.

The log names its format. A log file starts with a header line, {"format": 1}, and a log refuses a file whose header is another format or missing. The number covers the events and the files of the store, since stored values are found through the log. Motivation: open_folder keeps a log and a store in a user's folder, while spec identity, file identity, labels, and the stored form of a request are still open (#80, #175, #177, #179). With the number, a change to any of them is a bump that refuses old folders with a message, where today it would misread them.

A file the log refuses is left as it was. This PR adds the mechanism only. Whether a later format reads older folders or drops them is not decided here, and ADR 0004 is unchanged. One consequence to be aware of: a log.jsonl written before this change has no header and is refused.

A stored group that lacks a required field is refused. A group was read back with model_construct, so a file written when the outputs class had fewer fields gave the added field as None, without an error, and a binding received it:

# persisted when BeamCentre had x, y; then z: float = Field(gt=0) was added under the same version
client.output(record, 'centre')   # was: BeamCentre(x=0.01, y=-0.02, z=None)
                                   # now: NotReadable: ... the store no longer holds it: .../centre.h5 holds no valid BeamCentre: field 'z' of BeamCentre is missing. ...

A request that references such an output fails its record with the same reason. The store reports such a value as one it no longer holds (LookupError), so callers that handle NotReadable handle this too. Limits: a group is still not validated again when read (a validator need not give the same value when run on its own result, which an existing test states), so a field whose type changed is not caught, and an added field that has a default reads as that default. The complete answer is spec identity (#80).

Client.wait asks only about records that changed. It asked the status of every record after every change. Measured with trivial requests: 1000 records 1.34 s → 0.46 s, 4000 records 14.6 s → 1.9 s (0.40 s and 1.7 s is the time the work takes without waiting).

Not included: refusing a pipeline parameter that no output depends on. PipelineBinding accepts such a blank on purpose (a test and the module docstring say so), so this needs a decision first.

🤖 Generated with Claude Code

SimonHeybrock and others added 3 commits November 12, 2026 13:51
A log file starts with a header such as {"format": 1}, written when the
file is created. A file whose first line is not the header of the format
this version reads is refused with an error naming the file, the format
found (or that it has none), and the format read. The number is bumped
on any change to the events or to the files of the store, since stored
values are found through the log. Files without a header are refused:
no migration.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
A group read back from the store was built without checks, so a file
written before the spec's outputs gained a required field read back with
that field as None. Reading such a file raises ValueError naming the
file, the type, and the missing field, and saying that the spec's
outputs changed since the value was written. client.output raises it,
and a request that references the output fails with it.

The group is still not validated again: a validator need not give the
same value when it runs on its own result. So only absent required
fields (and enum values) are caught, not a field whose type changed.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
wait asked the status of every record after every change in the feed,
so waiting for n records took time quadratic in n. It keeps the records
still pending and, after each change, asks only about those that are
pending and changed. 4000 trivial records: 14.6 s to 1.9 s until all
finished (1.7 s without wait).

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
SimonHeybrock and others added 2 commits November 12, 2026 14:11
Log(path) cut a last line short and wrote a header into a file with no
complete line before checking the header, so a refused file lost its last
line and a file without a newline was emptied and accepted. The header is
now checked first, and written only into an empty file or one holding the
beginning of the header. A header naming the format as true or 1.0 is
refused: only the integer is accepted.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…e no longer holds

FolderStore.read let hdf5.read's ValueError through, which callers take
for a misuse of select or a refused call. It now raises LookupError with
that message, the store protocol's case for a value it no longer holds:
a client's read raises NotReadable and a request that references the
output fails. The workspace keeps the store's message in NotReadable, so
both name the missing field.

A required field typed Any or object that held None is left out when
written, and is read back as None instead of being refused as missing.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
@SimonHeybrock
SimonHeybrock merged commit ebcd47f into main Nov 12, 2026
2 checks passed
@SimonHeybrock
SimonHeybrock deleted the log-format-version-and-small-fixes branch November 12, 2026 14:20
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