Conversation
…re that release does not have (#305) `effect_limit` and priced effects are not in pypsa 1.3.0 — no Effect component, no effect_limit in global_constraints, no effects module (checked against the 1.3.0 tag). The row tracked a later PyPSA feature in a table pinned to 1.3.0, where it read as an open milestone gap. It is out of scope by version, not by decision, so it leaves the table rather than becoming an `out` row. Claude-Session: https://claude.ai/code/session_016jk5LAMCoiD39q4Xz4AMVw Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
… data-prep row used to answer for it (#272) Claude-Session: https://claude.ai/code/session_01YbYPoWmvwpkRmYeFvgW5jm Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
…e, not each consumer's to re-derive (#313) * feat(program): the conjuncts of a where mask are the program's to give, not each consumer's to re-derive A program's resolved `where` is a WhereNode, and the AND spine the grammar left-folds nests, so a consumer wanting the predicates one at a time had to flatten it and decide where the flatten stops. `conjuncts()` gives them: it flattens the AND spine and stops at an OR or a NOT, so the split has one home and two consumers cannot disagree on what a conjunct is. This is the language-side half of #312 — the model giving the conjuncts. The sweep that asks which conjunct the corpus never makes false needs lpspec's per-rung record and belongs there, beside the block-level parity.coverage() it extends. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SV5dfu6ZGBULW21x8GcN2g * test(parser): rename a conjuncts case id the typo check read as a misspelling Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SV5dfu6ZGBULW21x8GcN2g * style: reword the conjuncts docstrings the typo check read as a misspelling 'ANDs together' in program.conjuncts and reading.md is 'joins with AND' now — typos reads 'ANDs' as 'and', and the word is rewordable, so it is reworded rather than added to the extend-words list kept for words that are not. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jk5LAMCoiD39q4Xz4AMVw --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
…being one, and claims neither linearity nor a storage format (#315) Three claims a consumer reads and acts on. `Program` carries no data, so it is a description rather than the thing; the objective has taken degree 2 since the quadratic stack, so `linear` had been false for a while; and `over named tidy tables` named a storage format the language does not decide — one program feeds a backend keeping frames and one keeping dense arrays alike. Claude-Session: https://claude.ai/code/session_01TZbCoSM1Ah7YWg6K4Ps5sU Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
…rry their dims, and the where grammar is package-private (#327) Claude-Session: https://claude.ai/code/session_01D59sEp5d7CecJAwrzACbRh Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
…ss (#329) Every expression node is frozen like the where and program vocabularies already were; a call's args are a tuple and its kwargs a copied read-only view, excluded from the hash because a mapping has none. Refs #326. Claude-Session: https://claude.ai/code/session_01Q9qaMS43V6yaNApJPmuWtM Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
… the grammar's complaint (#332) The predictable mistakes — a strict or chained comparison, !=, a lone =, ^ for power — are diagnosed from the token at the parse failure, with the grammar's own message kept beneath. Refs #326. Claude-Session: https://claude.ai/code/session_01Q9qaMS43V6yaNApJPmuWtM Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
…sed, and the refusal says what actually breaks (#336) The rule landed in #327 with no home on the reference page a file's author reads, and its wording framed the cases as ordered. Cases are kept apart by proof rather than ranked, so an always-true arm is not one that shadows the arms under it — it is one no other arm can be proved apart from, leaving `otherwise:` nothing. The refusal says that, out of a named helper whose docstring carries the reason. `expressions.md` gains the rule beside the other two the cases are held to, quoting the refusal verbatim, and says why a declaration's `where:` is not held to it. The where-string table's literal row gains the double negation the fold also cancels. Claude-Session: https://claude.ai/code/session_01BJfy8K5XfxLmX5Tq5YW71b Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
…rrier it actually has (#334) Three sentences the Mask move left pointing at things that are no longer there: - `exclusivity.py` cited `dimensions.py` and the typesetter for its unresolved-node guard; both walks are gone, and the live one is `program._atoms`, which `Mask` exhausts at construction. - `where_of` claimed a Mask is the only shape a resolved where travels in past resolution; a case arm's `when` travels as a bare node through the AST, and lowering is what wraps it. - `_arm_mask` returns that node, not a Mask, so it is `_arm_when`. Claude-Session: https://claude.ai/code/session_01BJfy8K5XfxLmX5Tq5YW71b Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…that walk (#338) `Mask.__post_init__` already walks the whole tree — building the atom tuple is how it refuses an unresolved leaf — and then threw the tuple away, so `atoms`, `dims` and `names_read` each walked it again on every access. `atoms` becomes a `cached_property`, filled by the walk construction takes anyway, and `dims` and `names_read` read it instead of re-walking the root. One home for "the mask's leaves", and the construction cost is unchanged. The fold has to run before that walk: `x AND False` folds to `False` and drops `x`'s leaf, so a tuple held from before it would answer with a leaf the root no longer carries. `test_a_held_leaf_walk_is_taken_after_the_fold_absorbed_a_branch` fails when the two lines are swapped. Claude-Session: https://claude.ai/code/session_01BJfy8K5XfxLmX5Tq5YW71b Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…ping one in a Mask is a type error (#341) `parse_where` cast its result to `WhereNode` and its docstring explained that the annotation was the type the tree has only *after* resolution. Two more docstrings then leaned on that lie to justify a runtime guard. The return type is now `WhereNode | UnresolvedWhereNode`, which resolution already accepted. One site stores such a tree in a field typed as resolved — `_parse_cased`, whose docstring says why — and carries the pragma. The guard in `Mask.__post_init__` stays and its reason narrows: not that the annotation over-claims, but that a consumer runs none of our checks over its own code. `program._atoms`, `Mask` and the test that pins the refusal say that instead. Claude-Session: https://claude.ai/code/session_01BJfy8K5XfxLmX5Tq5YW71b Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
…refused, rather than loading unreferenceable (#340) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
…sion fails CI instead of scrolling past (#325) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nchecked Any cannot reach a caller (#330) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…n replaying the whole history (#344) Claude-Session: https://claude.ai/code/session_01Dnv7pdeso3eJNpnikcvVs6 Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…me the values they can be, rather than being a string (#345) Claude-Session: https://claude.ai/code/session_01Hdxai54hc4LwxFQBqMM8jF Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
… C connective habits (#346) & and | and ~ and ! (doubled or not) and a lone = are diagnosed from the token at the parse failure, beside the grammar's own complaint — the where twin of #332. Refs #326. Claude-Session: https://claude.ai/code/session_01Q9qaMS43V6yaNApJPmuWtM Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
…values they can be, rather than being a string (#354) Claude-Session: https://claude.ai/code/session_01Hdxai54hc4LwxFQBqMM8jF Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…es, rather than any string a walk happens to ask for (#352) Claude-Session: https://claude.ai/code/session_01Hdxai54hc4LwxFQBqMM8jF Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
Documentation build overview
20 files changed ·
|
…says what a caller needs rather than why (#363) Duplicated rules folded to one home: the comparison-operator set (five spellings), the node-shape walk over the core AST, the parse-failure frame shared by both grammars, the atom walk over a where predicate (Mask), the kwarg-kind dispatch of an operator call, the emitted piecewise names, the open-bound and typeset row/document assembly. Hand-rolled AST recursions now go through program.walk and program.children. Guards that validation already enforces one pass earlier are asserts. Unreachable branches, one-caller abstractions and the retired index() message are gone. Module and public docstrings carry the contract; rationale, history and rejected alternatives are cut. Visible to a consumer: Format.suffix, EdgeNode.policy, Expression.__sub__ and __neg__, degree.is_quadratic are removed; to_spec refuses a list with SchemaError rather than TypeError; overlapping, Symbols, chosen_expressions, expand, parse_and_expand and check_binary take what their caller already holds; several load-time messages are reworded. Claude-Session: https://claude.ai/code/session_01LArQyNe7Kjpryu7YrvvMgq Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…degree question names the tree it walks (#364) * refactor: a built-in's one positional argument is stated once, and a degree question names the tree it walks Builtin.positional was 1 for every operator; the count is now the one line call_shape_error acts on. degree.carries_variable and program.carries_variable answer the same question over two trees, so the three modules reading the degree one call it module-qualified and its docstring names its namesake. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LArQyNe7Kjpryu7YrvvMgq * docs(model): a Spec that exists has passed the whole language, whichever way it was built Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LArQyNe7Kjpryu7YrvvMgq --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ather than state left on the walk (#366) Claude-Session: https://claude.ai/code/session_01LArQyNe7Kjpryu7YrvvMgq Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…me and mask once (#367) Claude-Session: https://claude.ai/code/session_01LArQyNe7Kjpryu7YrvvMgq Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
… dim rule is one function (#368) Claude-Session: https://claude.ai/code/session_01LArQyNe7Kjpryu7YrvvMgq Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…sal names the rule that raised it (#369) Claude-Session: https://claude.ai/code/session_01LArQyNe7Kjpryu7YrvvMgq Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…on carries its claim, and a docstring argues for one assertion (#372) Claude-Session: https://claude.ai/code/session_01LArQyNe7Kjpryu7YrvvMgq Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: energy-models-release-please[bot] <319464627+energy-models-release-please[bot]@users.noreply.github.com>
FBumann
force-pushed
the
feat/separability-a-driver-can-act-on
branch
2 times, most recently
from
September 2, 2026 08:12
8397991 to
8c857fe
Compare
…a time, and what each coordinate needs from its neighbours One dataclass and one attribute beside the Footprint the program already answers: program.separability[dim] says how far a row reads behind and ahead along the axis, which declarations tie it together and the modelling change that would lift each, which reaches only data can decide and the parameter or lookup that decides them, and which masks count a position a window would restart. windowable and independent are the two verdicts a rolling-horizon driver and a scenario sweep ask before cutting a slice. Carries #248 rebased onto alpha.67, where a mask is a Mask and its leaves come off Mask.atoms rather than a walk of its own. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FPBsP3UM6esCBREVmjMEhZ
Contributor
Author
FBumann
force-pushed
the
feat/separability-a-driver-can-act-on
branch
from
September 2, 2026 08:13
8c857fe to
d0fc640
Compare
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.
Three changes to
Separability, read from the consumer that will call it (lpspec.strategy.EachWindow). The shape and placement of #248 are unchanged.halobecomesbehindandahead. A translation reads behind for a positive offset (t - offset) and ahead for a negative one; a trailing window reads behind bywidth - 1. A rolling-horizon driver supplies the two differently — lookahead is rows it solves and does not keep, history is rows it carries from the window before — andabs(reach)could not say which it was in.coupled(a sum in a constraint, a grouping, a wrap, a set, anat()) no window satisfies.undecidednames the parameter or lookup whose values set the reach — a named offset or width, a partition — and a driver holding the data computes it; the same shape ascoupledrefused PyPSA's link delay, which rolls routinely.restartsnames aposition()a window restarts, which a rolling horizon seeding its opening state means.windowableis false while anything is coupled or undecided; a restart does not count against it.reading.mdsaid it could.Two more, from the follow-up questions ("can we say what would make it windowable?", "isn't separability also what a scenario sweep asks?"):
coupledentry names the change that would lift it, after a dash: a horizon total becomes a rollingsum_back(within=n), a wrap an opening-state seed atposition(t) == 0, a grouping is windowed along the dimension it groups into, a set needs a window holding it whole. Named and not applied — every one changes what the model means, and math-spec'sAdvicealready takes that line.at()moves fromcoupledtoundecided, naming the lookup whose values set its reach.independentanswers the other driver: whether each coordinate builds on its own — windowable, reading nothing behind or ahead, counting no position. A scenario sweep asks it before solving one coordinate per slice, and it is what licenses solving slices in any order or at once. A restart counts against it, unlike forwindowable: with one coordinate per slice aposition()holds everywhere, which changes what the mask means.Verified
pixi run lint(lefthook: prettier, ruff),pixi run typecheck(0 errors),pixi run test: 931 passed.reading.mdgains one executed line (the remedy) and the page test's claim count follows. Not run:mkdocs build --strict,compile-tex.Coverage moved: the six coupling cases split into two structural (
coupled), three data-decided (undecided, asserting the name), and one restart (asserting it stays windowable); a negative-offset case, theat()case, fourindependentcases and the remedy wording are new.🤖 Generated with Claude Code
https://claude.ai/code/session_01FPBsP3UM6esCBREVmjMEhZ