Conversation
…ne name means one thing The fixture helper and the new file-level verb were both called `override`, and a test file that wanted the second would import the first. The helper replaces a dotted path wholesale; the verb lays a patch over a declaration field by field. Two rules under one word is one too many. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CA5v9XJvgYViUHP7hSiKPU
`override(base, patches)` lays each patch over the base a field at a time, so a patch says only what it changes and a framework's model can be extended without being copied. The composed mapping goes through `to_spec` like any file, so nothing here resolves a name or checks a dim. The verb is built to collide, so every collision the caller did not ask for is refused: - an entry naming some fields must land on a declaration the base has, and the refusal names the near miss and what the entry is short of; - two patches writing one field are refused, both named, which is what stops the order they are given in from deciding a model; - a dimension or a relation may be added or restated exactly, never changed. `null` under a declaration's name removes it, and a removal the base cannot satisfy is refused. The marker is positional, so `where: null` is still the mask the schema takes. `python -m math_spec compose base.yaml -p patch.yaml -o composed.yaml` writes the composed model and says on stderr what each patch added, edited or removed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CA5v9XJvgYViUHP7hSiKPU
The recipe: write the base, write each patch as the change it makes, name the patches, and read the composed file. Each refusal is quoted whole, since the message names the rewrite. `limits.md` gains the paragraph that says why the verb refuses what it refuses, which is the page that argues rather than instructs. Sentence length on the new page: n 37, avg 12.1, median 11, over 25 words 0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CA5v9XJvgYViUHP7hSiKPU
Documentation build overview
3 files changed+ howto/compose/index.html+ reference/math_spec/composition/index.html± about/limits/index.html |
…pelling is not corrected `_block` returns the schema's block class rather than `type[Any]`, so reading `model_fields` off it is typed, and `_agrees` returns a bool rather than whatever `==` gave it. Both were pyrefly errors that the local environment could not run. `balnce` joins `wher` and `generatr` in the typos allowance: it is the input to the did-you-mean suggester in the composition tests and on the how-to page, so correcting it deletes the case. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CA5v9XJvgYViUHP7hSiKPU
This was referenced Sep 16, 2026
FBumann
added this pull request to stack #512
September 17, 2026 06:48
`compose` had no caller in the tree, and the per-patch summary it printed was a change log threaded through eight functions that only it read. Both go, with the two tests and the how-to step that documented them. `override` is what it always was, and the composed model is written out with `Spec.to_yaml()`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CA5v9XJvgYViUHP7hSiKPU
This was referenced Sep 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
The following content was generated by AI.
What this changes
ms.override(base, patches)lays patch files over a base model, a field at a time. Three rules make every collision the caller did not ask for a refusal.The three rules, and what each one stops
Each refusal names both sides and the rewrite:
Layering is not lost — it is written out.
override(override(base, …), …)puts the order on the page, where a reader sees it, rather than in an argument's position, where nobody does.Why
#302 is the open decision, and this takes the layered side of it — #250's half, not #244's.
mergeis not ported here: peers and layers obey opposite laws, so one verb cannot be both, and the peer half is still #302's to decide.#250 named the risk it did not answer:
overrideis designed to collide, so a patch that quietly replaces a constraint the base relied on yields a valid model meaning something else. The three rules above narrow where a silent collision is allowed down to the one place the caller asked for it — the base and one patch, one field. What they do not catch is a patch that replaces a declaration correctly and changes what the model means. That is what the diff is for:Spec.to_yaml()writes the composed model, and the diff against the base is the review.What is new against #250, and what is carried over
Carried over: the patch-over-base shape, field-by-field laying,
nullas a declaration-level removal, the stale-removal refusal with the near miss, and the base left untouched.New here:
constraints: {power_balnce: {...}}created a second constraint. Whether an entry is whole is the schema's own answer (Block.model_validate), so no second list of required fields exists to drift.merge's law. A patch may add a dimension or a relation, or restate one exactly; a disagreement is refused.objective: {sense: maximize}dropped the expression and failed the schema. It now lays over field by field, takesnull, and refuses a partial patch where the base declares no objective.The compose verb, and why it is gone again
An earlier head of this branch added
python -m math_spec compose, which wrote the composed model to-oand a per-patch account to stderr.18685d2removes it.It had no caller in the tree, and the stderr account cost a change log threaded through eight functions in
composition.pythat only the shell front read —override()discarded it, and__main__.pyimported two private names to reach it. A diff of the composed model says everything the account did, except which of several patches wrote each line, and nothing here needs that yet.Spec.to_yaml()writes the file. The verb is 25 lines on the day something composes from a shell, and it will know its shape better then.Why this sits on main rather than on #250's branch
claude/override-a-baseis 250+ commits behind and predates the lookups → relations rename, so itsexamples/composed/fragments no longer load. The verb is ported forward instead, at the current spellings, which is what the user chose when asked. #244 and #250 stay closed and unrebased.Verified
No pixi in this environment, so the gates were run against a python 3.13 interpreter with the runtime and docs dependencies installed by pip, not the pinned solve.
pytest -q— 1302 passed, 6 skipped on18685d2(1278 onorigin/main).ruff checkandruff format --check— clean, at 0.15.8, wherepixi.tomlpins 0.16.1. Formatting could differ.mkdocs build --strict— builds, after droppingdocs.python.org/objects.invfrommkdocs.ymllocally, which this environment's proxy refuses with a 403. The dropped line is not in the commit.prettier --check(3.8.1) on the changed pages — clean.typosclean.Not run:
reuse,zizmor,taplo, andcompile-tex— not installed here.pyreflyreports the same pre-existing stub errors with and without this diff.Mutation table
Every guard deleted in turn, whole suite run, then restored. Taken on the head that still carried the shell front, so the counts include its two tests.
The last two were still green on the first pass, and both tests were the problem rather than the guards. The
null-at-depth case setwhereon a variable whose base declared none, so deleting the key and setting it toNonewere the same mapping. The copy case asserted only that the base was equal afterwards, which a shared sub-object satisfies; it now asserts that no declaration in the result is the base's or the patch's own object.Deliberately not done
merge. The peer half of shared math has no composition verb, so a component library ships as generated YAML or a script #302 is a separate decision and a separate verb.examples/is untouched. A patch file is a load error on its own, so it would need aNOT_MODELSentry intools/render_tex.py; the worked example lives on the how-to page and intests/test_composition.pyinstead.tests/fixtures.overrideis nowvaried, because the fixture and the verb cannot both beoverridein one suite. No coverage moved; the helper is unchanged.🤖 Generated with Claude Code
https://claude.ai/code/session_01CA5v9XJvgYViUHP7hSiKPU
Generated by Claude Code