Skip to content

feat(language)!: a construct that reads the order of a dimension needs the dimension declared ordered - #793

Merged
FabianHofmann merged 11 commits into
mainfrom
claude/datarecord-code-review-edeckw
Oct 6, 2026
Merged

FabianHofmann merged 11 commits into
mainfrom
claude/datarecord-code-review-edeckw

Conversation

@FBumann

@FBumann FBumann commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Prompt: Ordered should be false by default, and mathspec raises if a order sensitive operation is done along an unordered dim!? … Yes, raise the issue, explaining why its needed for datarecord AND its generally a good change inside mathspec. THen do a PR about it in mathspec

Follow-up: 793: do the fixes

Follow-up: let a fable agent recommend the strategy for 793

Note

The following content was generated by AI.

Breaking. A dimension declares ordered: true where its order is part of the model; the default is false. shift, sum_back, position(), piecewise over: and sos type: 2 along an unordered dimension are refused at load, naming the fix. Closes #792.

Before After
snapshot: { dtype: int } with shift(p, along=snapshot, …) loads refused at load; write snapshot: { dtype: int, ordered: true }
What changed
  • DimensionBlock.ordered: bool = False. It is written only where true, so a file that never wrote it dumps unchanged. A dump that leaves ordered out (exclude_defaults, exclude_unset, exclude=) writes the dimension without it.
  • One refusal, errors.unordered, raised at five places: the expression resolver (shift, sum_back), the where resolver (shift(<predicate>), position()), and reference_errors for piecewise and sos type: 2. The formulations are checked on the block, before their expansion writes the shift and position they lower to, so the refusal names the block.
  • Namespace.unordered(name) is not self.schema.dimensions[name].ordered. Every caller checks first that the name is a declared dimension. validation.py reads the field directly.
  • Program.dimensions[d].ordered. DimensionDeclaration carries the field, so a consumer of the program sees it. Lowering builds it by keyword.
  • ordered is a claim about the space, not the space. One rule, composition._joined, serves merge and override:
    • merge folds it as a union: a dimension one fragment declares ordered is ordered. Every other field still has to be equal. Each fragment still loads alone.
    • override compares a restated dimension or relation as the schema reads both sides, so a written ordered: false equals an omitted one. A patch may add ordered: true over a base without it. A patch that writes ordered: false over an ordered base is refused, because the base may already step along it. A patch that leaves ordered out over an ordered base keeps it ordered.
  • Sweep. Every spec that steps along a dimension declares it ordered: examples/, tests/expand/, the golden model, the test fixtures, inline test specs, and the YAML blocks of the reference pages. The generated pages, the schema and the golden output are regenerated; the golden .out files did not change.
  • Docs. dimensions gains the field and an ## Order section. The language index gains an eleventh rule that points at it. compose states the union rule for merge and the widen-only rule for a patch, with the refusal quoted whole.
Review fixes (2026-10-06)

Each fix has a test that failed on the tree before it:

Finding Fix Test that failed before
F4: Program.dimensions dropped ordered DimensionDeclaration.ordered, carried by lowering test_a_program_reports_whether_a_dimension_is_ordered[ordered]
F5: a patch's ordered: false differed from the omitted one both sides normalised through the schema before comparing test_a_patch_restates_a_dimension_as_the_schema_reads_it[false-written-out]
F6: two fragments that differ only in ordered were refused merge folds ordered as a union test_a_dimension_one_fragment_declares_ordered_is_ordered_in_the_composition[…] (both orders)
F6: a patch could not add ordered: true override widens …_as_the_schema_reads_it[widened-to-ordered], [ordered-left-out]
F6: a patch could narrow silently once widening was allowed explicit refusal test_a_patch_that_withdraws_ordered_is_refused
F7: shift(<predicate>, along=d) with d unordered and not carried by the predicate named the order first the carries-along check comes first test_a_where_shift_along_a_dimension_its_predicate_lacks_is_refused_for_that_first
F8: Namespace.unordered had a dead branch for an undeclared name removed none; no caller passes an undeclared name

Mutation: the narrowing guard replaced by if False: fails test_a_patch_that_withdraws_ordered_is_refused and nothing else (1 failed, 2698 passed).

Judged wrong, not changed:

  • F9 (fixtures step along an unordered dimension / the default is not covered): the fixtures do step along generator, g and h, which they declare ordered; the default is covered by UNORDERED in tests/test_validation.py.
  • F7, two other claims. One refusal per call is the house pattern, so a call refused for its shape is not also refused for its order. The sos elif chain skips a claimed set consistently.
Update to main (2026-10-02)

origin/main (b6c2861, with #664, #834 and #835) is merged in. python -m tools.pypsa_split split examples/pypsa regenerates the fragments from examples/pypsa.yaml, which declares period ordered. The changelog line reads feat(language)!:. #821 adds shift(…, along=period) masks to the PyPSA files; whichever of it and this PR lands second declares the dimensions it steps along as ordered.

Gates

On 9595463:

  • pixi run lint: passes.
  • pixi run ci (lint, test, docs-build, compile-tex): passes, 2699 tests.
  • python -m tools.schema, python -m tests.typesetting.golden, python -m tools.pypsa_split split examples/pypsa and the five page generators: no diff.
Deliberately not done, and departures
  • docs/about/limits.md gains no entry. It lists which words may enter the language; ordered is a rule a file obeys. Its one link to "the ten rules" now reads "the eleven rules", because the anchor moved with the new row and docs-build --strict refuses a stale anchor. Nothing else on that page changed.
  • pypsa_split still writes ordered: true in every fragment that declares snapshot or period, also where the fragment does not step along it. With the union rule only the fragments that step along it need it. That is cosmetic, and filed separately.
  • The typesetter prints nothing new. The order already shows in the math a construct prints (t-1, the window).
  • No default by dtype. An int dimension may number things with no order.
  • No check that an ordered dimension is used in order. A consumer such as energy-models/datarecord may read the claim with no operator stepping along it.

A dimension declares `ordered: true` where the order of its members is
part of the model; the default is false. shift, sum_back, position(), a
piecewise curve's `over:` and a type-2 set's `along:` read that order,
and the loader refuses each along a dimension that is not ordered,
naming the declaration to add. `ordered` is written only where true.

Every example, test spec and docs block that steps along a dimension
now declares it ordered; the generated pages, the schema and the golden
output are regenerated.

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

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

Copy link
Copy Markdown

claude added 2 commits October 2, 2026 21:49
… splitter writes it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011a8N2JQdmmd1WhVCKN1a2A
@FBumann FBumann changed the title feat(language): a construct that reads the order of a dimension needs the dimension declared ordered feat(language)!: a construct that reads the order of a dimension needs the dimension declared ordered Oct 2, 2026
claude and others added 6 commits October 2, 2026 22:29
#834 gave examples/pypsa/security.yaml a period dimension; the regenerated
fragment declares it ordered, as examples/pypsa.yaml does.
…h may add it but not withdraw it

A patch restatement is compared as the schema reads both sides, so a written ordered: false equals an omitted one.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: data contract What a file guarantees about the data it binds v0.3.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A dimension declares whether its order has meaning

3 participants