Skip to content

feat(book): section-level scope for proof:* / figure / equation auto-prefix#28

Merged
mmcky merged 2 commits into
mainfrom
feature/book-proof-scope
May 14, 2026
Merged

feat(book): section-level scope for proof:* / figure / equation auto-prefix#28
mmcky merged 2 commits into
mainfrom
feature/book-proof-scope

Conversation

@mmcky
Copy link
Copy Markdown

@mmcky mmcky commented May 14, 2026

Summary

Closes #27. Adds a scope option to NumberingItem so authors can opt kinds into LaTeX \newtheorem{...}[section]-style numbering when numbering.book: true.

project:
  numbering:
    book: true
    proof:
      scope: section          # theorems, lemmas, … render as 5.1.1, 5.1.2, 5.2.1
  • Counter resets on each new heading_2 (or heading_3 for subsection, etc.).
  • Proofs before the first ## Section render literal 5.0.1 to match the LaTeX PDF (avoids collision with later 5.1.x).
  • Resolution order: per-kind override (e.g. numbering.proof:theorem.scope) → numbering.proof.scope umbrella for the proof family → numbering.all.scopechapter (today's default).
  • Applies to every auto-prefix kind: figure, equation, table, exercise, proof:*. Default chapter preserves today's behaviour exactly.
  • Validator normalises chapter/section/subsection to heading_N.
  • continue: true still wins over scope (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.ts incrementCount, which #22 introduced as the auto-prefix hook. When upstreaming to jupyter-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 printed Theorem 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 literal 5.0.1, Alph chapter (appendix), heading_2 alias, subsection (heading_3) scope, continue: true opt-out wins, and figures picking up numbering.all.scope. All 81 enumerate tests pass.
  • packages/myst-frontmatter — 3 new YAML golden cases (numbering.yml) for alias normalisation, heading_N direct form, invalid-scope warning. All 502 frontmatter tests pass.
  • packages/myst-cli — 285 existing tests still pass (no behaviour change when scope is absent).
  • tsc clean for both packages.

CI note

The single CI failure is the Concurrent execution end-to-end test, which runs myst build --site --execute --execute-parallel 2 with 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 and enumerate.ts only — nothing in the Jupyter execution path. A re-run should resolve.

🤖 Generated with Claude Code

…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>
Copilot AI review requested due to automatic review settings May 14, 2026 23:10
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread packages/myst-transforms/src/enumerate.ts Outdated
Comment thread packages/myst-transforms/src/enumerate.ts Outdated
Comment thread packages/myst-frontmatter/src/numbering/types.ts Outdated
- 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>
@mmcky mmcky merged commit 80bb9ec into main May 14, 2026
7 of 10 checks passed
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>
@mmcky mmcky deleted the feature/book-proof-scope branch May 15, 2026 02:07
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.

Book mode: support section-level prefix for proof:* enumerators (LaTeX-style chapter.section.counter)

2 participants