Repository navigation
Conversation
…mmand load_manifest reads a YAML file of solve and solve_over arguments, one entry per run, and the specsolve command (the cli extra) checks, solves and lists those runs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GS5ucQvLdLgdP1vaFy7kUS
…e solve Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GS5ucQvLdLgdP1vaFy7kUS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GS5ucQvLdLgdP1vaFy7kUS
Merging this PR will degrade performance by 14.76%
Warning Please fix the performance issues or acknowledge them on CodSpeed. Performance Changes
Tip Investigate this regression by commenting Comparing Footnotes
|
Documentation build overview
59 files changed ·
|
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GS5ucQvLdLgdP1vaFy7kUS
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GS5ucQvLdLgdP1vaFy7kUS
… them A run reads exactly the locations it lists, so what a run reads is on its own line, and a windowed run inherits no table its spec does not declare. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GS5ucQvLdLgdP1vaFy7kUS
> **Prompt:** Do the release PR now > [!NOTE] > The following content was generated by AI. Renames `## Upcoming version` to `## 0.6.0 (2026-10-05)`, names the three breaks, and keeps the four PR lines. Merging releases 0.6.0, a minor release because the on-disk layout is 3. <details><summary>Checks</summary> - `tools.changelog check`: "releases 0.6.0 on merge". `pixi run lint`: pass. - The breaks are checked against `main` (331a839): `LAYOUT = 3`, no `dim_position` in `catalog.parquet`, and `owned.parquet` under `answer/windows/` only with `keep_windows=True`. - Not in it: #1845, which is still open. </details> 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01NMkmEe6LFizRyp5cVPjk3R --------- Co-authored-by: Claude <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GS5ucQvLdLgdP1vaFy7kUS
Note The following content was generated by AI.
start: previous # each slice starts from the slice before it
start: {archive: runs/monday.zip} # an archive anywhere, read with load_archive
start: {run: monday} # a run of this manifest, read from <archive>/monday
start: {primal: [data/start/], constraint_basis: [data/basis/]} # tables, read like sourcesRules
Generated by Claude Code |
# Conflicts: # CHANGELOG.md # docs/about/architecture.md # docs/reference/api.md # src/specsolve/__init__.py # src/specsolve/types.py # tests/test_architecture.py
…ince #1884 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TktdzJKXuU8fc2p1JNVTqn
Note
The following content was generated by AI.
A YAML manifest of
solve/solve_overarguments, one entry per run, read bysps.load_manifestand by a newspecsolvecommand (check,solve,list) behind acliextra. Data comes from Excel, parquet and CSV files.Method, gate output, alternatives
Changes, file by file
src/specsolve/manifest.py(new):load_manifest(path) -> Manifest;Manifest.runs[name]is aRunwith.arguments(exactly the keyword arguments ofsolveorsolve_over) and.solve(**overrides). A closed pydantic schema (pydantic arrives through mathspec, as does pyyaml). The manifest's own keys are exactlymanifest,runs,from,archive(a directory:<archive>/<run>),sources(a list of locations) andworkers; every other key is an argument name ofsolve/solve_over, and the axis arguments are the axis classes' own field names. What onlysolve_overtakes is read off the two signatures at import, not listed by hand. A duplicate YAML key is refused (PyYAML would keep the last). Every refusal names the file and the run; an unknown key names the near miss and the keys valid there.src/specsolve/cli.py(new): Typer, imported only when the command runs, somainwithout Typer exits withpip install 'specsolve[cli]'rather than a traceback.src/specsolve/__init__.py,types.py,tests/test_architecture.py:load_manifestinPUBLIC_API("run it from a file");ManifestandRuninspecsolve.types.pyproject.toml: extracli = ["typer>=0.12", "fastexcel>=0.9"];[project.scripts] specsolve = "specsolve.cli:main"; the dev feature installs the extra besidegurobiandxpress, and the docs feature installs it because the how-to page runs the command.examples/manifest/: the how-to's study:specsolve.yaml, two workbooks, a CSV directory and a parquet file, overexamples/dispatch.yaml. The workbooks were written once withxlsxwriterthroughuv run --script; nothing in the tree regenerates them.docs/howto/manifest.md(new, How-to nav): runsspecsolve check,solve,solve base(the skip) andlistfor real during the build, then reads an archive and callsRun.solvefrom Python.docs/reference/manifest.md(new, Reference nav): the one home of the keys, inheritance, source locations, the Excel convention, the load-time refusals and the command.docs/reference/api.md,docs/reference/glossary.md(Manifest, Run),docs/about/architecture.md(module map, the fifteen unfenced modules, sixteen names),mkdocs.yml.tests/test_manifest.py,tests/test_cli.py(new);tests/test_architecture.py: one fence test, parametrized:typeronly incli.py,fastexcelimported nowhere.Tests
Loading (defaults, run keys replacing them,
from, a run's sources replacing what it inherits, a later location replacing a table, paths relative to the manifest from another working directory);argumentsequal to a hand-writtensolve_overcall, and the same objectives from both;workersbuilds a spawn pool thatRun.solveshuts down; a workbook reads one table per sheet; 17 load refusals and 3 location refusals parametrized with ids; the fastexcel and typer install hints; the signature test (every key is an argument ofsolve/solve_overor a manifest-own key; each axis class's constructor takes exactly its fields); CLI throughtyper.testing.CliRunneron a copy ofexamples/manifest: solve two runs then skip them,list,checkreporting a missingloadper run, an unknown run (exit 2), a failed run followed by one that solves, an infeasible run (exit 1). No bug, so no xfail.Mutation table
manifest.py:207-209)manifest.py:217-221)manifest.py:246-249)manifest.py:288-289)manifest.py:291-292)manifest.py:299-300)manifest.py:301-305)manifest.py:329-333)manifest.py:338-339)manifest.py:341-342)manifest.py:367-368)manifest.py:370-371)manifest.py:376-378)manifest.py:387-388)cli.py:37-38, body →pass)cli.py:69-71)cli.py:80)cli.py:114-116)extra='forbid'→'ignore', by hand)Each row deletes the guard and runs the whole suite (
tools.mutate, or by hand withgit checkout --and__pycache__dropped on both sides). Tree clean after each. Line numbers are those of 79477a4; 6f57835 removed one line from the resolve, which moves the rows below line 222 up by one.Gates
pixi run check(lint, format-check, typecheck, test): pass — 5046 passed, 567 skipped, 1 xfailed, on 6f57835 after mergingmain.pixi run -e docs docs-build: pass, "No issues found"; the how-to page's commands ran during the build.pixi run -e docs docs-test: 112 passed.Floors:
typer==0.12.0andfastexcel==0.9.0withpolars==1.30,numpy==1.26,highspy==1.13,mathspec==0.2.1in a scratchuvvenv:tests/test_cli.pyandtests/test_manifest.pypass (38).typer 0.12.0refuses alist[str] | Noneargument annotation, so the commands annotatelist[str].Not run:
pixi run test-floors(the floors environment installs no extras, so the CLI tests skip there and the manifest tests run).pixi lock: this repository has nopixi.lock(pyproject.tomlsays why), so there was nothing to lock. The changelog line is under## Upcoming version. LAYOUT: nothing an archive writes changed, so noLAYOUTbump and no interaction with #1835.Decisions the brief left open
specsolve.yamlin the working directory.[MANIFEST] [RUN]...is told apart by suffix: the first argument is the manifest where it ends in.yamlor.yml, sospecsolve solve basesolves the runbase.Recordholds no duration, andMetricsis not on aResult. Status and objective come from the returned record; a sweep prints its slice count and termination conditions, since a sum of objectives means nothing for a scenario sweep.checkattaches every slice of a sweep withtidy; whatsolve_overrefuses of the model as a whole (window separability) is reported bysolve. Missing files are found when data is read, not at load, socheckreports them per run andlistworks before the data exists.executor,workers_share_fs,spill_to.workers_share_fshas no meaning for a local pool;spill_toshared as a default would put two sweeps in one directory, which resumes the second from the first's slices when their keys match. All three remain overrides ofRun.solve..parquetand.csv, and refusesload.csvbesideload.parquet.SpecsolveErrorfor manifest refusals (DataErrorfor locations); no new error class.from_arrow(...) will return a Seriesfrom insideread_excelwhere pyarrow is absent;_workbooksilences that one warning.Sources replace, they do not add
A run that sets
sourcesreads exactly those locations. A run that sets none reads its parent's, or the defaults'. This is the same rule as every other key, so a run's line shows all it reads. Within one list, a later location still replaces an earlier table of the same name, which is howhigh_gaschangescost. The cost is repetition:high_gasandscenarioslist the default locations again.This also unblocks a rolling-horizon run beside plain solves: it no longer receives an inherited table its spec does not declare. A rolling run still needs its own spec over a local index (
into='t') and its own data, so the example does not add one.EachWindowis in the reference and the load tests.Commit 6f57835 changes the resolve, the example manifest, both doc pages and three tests: the inheritance test now asserts replacement and that a run with no
sourcesreads its parent's, and two tests that leaned on adding now list their full sources.Deliberately not done
Incremental manifests (a run that adds to or drops from what it inherits),
--jobsacross runs,init,write, zip archives, datarecord sources, nested or matrix runs, export adapters.tests/test_doc_examples.pydoes not track the two new pages: the manifest YAML is not a spec, and the how-to's Python runs during the build.🤖 Generated with Claude Code
https://claude.ai/code/session_01GS5ucQvLdLgdP1vaFy7kUS