Skip to content

Move the engine pin past composite figures and cell attributes - #66

Merged
dereuromark merged 1 commit into
mainfrom
chore/composite-figures-composite-sat-6436a4ec
Aug 15, 2026
Merged

Move the engine pin past composite figures and cell attributes#66
dereuromark merged 1 commit into
mainfrom
chore/composite-figures-composite-sat-6436a4ec

Conversation

@dereuromark

Copy link
Copy Markdown
Contributor

Part of markup-carve/carve#1220 (secondary-engine tracker); spec markup-carve/carve#1215, design markup-carve/carve#1122.

There is no port to write here

The tracker's checklist line for this repository reads "port parser/renderers/writer per section 4c", which describes a native engine. This gem is a binding: ext/carve is magnus over carve-rs, pinned to an exact revision in ext/carve/Cargo.toml with the lockfile committed beside it. A construct the engine gains arrives here when the pin moves, and no Ruby change reaches it before that.

Both constructs have landed upstream: markup-carve/carve-rs#986 (PART 9 section 4c, composite figures) and markup-carve/carve-rs#996 (a cell's attributes bind after its kind and alignment markers).

The change

The pin moves from 8dada737 to 98de7874 (carve-rs main), in ext/carve/Cargo.toml and ext/carve/Cargo.lock. EXPECTED in test/corpus_ast_types_test.rb records the new figure_group node type. The rest is the CHANGELOG.

Measured

Against carve 1436f39a, extension recompiled for each row, 1025 corpus pairs:

pin diverging
8dada737 (main) 13
98de7874 0

The 13 were nine 318-composite-figures* documents and four 319-cell-attributes-* documents. All eleven 318-composite-figures documents and all six 319-cell-attributes-bind-after-the-kind-and-alignment-markers documents now match the spec. Nothing is allowlisted or skipped; this repository has no allowlist and does not gain one.

Proof, both directions

At the old pin, 318-composite-figures-4:

::: figure
![lone](l.png)
^ The only panel
:::
^ Figure #: One panel is valid

rendered the pre-section-4c shape, a generic container with the caret line surviving as a paragraph:

<div class="figure">
  <figure>
    <img src="l.png" alt="lone">
    <figcaption>The only panel</figcaption>
  </figure>
</div>
<p>^ Figure #: One panel is valid</p>

At the new pin it renders what the spec pins, and so do the other twelve.

The control now discriminates

318-composite-figures-8 is the control for the clause: an opener carrying a title or a [label] must stay a generic container rather than become a figure group. At the old pin it could not have failed - the engine had no rule separating a bare opener from a decorated one, which the corpus shows directly, since every diverging bare-opener document rendered the same <div class="figure"> the control asserts.

At the new pin the distinction is real. The document as written:

<div class="figure">
  <p class="admonition-title">A titled figure div</p>
  <figure>
    <img src="a.png" alt="one">
    <figcaption>(a) One</figcaption>
  </figure>
</div>
<p>^ Not a group caption</p>
<div class="figure">
  <p class="div-label">g</p>
  <p>Body.</p>
</div>

and with the title and the [label] stripped, so both openers are bare:

<figure class="carve-figure-group">
  <div class="carve-figure-panels">
    <figure class="carve-figure-panel">
      <img src="a.png" alt="one">
      <figcaption>(a) One</figcaption>
    </figure>
  </div>
  <figcaption>Not a group caption</figcaption>
</figure>
<figure class="carve-figure-group">
  <div class="carve-figure-panels">
    <p>Body.</p>
  </div>
</figure>

Different element, different class, and the caret line is consumed as the group caption.

Why EXPECTED grows

corpus_ast_types_test.rb keeps a recorded list of node types the corpus produces. The assertion is one-directional by design: a recorded type going missing fails, a new type only warns. This run warned, naming figure_group.

Recording it tightens the check rather than widening it. The old pin produced no unrecorded type at all, so this line would have failed the suite before the bump; from here on, a pin that drops the group node fails with the type named instead of leaving thirteen documents to explain it.

CHANGELOG placement

Both entries go under the existing Unreleased heading, which already states that it argues from v0.1.0 - the only released tag, since v0.1.1 is a draft. No version field is touched.

This gem has no parser. ext/carve is a magnus binding over carve-rs, pinned to
an exact revision, so a construct the engine gains arrives here when the pin
moves and not before. Thirteen corpus documents diverged for that reason alone.

Both constructs have landed upstream: markup-carve/carve-rs#986 (PART 9 section
4c, composite figures) and markup-carve/carve-rs#996 (a cell's attributes bind
after its kind and alignment markers).

The pin moves from 8dada737f608da124e06a1f7e43f962f98a3e68a to
98de7874ad2e81f69e57764562c83f4918522ac2, in ext/carve/Cargo.toml and
ext/carve/Cargo.lock together.

Measured against carve 1436f39a, extension recompiled each time, 1025 corpus
pairs:

  at 8dada737   13 diverging
  at 98de7874    0 diverging

The thirteen were the nine `318-composite-figures*` documents and the four
`319-cell-attributes-*` documents that were failing; all eleven of the former
and all six of the latter now match the spec.

EXPECTED in test/corpus_ast_types_test.rb gains `figure_group`. That list is a
one-directional check: a recorded type going missing fails, a new one only
warns, and this run warned. Recording it is what makes the type load-bearing -
if a later pin drops the group node, the suite says which type went rather than
leaving thirteen documents to explain it. It is a tightening, not a widening:
the old pin produced no unrecorded type at all, so this line would have failed
the suite before the bump.

The control for the clause, `318-composite-figures-8`, asserts that an opener
carrying a title or a `[label]` stays a generic container. At the old pin it
could not have failed - the engine had no rule separating a bare opener from a
decorated one, so both spellings rendered `<div class="figure">`, which is what
every diverging bare-opener document showed. At the new pin the mutation that
should break it does. Stripping the title and the `[label]` so both openers are
bare turns

    <div class="figure">
      <p class="admonition-title">A titled figure div</p>
      <figure>
        <img src="a.png" alt="one">
        <figcaption>(a) One</figcaption>
      </figure>
    </div>
    <p>^ Not a group caption</p>

into

    <figure class="carve-figure-group">
      <div class="carve-figure-panels">
        <figure class="carve-figure-panel">
          <img src="a.png" alt="one">
          <figcaption>(a) One</figcaption>
        </figure>
      </div>
      <figcaption>Not a group caption</figcaption>
    </figure>

Nothing is allowlisted or skipped. The CHANGELOG entries go under the existing
Unreleased heading, which already argues from v0.1.0, the only released tag.
@dereuromark
dereuromark merged commit 1827b25 into main Aug 15, 2026
2 checks passed
@dereuromark
dereuromark deleted the chore/composite-figures-composite-sat-6436a4ec branch August 15, 2026 23:38
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.

1 participant