refactor(program): the declaration vocabularies have one home, so a program cannot spell one differently from the file - #219
Merged
Conversation
…rogram cannot spell one differently from the file Closes #209 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
FBumann
enabled auto-merge (squash)
August 28, 2026 13:19
This was referenced Aug 28, 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.
program.pyderives its four vocabularyLiterals frommath_spec.modelinstead of spelling them a second time, the twoby=-list claims that had no home here get one intests/test_parser.py, and the boundary claim #209 offers to decline is declined in writing. Closes #209.The fourteenth claim: the second home is removed, not fenced
test_architecture.py::test_every_program_vocabulary_matches_the_one_the_language_validatespredicted its own end — "the day the program is declared where the vocabularies are, each of these becomes an import". It becomes an import here:The comment's first option — keep both spellings and add four
set(get_args(...)) == set(...)assertions — is the weaker one, and measurably so:typinginterns identicalLiterals, soprogram.VariableAbsence is model.VariableAbsencewas alreadyTrueonorigin/mainwith the two written out separately. A fence that passes for the wrong reason is not a fence.What the removal buys, run on both trees with
'semicontinuous'added tomodel.VariableDomainalone:get_args(model.VariableDomain)get_args(program.VariableType)origin/main('continuous', 'integer', 'binary', 'semicontinuous')('continuous', 'binary', 'integer')('continuous', 'integer', 'binary', 'semicontinuous')('continuous', 'integer', 'binary', 'semicontinuous')(Mutation reverted; the tree here is unmutated.)
The four names stay on
math_spec.program's surface — it is the only modulemath_spec.__all__exports, so it is where a consumer reaches a vocabulary — and they stay documented assignments rather than a bare re-import, becausetest_public_surface.py::test_the_program_module_exports_everything_it_definesreads the module's defined names from source and an import is not one. The prose that made each vocabulary make sense moved tomodel.py, beside the members.VariableTypeis still the program's name forVariableDomain. Renaming the field is a separate decision (#157's), not this one.The eleven claims: where each lives now
Eight were reported as already having homes here, and all eight check out on
origin/main:sumover an undeclared nametest_validation.pyan-unknown-name-in-a-constraint,sum-over-typoby=where the operand lacksovertest_dimensions.pysum-requires-the-grouped-dimtest_validation.pyby-lookup-typo,by-names-a-dimensionedge=test_dimensions.py::TestTheEdgeRulesAreDecidedAtLoad::test_a_named_offset_with_no_edge_is_refused_by_to_spectest_validation.pyby-lookups-over-different-dimensionsby=onto a dim already carriedtest_dimensions.pysum-into-a-dim-the-operand-already-carriesTwo did not, and are added here —
test_a_list_the_language_cannot_read_is_refused_at_loadandtest_a_list_is_only_legal_in_a_kwarg_value. Both are grammar claims, so they land intests/test_parser.pyas one table of seven refusals (a trailing comma, an empty list, a missing comma, an unclosed bracket, a list as a positional argument, as a term, and as the whole expression) plus the well-formedby=[a, b]beside it.expression_parser.pystates the rule in a docstring and nothing held it.The remaining five are the shape claims, in #215 (#212).
The two boundary claims, declined
test_a_program_outside_the_language_is_rejectedandtest_the_eager_lane_holds_every_program_to_the_boundaryboth hand-built a malformedProgramand expectedcheck()to refuse it. #209 says declining is a fine answer if it is written down as one, so it is written down, inprogram.py's module docstring:test_program_check.py, the second of the two deleted modules, covered a consumer verb this package does not have;adviceis the verb that crossed, andtests/test_advice.pyis its home.Verification
pixi run cigreen (lint, 767 tests, docs-build, compile-tex).Not done
VariableType→VariableDomainon the program, and thevariable_typefield with it: one name for one vocabulary is right, and it is a rename downstream sees, so it belongs with #157 rather than smuggled in here.