Skip to content

chore(program): nothing left — this PR's work is in #585, #566 and #569 - #580

Closed
FBumann wants to merge 6 commits into
claude/mathspec-piecewise-api-7j0wptfrom
claude/mathspec-475-status-3whm7x
Closed

FBumann wants to merge 6 commits into
claude/mathspec-piecewise-api-7j0wptfrom
claude/mathspec-475-status-3whm7x

Conversation

@FBumann

@FBumann FBumann commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Prompt: "Rebase the stack below 580." — "What about 580?" — "Do that"

Note

The following content was generated by AI.

This PR is empty. Its diff against its base is nothing, and it should be closed rather than merged.

Where its work went:

what it did where it is now
the naming rule over every name main already had #585, merged
the same rule over ExpressionComparison and ArithmeticComparison #566, which is where those two nodes arrive
the paragraph saying lowering rewrites the one into the other #566
carrying all of it up the stack #569

The last eleven lines moved to #566 because they are only true where its two nodes exist, and the repo keeps one issue to one PR.

How this branch got here

Merged, never rebased: the hard rule here is never to force-push. main came in at #566, was carried up through #569, and arrived here; each merge is on the branch it was made on.

The suite passes on this head — 1446 passed — which is #569's suite, since this branch now adds nothing to it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JPqtQuarRGDmX2WrNVuKDP

…ts groups as the file does

Every public name in math_spec.program follows the naming rule in
docs/contributing.md, and a relation and a named expression are read from
the program the way the file declares them.

Redone on the #569 stack rather than replayed: #494 renamed Walk to
Direction and #559 split Partition out of it, so the records this touches
no longer exist under the names the original branch changed.

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

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

Copy link
Copy Markdown

FBumann and others added 2 commits September 20, 2026 13:04
The stack merged main up through itself while this branch was building.
`test_parser.py` gained two records built with the old signatures, so the
relation declaration drops its name there and the direction and the
partition carry it, which is what those two cases now assert prints.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MKUDdoyCtxHXm5Gcuze24W
… does, and a footprint holds kinds

`Direction.is_function_read` is `is_single_valued`, which is the phrase the
refusal beside it already prints; "function" read as callable. `Footprint.nodes`
is `kinds`, because it holds classes and the reading page iterates them as
`kind`. Neither is a name lpspec reads.

`RelationDeclaration.dim` reads a mapping built once rather than rebuilding
`dict(self.columns)` per call, which the typesetter makes once per role inside
comprehensions over roles.

The naming section of the contributing page said a node names the coordinate
map, which describes one node of five and made `Translate` read as a
counter-example to its own rule. It now says a node names the operation, with
the five verbs and nodes beside each other.

docs/contributing.md: n 38, avg 8.7, median 6, over25 2. Both long sentences
are older than this change; the two added are 11 and 15 words.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MKUDdoyCtxHXm5Gcuze24W
… check the loader makes (#581)

* refactor(program): a direction carries what a consumer reads, not the check the loader makes

`Direction.is_single_valued` is a module-private function in resolution.py.
It is the discriminator between `at` and `sum(by=)`, it has two callers in
one function, and no consumer asks it: a program's node type already says
which operator a call became. The refusal table in
docs/about/what-counts-as-public-api.md refuses a function whose answer a
declaration could give.

The `Predicate` union said it was what a lowered mask's root is built of.
That is false. It also holds `ArithmeticComparison`, which lowering rewrites
into an `ExpressionComparison`, so a consumer walking a program meets every
other member and never that one. The comment and the reading page now say so.

docs/reference/reading.md: n 52, avg 14.9, median 13, over25 7. The long
sentences all predate this change; the two added are 11 and 9 words.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MKUDdoyCtxHXm5Gcuze24W

* chore(resolution): the single-valued test is a local of the one call that asks it

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJfEUsCDtwXuXV8CANCHuR

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
#585 landed this branch's renames on main, and the two branches below carried
them up. What is left here is the paragraph saying lowering rewrites every
`ArithmeticComparison` into an `ExpressionComparison`, which needs #566's two
nodes to be true, and two docstrings.

Both conflicts were wording the base branch already carries: the row-building
trees are `roots`, and the golden test's carrier note names a direction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JPqtQuarRGDmX2WrNVuKDP
@FBumann FBumann changed the title refactor(program): a program names its nodes by the naming rule and its groups as the file does chore(program): the predicate union says which member a program never carries Sep 20, 2026
FBumann added a commit that referenced this pull request Sep 20, 2026
…carries

The `Predicate` union said it was what a lowered mask's `root` is built of.
That is false here: it also holds `ArithmeticComparison`, which lowering
rewrites into an `ExpressionComparison`, so a consumer walking a program meets
every other member and never that one.

These lines were #580's, which is where they were written. They are only true
where the two nodes exist, and this is the branch that adds them.

docs/reference/reading.md: the two sentences added are 11 and 9 words.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JPqtQuarRGDmX2WrNVuKDP
@FBumann FBumann changed the title chore(program): the predicate union says which member a program never carries chore(program): nothing left — this PR's work is in #585, #566 and #569 Sep 20, 2026
@FBumann FBumann closed this Sep 20, 2026
@FBumann
FBumann removed this pull request from stack #587 September 20, 2026 21:35
FBumann added a commit that referenced this pull request Sep 22, 2026
* chore(language): a where comparison is one grammar rule over the expression grammar's arithmetic

The where grammar's three comparison rules — a name against a literal,
two relation columns, and position() against an integer — are one rule,
side <op> side, where a side is the expression grammar's ARITHMETIC. What
a side is, resolution decides with the schema in hand, and it hands back
the same typed nodes with the same messages.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EEeM2YoAk4Xr2uWB5qwMsH

* test: the parser test names the validation class that holds the refusals it points at

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EEeM2YoAk4Xr2uWB5qwMsH

* chore(parser): the where depth measurement names the nodes it walks rather than Any

`explicit-any` is an error on `src/math_spec` since #572, and that PR took
only the fold's return type from #567, leaving `_nested` on this branch
with a signature whose `Any` no longer even imports. `_ParsedWhere` is the
union the measurement walks, which is #567's own answer for this file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V2SFmoxEF3SnaPKp7HbZTk

* feat(language): a where may compare arithmetic over parameters

Either side of a where comparison may be an expression over
parameters: arithmetic, a reduction, a pullback, a translation with
its edge, a macro, a named expression. A side is expanded, typed,
degree-checked and dim-checked as an expression is, and refused where
it names a variable or a dual. Two parameters compare the same way.

The resolved tree holds ArithmeticComparisonNode over the core syntax
tree, and lowering rebuilds every mask with ExpressionComparisonNode
over program expressions. A case when: comparing expressions is
refused as undecidable before the data arrives.

expressions.md sentence lengths: n 68, avg 14.0, median 13, over 25
words 3 (base: n 56, avg 13.0, median 13, over 25 words 2).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EEeM2YoAk4Xr2uWB5qwMsH

* refactor(language): a comparison of expressions answers what it reads once, on the program's form

The resolved form of the comparison had a second walk collecting the
parameters and relations its sides read, and nothing asks a resolved
mask that question: lowering rebuilds every mask before one reaches a
consumer. The arm is the assertion the typesetter already carries for
the lowered node. The one-line mask wrapper in lowering is inlined, and
the two validation classes for what a where side may be are one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EEeM2YoAk4Xr2uWB5qwMsH

* refactor(language): a namespace is built from its schema and carries it, so nothing passes both

Namespace(schema) reads every declaration at construction; the eight
argument constructor and the classmethod that was its only caller are
gone. expression_of, _check_expression and _named took the schema and a
namespace built from that same schema; they take the namespace.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EEeM2YoAk4Xr2uWB5qwMsH

* refactor(language): the single-text doors live with the tests that are their only callers

expression_of and where_of had no caller in the package, and the two
assertions that named expression_of as the door now name
resolve_expression, which is the one the package walks through. The
plain form of a where comparison is read by one method: a name that is
a value against another is arithmetic, which used to be a second
question asked after the first. The "prefix a context once" rule has
one home in errors.py.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EEeM2YoAk4Xr2uWB5qwMsH

* refactor(program): the branch's nodes follow the naming rule main now states

#585 renamed the program's where vocabulary and expression nodes on main. This
branch was written against the old names, and its own two comparison nodes
arrived with the suffix the rule reserves for the core AST. Applying the same
rename here first is what lets the base merge that follows agree line for line
rather than conflict on every one of them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JPqtQuarRGDmX2WrNVuKDP

* docs(reading): the predicate union says which member a program never carries

The `Predicate` union said it was what a lowered mask's `root` is built of.
That is false here: it also holds `ArithmeticComparison`, which lowering
rewrites into an `ExpressionComparison`, so a consumer walking a program meets
every other member and never that one.

These lines were #580's, which is where they were written. They are only true
where the two nodes exist, and this is the branch that adds them.

docs/reference/reading.md: the two sentences added are 11 and 9 words.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JPqtQuarRGDmX2WrNVuKDP

* docs(reading): a comparison against a literal is not an expression comparison

A consumer walking a lowered mask gets ParameterComparison for `p_max > 5` and
ExpressionComparison for `1 * p_max > 5`, though both mask the same
coordinates. The page named only the second form.

Gates: pytest tests/test_docs.py tests/test_reading_page.py (34 passed), the
full suite (1383 passed, 1 skipped), mkdocs build --strict (clean, with the
docs.python.org inventory dropped for the run, which this environment's proxy
refuses with a 403), prettier --check (clean). Not run: typos, reuse lint,
taplo, zizmor, compile-tex, for want of the tools.

docs/reference/reading.md, by the measurement in the docs-writing skill:
n 55, avg 14.6, median 13, over25 7. Every long sentence predates this change;
the three added are 10, 20 and 9 words.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JJfEUsCDtwXuXV8CANCHuR

* fix(language): a comparison with the literal first is refused as the order it is (#594)

* fix(language): a where comparing two numbers, and a lone case comparing expressions, are refused at load

A comparison with a number on each side is decided before any data arrives,
and reached the program as an ExpressionComparison over two constants. A
cases: block of one case escaped the undecidable refusal, which only looked
at pairs. names_read on a mask over a cased entry dropped the data its
regions are decided by. The relation refusal still said every other
comparison tests a name against a literal.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019tCWoetBmjF1LpbTbjQY29

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants