feat(book): section-level scope for proof:* / figure / equation auto-prefix#28
Merged
Conversation
…prefix
Adds a `scope` option to NumberingItem so authors can opt kinds into
LaTeX `\newtheorem{...}[section]`-style numbering. With
`numbering.proof.scope: section` and `numbering.book: true`, theorems
render as `5.1.1`, `5.1.2`, `5.2.1` (chapter.section.counter) and the
per-kind counter resets on each new heading_2. Pre-section proofs
render the literal `5.0.1`, matching the LaTeX PDF rather than the
trailing-zero-stripped `5.1` (which would collide with later `5.1.x`
numbers).
Resolution order for a kind's scope: per-kind override → `proof`
umbrella (for proof family) → `numbering.all.scope` → `chapter`
(today's default). Applies to every auto-prefix kind — figure,
equation, table, exercise, proof:*. Accepted values: `chapter` /
`section` / `subsection` / `heading_1`..`heading_6`.
Closes #27.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds an opt-in scope setting to book-mode numbering so auto-prefixed kinds (especially proof:*) can use LaTeX-style section-/subsection-scoped prefixes (e.g. 5.1.1) and reset counters at the chosen heading boundary, while preserving the current chapter-scoped default.
Changes:
- Implement scope resolution + scoped prefix formatting/reset logic for book-mode auto-prefix kinds in
ReferenceState.incrementCount. - Extend frontmatter numbering validation/types to accept and normalize
scope(chapter/section/… →heading_N). - Add unit + YAML golden tests covering scope behavior, overrides, aliases, and invalid values.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/myst-transforms/src/enumerate.ts | Adds scope resolution and scoped auto-prefix/reset behavior for book-mode numbering. |
| packages/myst-transforms/src/enumerate.spec.ts | Adds unit tests for section/subsection scoping, overrides, and edge cases. |
| packages/myst-frontmatter/src/numbering/validators.ts | Validates + normalizes scope values and emits warnings for invalid scope. |
| packages/myst-frontmatter/src/numbering/types.ts | Documents and exposes the new NumberingItem.scope option. |
| packages/myst-frontmatter/src/numbering/numbering.yml | Adds golden cases for scope normalization and warnings. |
| .changeset/book-proof-scope.md | Declares patch releases and summarizes the new scope feature. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Scope-change reset no longer fires on first encounter of a kind, so
`numbering[kind].start` seeded by initializeTargetCounts survives.
Regression test confirms `figure: { start: 5, scope: section }` now
renders `1.1.5`, `1.1.6`, `1.2.1` instead of `1.1.1, 1.1.2, 1.2.1`.
- Hoist `SCOPE_ALIASES` + `scopeAliasToDepth` into
`myst-frontmatter/numbering/validators.ts` and consume from
`myst-transforms/enumerate.ts`. Single source of truth for accepted
scope spellings; the validator-side normalisation and the enumerate-
side depth lookup no longer drift.
- Tighten `NumberingItem.scope` from `string` to a `NumberingScope`
string-literal union so invalid spellings fail at compile time.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4 tasks
mmcky
added a commit
that referenced
this pull request
May 15, 2026
…ERSION.yml Two tracker files now record orthogonal facts, cross-referenced by squash-commit SHA: - VERSION.yml answers "what's in our main?" — diagnostic identifier for the integration build. Drops its `upstream:` block (was an always-`pending` placeholder). - UPSTREAM-PRS.yml answers "how do we plan to ship those squash commits upstream?" — bundles related squashes into logical upstream PR candidates (so a feature spanning multiple commits, or two features that form one upstream story, can be tracked as a single unit), records dependency order for cherry-pick, tracks status. Seeds UPSTREAM-PRS.yml with three candidates: myst-to-ipynb (#16), book-mode-with-section-scope (#22 + #28, bundled — they form one coherent upstream story since #28 builds on #22's auto-prefix hooks), and book-parts (#26, planned). Adds the missing book-proof-scope entry to VERSION.yml's merged_features. README updated to partition the two files' roles and reference UPSTREAM-PRS.yml from the cherry-pick workflow. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
mmcky
added a commit
that referenced
this pull request
May 15, 2026
* docs(quantecon): cherry-pick upstream model, batched tags
Replace the "preserve feature branch, push as upstream PR" workflow
with "delete feature branch after merge; cherry-pick main squash
commits onto a fresh branch off upstream/main when upstreaming."
Why:
- Feature branches that depend on prior QuantEcon-only features were
awkward to branch from upstream/main (had to carry the dependency
inline or wait for it to upstream first).
- Branching from main lets dependencies just work; the squash commit
on main is already the upstream-ready artifact.
- Cherry-picking at upstream-PR time lets us bundle related squashes
into a coherent story ("book mode + section scope") when that's the
right unit for upstream review, or split them.
Tagging also moves from per-PR to batched: a `qe-vN` tag is cut at a
checkpoint covering multiple features, not on each merge.
Updates the diagram, branching-model table, "Develop a new feature",
"Opening an upstream PR" (now cherry-pick-driven), "When upstream
merges", "Resolving merge conflicts between features", and the
VERSION.yml comment block. Adds an `upstream/<topic>` branch type to
the branching model.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs(quantecon): add UPSTREAM-PRS.yml; split upstream tracking from VERSION.yml
Two tracker files now record orthogonal facts, cross-referenced by
squash-commit SHA:
- VERSION.yml answers "what's in our main?" — diagnostic identifier
for the integration build. Drops its `upstream:` block (was an
always-`pending` placeholder).
- UPSTREAM-PRS.yml answers "how do we plan to ship those squash
commits upstream?" — bundles related squashes into logical upstream
PR candidates (so a feature spanning multiple commits, or two
features that form one upstream story, can be tracked as a single
unit), records dependency order for cherry-pick, tracks status.
Seeds UPSTREAM-PRS.yml with three candidates: myst-to-ipynb (#16),
book-mode-with-section-scope (#22 + #28, bundled — they form one
coherent upstream story since #28 builds on #22's auto-prefix hooks),
and book-parts (#26, planned). Adds the missing book-proof-scope
entry to VERSION.yml's merged_features.
README updated to partition the two files' roles and reference
UPSTREAM-PRS.yml from the cherry-pick workflow.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* docs(quantecon): address Copilot feedback on PR #30
- VERSION.yml header: remove double-accounting in the tag-cut steps.
Per-PR landing already appends each feature to merged_features with
tag: null; the tag-cut workflow just *sets* the tag field on those
pre-existing entries and bumps qe_version. The old wording said
"Append each newly-included feature" which contradicted the
per-PR-landing description below it.
- README "About this folder": the allow-list now contains README.md,
VERSION.yml, UPSTREAM-PRS.yml, and .gitignore itself — the original
text only mentioned the first two.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
mmcky
added a commit
that referenced
this pull request
May 15, 2026
Smoke test confirming PR #26 and PR #28 operate at independent layers: parts are a TOC artefact (fromTOC.ts); scope is an in-page counter (enumerate.ts). The two features have no file overlap and no shared state, so they should compose naturally. A future change that accidentally couples them (e.g. by reading scope from inside fromTOC) would silently regress book-dp2-style projects — pin the expected output now so any such regression is caught at the TOC layer. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
mmcky
added a commit
that referenced
this pull request
May 15, 2026
Bumps qe_version to qe-v3 and sets `tag: qe-v3` on the book-proof-scope entry. The git tag qe-v3 will be cut on the squash commit of this PR so that `cat VERSION.yml` at the tag is self-consistent (qe_version matches the tag). Per quantecon/README.md: "Doing it in this order means anyone who `cat`s VERSION.yml at tag qe-v<N+1> sees a self-consistent file." Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
mmcky
added a commit
that referenced
this pull request
May 15, 2026
Bumps qe_version to qe-v3 and sets `tag: qe-v3` on the book-proof-scope entry. The git tag qe-v3 will be cut on the squash commit of this PR so that `cat VERSION.yml` at the tag is self-consistent (qe_version matches the tag). Per quantecon/README.md: "Doing it in this order means anyone who `cat`s VERSION.yml at tag qe-v<N+1> sees a self-consistent file." Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
mmcky
added a commit
that referenced
this pull request
May 15, 2026
* feat(book): section: parts dividers with Roman labels
Extends BookSection with 'parts' and wires the structural-divider
behaviour through fromTOC:
- `section: parts` ParentEntry emits a LocalProjectFolder at part level
(-1) with a pre-formatted title ("Part I — Theory")
- Children of a parts subtree default to `section: chapters` (the dp2
convention) and bump to chapter level (0)
- Project-level `numbering.parts.{format,label}` override the Roman /
"Part %s" defaults
- TOC auto-detects parts and starts at level=-1 so chapter pages land
at heading_1, not heading_2
- Existing TOCs without parts keep their level=1 start (regression
test included)
Chapter counter continues across part boundaries (Ch 5 → Part II →
Ch 6) — verified end-to-end in quantecon/demo-book-parts. This is the
LaTeX `book` default and matches dp2's numbering.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* fix(book): address Copilot PR feedback on parts implementation
Three review findings, all fixed:
1. types.ts: "Roman-numeralled" → "Roman-numbered" (typo in doc comment)
2. fromTOC.ts hasPartsSubtree(): restrict detection to non-file
ParentEntry so it matches the emission predicate exactly. A
FileEntry/URLEntry tagged with section: parts is malformed (a
divider can't be a file) and gets downgraded to section: chapters
without emitting a folder — previously this still shifted the top-
level start level to -1, producing off-by-one levels for the rest
of the TOC. Added a regression test asserting the rogue file stays
at level 1 and no parts folder is emitted.
3. fromTOC.parts.spec.ts basename(): use path.basename from node:path
instead of splitting on '/' so the test passes on Windows where
memfs/resolveExtension use backslash separators.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
* test(book): parts compose with numbering.proof.scope: section
Smoke test confirming PR #26 and PR #28 operate at independent layers:
parts are a TOC artefact (fromTOC.ts); scope is an in-page counter
(enumerate.ts). The two features have no file overlap and no shared
state, so they should compose naturally. A future change that
accidentally couples them (e.g. by reading scope from inside fromTOC)
would silently regress book-dp2-style projects — pin the expected
output now so any such regression is caught at the TOC layer.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
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.
Summary
Closes #27. Adds a
scopeoption toNumberingItemso authors can opt kinds into LaTeX\newtheorem{...}[section]-style numbering whennumbering.book: true.heading_2(orheading_3forsubsection, etc.).## Sectionrender literal5.0.1to match the LaTeX PDF (avoids collision with later5.1.x).numbering.proof:theorem.scope) →numbering.proof.scopeumbrella for the proof family →numbering.all.scope→chapter(today's default).figure,equation,table,exercise,proof:*. Defaultchapterpreserves today's behaviour exactly.chapter/section/subsectiontoheading_N.continue: truestill wins overscope(matches §3.4(6) opt-out — keeps the kind globally flat).Dependency
Builds on the auto-prefix machinery shipped in #22 ("Book-style numbering: format, label, section-tagged TOC, auto-prefix"). Specifically extends
packages/myst-transforms/src/enumerate.tsincrementCount, which #22 introduced as the auto-prefix hook. When upstreaming tojupyter-book/mystmd, #22 must land first.Motivation
From testing of qe-v2 against QuantEcon/book-dp1: the LaTeX PDF is the canonical reference and prints
Theorem 1.2.1(chapter.section.theorem), but qe-v2 MyST printedTheorem 1.1(chapter.theorem). Body prose like "see Theorem 5.1.2" then no longer agrees with the rendered enumerator. This adds the missing opt-in.Test plan
packages/myst-transforms— 8 new unit tests in enumerate.spec.ts covering: section-scoped multi-section reset, per-kind override of proof umbrella, pre-section literal5.0.1, Alph chapter (appendix),heading_2alias,subsection(heading_3) scope,continue: trueopt-out wins, and figures picking upnumbering.all.scope. All 81 enumerate tests pass.packages/myst-frontmatter— 3 new YAML golden cases (numbering.yml) for alias normalisation,heading_Ndirect form, invalid-scope warning. All 502 frontmatter tests pass.packages/myst-cli— 285 existing tests still pass (no behaviour change whenscopeis absent).tscclean for both packages.CI note
The single CI failure is the
Concurrent executionend-to-end test, which runsmyst build --site --execute --execute-parallel 2with a 15s timeout for two Jupyter notebook kernels. It's a known timing-flaky test (file comment: "Used to be 5000, increased due to CI slowness") and has failed similarly on recent unrelated PRs (#20, #21). This PR touches the numbering schema andenumerate.tsonly — nothing in the Jupyter execution path. A re-run should resolve.🤖 Generated with Claude Code