Skip to content

feat(language): a file says what it reads, and fragments and patches compose into one model - #691

Closed
FBumann wants to merge 12 commits into
mainfrom
claude/loving-thompson-vurdqx
Closed

FBumann wants to merge 12 commits into
mainfrom
claude/loving-thompson-vurdqx

Conversation

@FBumann

@FBumann FBumann commented Sep 24, 2026

Copy link
Copy Markdown
Contributor

Prompt: Please take a look at #571 — copy its branch and rebase onto main.

Prompt: Open the pr as a replacement of Fabian's

Note

The following content was generated by AI.

This replaces #571. It holds the same nine commits, with Fabian as author, rebased onto main 41 commits later, plus one commit that satisfies main's type gate. #571's description still holds for the design. This body lists only what the rebase changed.

What this changes

What #571 changes: a closed given: key, math_spec.merge, math_spec.override, and the component library under examples/library/. The rebase ported that onto main's refactors:

Why

#571 could not merge: main refactored the modules it touches. This is the rebase, so the linopy work can build on given: against current main.

Gates, what was not run, what follows

Gates run on the final tree:

  • pixi run lint passes.
  • pixi run test: 1752 passed.
  • mkdocs build shows no warnings except one: this sandbox cannot fetch the docs.python.org inventory, which fails --strict.
  • render-tex rendered 34 models.

Not run.

  • compile-tex: the tectonic bundle download is blocked in this sandbox.
  • The single commits were not gated one by one. Commits 3 to 9 fail pyrefly until the last commit, and commit 1 carries the balnce typo that commit 2 allows.

Stacked on this. #690 (merge and override load every model they compose, null in a patch makes what it names absent, and a merged model keeps its assumptions). #689 on main is related (an open bound is null).

Not done.

  • Program.given drops the domain a file states.
  • A given frame is compared as a list.
  • specsolve does not yet refuse a program whose given is not empty.

Generated by Claude Code

FabianHofmann and others added 10 commits September 24, 2026 14:10
A file declares the columns and row families it reads and does not build
under one closed key, given: variables: and given: constraints:. A given
name is in the namespace every expression resolves against, has a frame for
the dimension check, and dual() may name a given row family. The file
typesets on its own, with a Given legend group. The program carries the
declarations under Program.given, lowering does not refuse, and advice gains
the kind given, one note per declaration a consumer binds. A dimension only a
given declaration indexes counts as reached. The schema is regenerated; the
golden output does not move.
…ne, and given: sits beside variables:

A given name asked of typeset_declaration was refused with "is not a named
expression, constraint or variable". It now says that a given declaration
prints no line of its own, and that it prints in the legend under Given.

The `given` field moves after `variables` on Spec, so to_yaml writes the key
where the file-shape table and the examples already put it. The schema and the
golden output are unchanged.

The symbols kwarg `given` is renamed to `upright`: it means the glyph, not the
language's `given:` key. `balnce`, the did-you-mean input in tests/test_given.py,
joins the typos allowance.
override(base, patches) lays each patch over the base a field at a time and
hands back one mapping for to_spec. A partial entry must land on a declaration
the base has, and a miss is refused with the near miss named. A whole entry
creates. null at declaration level removes, and a stale removal is refused.
Sibling patches must write disjoint paths, so their order never decides a
model. A dimension or a relation may be added or restated exactly and never
changed. The objective is one declaration laid over by the same rules. given:
is laid over one kind at a time, entry by entry. Base and patches are copied,
never mutated.

override is exported from math_spec. The tests' own fixture override is
renamed varied so the verb owns the word. docs/howto/compose.md carries the
recipe and the four refusals, each produced by running the case.
…nd a null section is refused

`override` read `dimensions: {snapshot: null}` as a removal and `dimensions:
{snapshot: {}}` as a restatement, though the rule is that a patch adds a
dimension or a relation or restates one word for word. Both are refused now,
with a message that names the rewrite.

A section set to null, such as `constraints: null`, laid as an empty mapping
and changed nothing. It is refused too, and the message says that removal is
written one declaration at a time.

The page and the messages now say dimension and relation throughout, where
they said axis in some places and dimension in others.
… sibling introduces

merge(fragments, description=None) composes peers before validation. A
dimension or a relation every fragment may declare, and the ones that do
say the same thing about it, prose excluded. Every other name is owned,
and a second claim is refused naming both fragments. The objectives are
summed, each term in parentheses, and the senses and the versions have
to agree. A given declaration is folded into the declaration a sibling
introduces, once the reader is checked to say the same or less, and two
fragments that both only read a name have to read it the same way.

merge is exported beside override, the given advice names merge() as
the fragment's route, and compose.md, declarations.md and limits.md
describe both verbs.
…annot read what it builds

The composed objective joined its terms in fragment-iteration order, so the
same fragments under two argument orders gave two expressions. The terms are
summed in the fragments' name order now.

A fragment that declares a name and reads it under given: too is refused,
naming the fragment, the name and the rewrite. Such a file does not load on
its own, and folding the reading away put it in a model that loads.

merge writes version: only where a fragment declares one. Two fragments
declaring different versions are still refused.
… file prints alone

examples/library/ holds a coupling surface, a generator and a load as
fragments, and variants/commitment.yaml as a patch over their composition.
One symbol table serves the library, cut per page to what each model
declares. tools/gallery.py prints a page per fragment and a composed page
with one tab per variant; tab() moves to tools/_page.py; render_tex skips
variants/. tests/test_library_example.py holds the library to what its
pages claim.

Sentence lengths on the five pages: median 8 to 15 words, three sentences
over 25.
…t flow one way

The layout rules keep the rule sentence and drop the clause that argued for
it. The library pages say "surface" where one said "spine", and the component
fragments call `Port_p` a flow, as the surface and its page already do.

The composed page's variant test reads the page and asks for a tab per file
under `variants/`, rather than comparing a glob with itself. The balance test
compares each merge against the surface's own row, which is what the page
claims. The gallery picks the library block by the model's directory.
The two verbs typed their inputs and results as dict[str, Any], which the
type gate on main refuses. They now take a Mapping[str, object] like every
other entry point, and read a section through one helper that names the
shape the closed schema checks at load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012aGN1gzy2vbqGchcQ8TbfV
@read-the-docs-community

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

Copy link
Copy Markdown

…sumer binding it

"Bind" collided with a variable's bounds, which the glossary keeps as the
only sense of "bound". The glossary gains a Provide entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q8Y8AoPsJCyMsURHvgzm7h
@FBumann FBumann added the area: composition Assembling a model from files, and fixing a decision label Sep 25, 2026
@FBumann FBumann added this to the Model composition milestone Sep 25, 2026

FBumann commented Sep 25, 2026

Copy link
Copy Markdown
Contributor Author

Prompt: "I think we should close all superseeded ones that are from me (FBumann), and name what superseeded them "Superseeded by #xyz""

Note

The following content was generated by AI.

Superseded by #732.


Generated by Claude Code

@FBumann FBumann closed this Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: composition Assembling a model from files, and fixing a decision

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants