Repository navigation
feat(language): a model declares what it assumes of its data - #589
Merged
FBumann merged 8 commits intoSep 22, 2026
Conversation
An eleventh declaration key, `assumptions:`, states what the model expects of the numbers a consumer binds. An entry is a where string, or a mapping with `holds:`, an optional `where:` and a `description:`. The language decides nothing about the data, so it types the predicate, carries it, and prints it. `Program.assumptions` is the one place a consumer looks. It holds the file's entries as `Holds`, then each `piecewise:` block's own conditions, which used to sit on `PiecewiseDeclaration.checks` under a second message function. `assumption_message` replaces `check_message`, `Increasing` and `Curved` carry the method their sentence quotes, and every member carries its block. The typeset document prints all of them under one Assumptions heading. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DBU3ocfqkHe99mWmxijw64
Documentation build overview
25 files changed ·
|
… leaves alone `pixi run lint` formats the Python in a fenced block at 120 columns. The message claim was 180 columns, so the hook split the call across three lines and left the comment on the closing one, where `tests/test_reading_page.py` reads no claim at all. Binding the message first keeps the code short, the sentence whole, and the line stable under `ruff format`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DBU3ocfqkHe99mWmxijw64
…e-split-kdqegz-arithmetic-where' into feat/assumptions-on-566
…e-split-kdqegz-arithmetic-where' into feat/assumptions-on-566
FBumann
added this pull request to stack #601
September 21, 2026 16:27
…ic-where' into claude/adoring-brahmagupta-ovwe3v Carries main at 0.0.0-alpha.110 up the stack from #566. Merged clean, and the suite is green: 1414 passed, 5 skipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EQHKbwGtpVBm6yKHzrjMx9
FBumann
pushed a commit
that referenced
this pull request
Sep 21, 2026
…brahmagupta-ovwe3v-operators Carries main at 0.0.0-alpha.110 up the stack from #589. Merged clean, and the suite is green: 1448 passed, 5 skipped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EQHKbwGtpVBm6yKHzrjMx9
`description:` stood on the block and reached no consumer. `Holds` now carries it and `assumption_message` trails the sentence with it, so a failure names the columns that are wrong and the reason the rule exists. The typeset document still prints no `description:`, for an assumption as for a constraint: where author prose belongs in an equation is one question for every declaration, not this key's. Prose measured: assumptions.md n 23 avg 14.7 median 15 over25 1; reading.md n 60 avg 14.8 median 13 over25 8. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018YVS5Ngm3dvMzrYw6sFwbz
FBumann
pushed a commit
that referenced
this pull request
Sep 21, 2026
…d/582-onto-592 #589 grew `feat(language): a refusal quotes the assumption's description` while this branch was building, and it lands on the same two things this branch changed: `Holds.description` and `assumption_message`. Both sides added the field. They differ on what the refusal does with it: - there, the description **trails** the generic sentence, so a failure names the columns and then says why the rule is there; - here, it **replaced** the sentence, so a curve's condition read as its own prose and the columns were lost. Resolved to the trailing form. It loses nothing either side had — a derived condition still says everything it said, with the columns in front of it — and replacing would have reverted a decision taken on the rung below. The `Check` arms of their `assumption_message` go, because this branch retires that union; what they said now rides on each entry's `description:`. `test_every_check_has_a_sentence` asserted the message *starts* with the method's sentence. It now asserts both halves: the columns lead, and the method's sentence trails. `reading.md` keeps their `cost_is_never_negative` entry, which is what shows a file-written description, and its two message values are recomputed rather than merged: both now carry the columns and the reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EQHKbwGtpVBm6yKHzrjMx9
…e-split-kdqegz-arithmetic-where' into claude/adoring-brahmagupta-ovwe3v # Conflicts: # tests/test_lowering.py
…ed at load A where that folds to false checked the entry on no row, and one that folds to true narrowed nothing; both loaded, and a variable behind such a fold was never seen by the variable refusal. The typesetter's refusal of a name declared as two kinds no longer puts an article before the kind. The expressions page says assumptions sit outside the flat namespace, as constraints do. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019tCWoetBmjF1LpbTbjQY29
This was referenced Sep 22, 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.
Replaces #471, which is stacked on the retired #469. A file states what it
assumes of its data under
assumptions:.Program.assumptionsis the oneplace a consumer reads every fact the numbers must meet, the file's and each
curve's, with one message function.
What this changes
Surface. An eleventh declaration key,
assumptions:. An entry is a barewhere string, or a mapping with
holds:, an optionalwhere:and adescription:. It holds at every coordinate of the product of the dimensionsits two masks name, a missing row reading as false as in any
where. There isno
dims:, because a predicate widens nothing. A predicate that folds to aliteral is refused, and so is a
where:that folds to one: true narrowsnothing, and false checks the entry on no row. A variable in either is
refused. Reference:
docs/reference/language/assumptions.md.An assumption is named outside the flat namespace, as a constraint is:
no expression names either, so a model may name an assumption after a
variable. The expressions page says so beside the sentence on constraints.
typeset_declarationrefuses a name declared as two of the kinds it prints,as it did for a constraint and a variable.
One carrier for what the data has to satisfy.
Program.assumptionsmaps aname to an
Assumption: the file's entries asHolds(predicate, where, description), then eachpiecewise:block's own conditions, in block order.PiecewiseDeclaration.checksis gone, andcheck_messageisassumption_message.IncreasingandCurvednow carry the method theirsentence quotes, and all four derived members carry their block, so the
sentence needs no second argument. A derived entry is named
<block> increasing; the space is what keeps the two kinds apart in onemapping, since a declaration is named the way an expression writes it and no
file can write that name.
The author's words reach the refusal. A
description:says why a rule isthere, which the parameter names do not.
Holdscarries it, andassumption_messagetrails the sentence with it, so a failure names thecolumns that are wrong and the reason the rule exists. This is what #268 asked
its
refusals:block for, undermessage:.Rendering. A fourth section, Assumptions, after Variable domains: each
entry as a line, a lone comparison aligned on its relation as a constraint is,
then each curve's conditions. The increasing x-axis is an inequality between
neighbours under the plain translation; the shape a method is exact for is
prose, as a paper writes it; the two conditions on a
points:mask are statedof the set the mask admits.
Format.set_ofis the one new format method.typeset_declarationtakes an assumption name, and the notation page showswhat each
method:row assumes.Why
An assumption about the numbers is a rule two engines must not answer
differently, so it is language; the numbers are the engine's, so the engine
checks. The language already did this once, for
piecewise:, under a secondvocabulary. Landing the author's entries beside those rather than next to them
leaves a consumer with one mapping, one closed union and one refusal sentence.
What the port dropped, and why this is not a rebase
#471 forked at
alpha.91;mainisalpha.108. Merging #566's branch intoit conflicts in 27 of the 36 files it touches, so the change is re-applied
here rather than replayed — the hard rule is never to force-push.
#471's centre was
ParameterPairComparisonNode, built because #469 admittedp_min <= 0.5 * p_maxand refusedp_min <= p_max. #566 deletes thatrefusal, so the node goes, with its dtype refusal, its
param_pairexclusivity subject and
_PAIR_ORDERS, its dimension rule, its schema entryand its typesetter branch.
p_min <= p_maxlowers as anExpressionComparisonlike any other arithmetic. A casewhen:cannotcompare two parameters, which is #566's rule for every comparison of
expressions, not a loss this PR adds.
Gates
pixiis refused by this environment's egress proxy, so the gates ran from auvenvironment on Python 3.12. That is a departure from the "everything runsin a pixi environment" default.
pytest -n 4ruff check,ruff formatpyrefly checktypos,reuse lintprettier --checkmkdocs build --strictdocs.python.orginventory dropped for the run, which the proxy refuses with 403render-texcompile-texThe schema, the three golden files, the notation page and the gallery pages
were regenerated and read. Both YAML models on the new page load with
python -m math_spec check. The description commit regenerates none of them:it changes no typeset output, so the goldens and the generated pages are byte
for byte what the suite already held.
The review commit (
3352776) was written as three failing cases first, inTestAssumptions:a-where-that-is-always-false,a-where-that-hides-a-variable-behind-a-foldanda-where-that-is-always-true. The merge of #566's review commit is its owncommit (
7e9f7a6), resolving one conflict intests/test_lowering.pybykeeping both sides.
Mutation table
Each guard deleted in turn, the suite run, the file restored and the tree
checked clean.
TestAssumptions[a-predicate-that-is-always-true],[...-always-false]TestAssumptions[a-where-that-is-always-false],[...-always-true],[...-behind-a-fold]TestAssumptions[a-variable-in-the-predicate],[a-variable-in-the-where]Program.assumptionstest_assumptions_carry_the_file_s_entries_and_the_curves_behind_them, 9 more intest_piecewisegallery:commitment.mdHoldstest_an_assumption_refuses_in_the_words_the_file_wrote,test_the_page_shows_the_declarations_the_expansion_emitsThe walk's line census was the guard that caught the two arms the fixture did
not reach while this was being written:
line()for an assumption, and theassert_neveron the derived union.Coverage moved, and what was left out
test_a_block_is_kept_as_the_checks_a_consumer_binding_it_runsistest_a_block_assumes_of_its_data_what_the_method_implies, asserting thewhole
Program.assumptionsmapping rather than acheckstuple.test_every_check_has_a_sentencereads the mapping and callsassumption_message;test_a_method_names_the_curvature_it_is_exact_forand
test_a_file_supplied_mask_derives_nothingread it too.test_a_name_declared_as_none_of_the_three_is_refusedis..._of_the_four_....tests/test_reading_page.pychecks 16 claims rather than 12: the page's newblock is run like the rest of it, and
curve.yamlgains one writtenassumption so the refusal quoting its
description:is a line the suiteevaluates.
bpdimension, twolpcurves — one masked byits own breakpoints, one over the whole axis — and eight assumptions, so
every new arm of the walk prints into a committed file.
Program.assumptionsat the data door — is its own PR, as feat(language): a where may compare arithmetic over parameters #566 said of the mask evaluator. The
typeset document prints no
description:, for an assumption as for aconstraint: where author prose belongs in an equation is one question for
every declaration, and answering it for this key alone puts a paragraph
inside a math line. The
convexmethod's "convex or concave" wording isreached through the notation page rather than the golden fixture, which
would have cost a third curve for one string. An empty
description: ""loads and says nothing; refusing it is one field constraint, left for a
reviewer to ask for.
A limit moved.
docs/about/limits.mdsaid a check on a column belongs indata preparation. It now says the rule is language and the check is the
consumer's, and the unit-checking row points at
assumptions:for a range.🤖 Generated with Claude Code
https://claude.ai/code/session_018YVS5Ngm3dvMzrYw6sFwbz
https://claude.ai/code/session_019tCWoetBmjF1LpbTbjQY29