Skip to content

feat(api): a result hands back how its solve terminated as one Record - #1738

Merged
FBumann merged 1 commit into
mainfrom
claude/result-record
Sep 25, 2026
Merged

FBumann merged 1 commit into
mainfrom
claude/result-record

Conversation

@FBumann

@FBumann FBumann commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

Prompt: In the records change, does each Result and Runs also have a .record property? … Add it

Note

The following content was generated by AI.

result.record returns the Record that save() writes to record.parquet: one row of the table a sweep keeps as sweep.record, under the same name one dimension wider. Before this, Result exposed only the separate fields, and the Record value existed only inside save(). save() now builds its row from the property, so the row has one home.

Details, mutation table and verification

Behaviour

  • result.record computes model_digest() on first use, as save() already did. The digest is cached, so later reads are free.
  • An answer read back with load_result or load_archive now keeps the run its record was stamped with. Before, spec_digest, solved_at and model_digest were carried over, but run was dropped. So load_archive(...).answer.record equals the row on disk.
  • save() still writes run as null. A plain save is not an archive, and Record documents run as "stamped when the archive is written and null until then".

Tests

  • test_a_result_hands_back_the_row_its_save_writes, parametrized over LP, MIP and INFEASIBLE. The property, the saved file and a load_result of that file must all give one row, and objective must be null exactly where nothing was reached.
  • test_an_archive_stamps_its_own_name_and_when_the_solve_returned also checks that an archived answer carries its run, and that re-saving it plainly writes run as null.

Mutation table (committed tree, restored with git checkout --, __pycache__ dropped on both sides)

guard result
load_result carries run (api.py:673, via tools/mutate.py) caught
save() writes run as null (result.py:791: ._replace(run=None) removed, by hand) caught

Gates: ruff check, ruff format --check, pyrefly check, the full suite (4033 passed, 319 skipped, 1 xfailed) and bench/test_harness.py (151 passed), in a uv environment. After #1730–#1734 merged, GitHub rebased this branch onto main; the resulting tree is identical to the one those gates ran on.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VgkRT9Kpb2iNvL6dpV8VER

@read-the-docs-community

read-the-docs-community Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

@read-the-docs-community

read-the-docs-community Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

@codspeed

codspeed Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Merging this PR will not alter performance

🆕 24 new benchmarks
⏩ 82 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
🆕 test_emit[commitment-s-specsolve-highs] N/A 2 MB N/A
🆕 test_emit[commitment-s-specsolve-lp] N/A 2.6 MB N/A
🆕 test_emit[dispatch-s-specsolve-highs] N/A 12.4 MB N/A
🆕 test_emit[dispatch-s-specsolve-lp] N/A 18.1 MB N/A
🆕 test_emit[fleet-s-specsolve-highs] N/A 21.9 MB N/A
🆕 test_emit[fleet-s-specsolve-lp] N/A 26.2 MB N/A
🆕 test_emit[nodal-s-specsolve-highs] N/A 4.9 MB N/A
🆕 test_emit[nodal-s-specsolve-lp] N/A 5.9 MB N/A
🆕 test_emit[profiled-s-specsolve-highs] N/A 21.1 MB N/A
🆕 test_emit[profiled-s-specsolve-lp] N/A 24.3 MB N/A
🆕 test_emit[sector-s-specsolve-highs] N/A 5.3 MB N/A
🆕 test_emit[sector-s-specsolve-lp] N/A 5.3 MB N/A
🆕 test_emit[storage-s-specsolve-highs] N/A 16.9 MB N/A
🆕 test_emit[storage-s-specsolve-lp] N/A 21.7 MB N/A
🆕 test_emit[transport-s-specsolve-highs] N/A 15.7 MB N/A
🆕 test_emit[transport-s-specsolve-lp] N/A 19.9 MB N/A
🆕 test_window[commitment-s-specsolve-highs] N/A 1.6 MB N/A
🆕 test_window[dispatch-s-specsolve-highs] N/A 11.3 MB N/A
🆕 test_window[fleet-s-specsolve-highs] N/A 17.4 MB N/A
🆕 test_window[nodal-s-specsolve-highs] N/A 4.7 MB N/A
... ... ... ... ...

ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.


Comparing claude/result-record (01fbfc2) with main (85ebcd2)2

Open in CodSpeed

Footnotes

  1. 82 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

  2. No successful run was found on main (8c783ca) during the generation of this report, so 85ebcd2 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩

@FBumann
FBumann added this pull request to stack #1735 September 24, 2026 20:45
@FBumann
FBumann force-pushed the claude/result-record branch from 2b7e25c to ae83e5d Compare September 24, 2026 21:06
Base automatically changed from claude/handoff to main September 25, 2026 06:07
FBumann added a commit that referenced this pull request Sep 25, 2026
…aved as record.parquet, so objective always means the number (#1731)

> **Prompt:** Also discuss some of the names, and if they have better
alternatives — approved from the proposed list: "1. Yes". A later
follow-up settled the attribute's number: "Runs.record or .records?" …
"agree" (to `.record`).

> [!NOTE]
> The following content was generated by AI.

`result.objective` is a float, but `runs.objective` was a table of how
each slice terminated, and `objective.parquet` held a `Record`. That
table is now `runs.record` and the file is `record.parquet`, matching
the `Record` type and the `RECORD_FILE` constant. A spilled sweep's
`objective/` directory is now `record/`.

The attribute is singular to follow the sweep rule "`Result`'s readers,
one dimension wider, under the same names", as `metrics` already does.
`result.record` (#1738) is one `Record`, and the sweep's `record` is a
table of one per slice, written as `record.parquet` beside
`metrics.parquet`.

This is a breaking change: an answer or archive saved before it is
refused with "holds no 'record.parquet'". The layout stamp stays at 0,
following its own rule ("zero while the layout is still moving").

This is stacked on the glossary PR.

<details><summary>Verification</summary>

`ruff check`, `ruff format --check`, `pyrefly check` and the full suite
(4030 passed, 319 skipped, 1 xfailed), in a `uv` environment because
`pixi` was not available. The tests that asserted the old names and file
listings now assert the new ones; no coverage was dropped.

The singular name was applied to this PR and every PR stacked on it by
rebuilding each commit's tree with one scripted rename, and each rebuilt
branch was re-run through the same gates. Every rebuilt commit differs
from its original only on lines naming the record, apart from one line
`ruff format` re-joined.

</details>

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01VgkRT9Kpb2iNvL6dpV8VER

Co-authored-by: Claude <noreply@anthropic.com>
@FBumann
FBumann force-pushed the claude/result-record branch from ae83e5d to 01fbfc2 Compare September 25, 2026 06:07
@FBumann
FBumann merged commit c6ffe74 into main Sep 25, 2026
14 checks passed
@FBumann
FBumann deleted the claude/result-record branch September 25, 2026 08:27
fluxopt-release-bot Bot added a commit that referenced this pull request Sep 25, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.0.1-alpha.355](v0.0.1-alpha.354...v0.0.1-alpha.355)
(2026-09-25)


### Features

* **api:** a result hands back how its solve terminated as one Record
([#1738](#1738))
([c6ffe74](c6ffe74))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: fluxopt-release-bot[bot] <307443024+fluxopt-release-bot[bot]@users.noreply.github.com>
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.

2 participants