Skip to content

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

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#34
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 crate is a wasm-bindgen wrapper around carve-rs, pinned to an exact revision in Cargo.toml with Cargo.lock committed beside it. A construct the engine gains arrives here when the pin moves, and not before.

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 6f1d9bfb to 98de7874 (carve-rs main). Cargo.lock follows, and picks up pulldown-cmark and unicase, which the engine now depends on. That is the whole diff: this repository keeps no CHANGELOG, and the README deliberately does not quote the revision.

Measured

Against carve 1436f39a, artifact rebuilt with wasm-pack build --target nodejs for each row and driven through the JS API, which is what tests/corpus.mjs does:

pin diverging
6f1d9bfb (main) 94 of 1025
98de7874 0 of 1025

The pin was far enough behind that the 94 span most of the table work (a header cell carrying scope, cell attributes, alignment, span markers, continuation rows), attribute parsing, footnote and reference resolution, semantic spans, and the language attribute. Thirteen of them are the two constructs this bump was taken for: nine 318-composite-figures* documents and four 319-cell-attributes-*.

All eleven 318-composite-figures documents and all six 319-cell-attributes-bind-after-the-kind-and-alignment-markers documents now match the spec. smoke.mjs and the AST schema check pass unchanged. Nothing is allowlisted; 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 does every other pair.

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, and the corpus shows why directly - -8 passed while every bare-opener document diverged by rendering the same <div class="figure">. The engine had no rule separating a bare opener from a decorated one, so the assertion had nothing to catch.

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 the same document 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 instead of surviving as a paragraph.

This crate wraps carve-rs and exposes it through wasm-bindgen; it has no parser
of its own. The pin had not moved since 6f1d9bfb, and the corpus measured the
cost: 94 of 1025 documents rendered something other than what the spec pins.

The pin moves to 98de7874ad2e81f69e57764562c83f4918522ac2 (carve-rs main), in
Cargo.toml and Cargo.lock together. Cargo.lock also gains pulldown-cmark and
unicase, which the engine now depends on.

Measured against carve 1436f39a, artifact rebuilt with `wasm-pack build
--target nodejs` for each row and driven through the JS API, which is what
tests/corpus.mjs does:

  at 6f1d9bfb   94 diverging
  at 98de7874    0 diverging

The 94 are dominated by tables (a header cell carries `scope`, cell attributes,
alignment, spans, continuation rows), attribute parsing, footnote and reference
resolution, semantic spans, and the language attribute. Thirteen of them are the
two constructs this bump was taken for: nine `318-composite-figures*` documents
and four `319-cell-attributes-*`. All eleven `318-composite-figures` documents
and all six `319-cell-attributes` documents now match.

Composite figures are PART 9 section 4c, landed upstream as
markup-carve/carve-rs#986; the cell-attribute order is
markup-carve/carve-rs#996.

The clause's control, `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, which the corpus shows directly - `-8` passed while every bare-opener
document diverged by rendering the same `<div class="figure">`. At the new pin
the distinction is real. Stripping the title and the `[label]` from `-8`, so
both openers are bare, changes the production:

    <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>

becomes

    <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; this repository has no allowlist and does not gain one.
smoke.mjs and the AST schema check pass unchanged.
@dereuromark
dereuromark merged commit 6155f1f into main Aug 15, 2026
3 checks passed
@dereuromark
dereuromark deleted the chore/composite-figures-composite-sat-6436a4ec branch August 15, 2026 19:06
dereuromark added a commit that referenced this pull request Aug 15, 2026
EXPECTED_TYPES in tests/corpus.mjs is this crate's own record of what the
language emits, and the assertion over it is one-directional: a recorded type
going missing fails, an unrecorded new type is invisible. The pin bump that
brought PART 9 section 4c in (#34) added a node type nothing here names, and
nothing in this file would have said so.

Recording it is a tightening, not a widening. `figure_group` appears in no
carve-rs source file at the revision this crate pinned before that bump
(6f1d9bfb, zero hits under src) and in eleven at the one it pins now
(98de7874), so this line would have failed the suite before the bump and passes
after it. From here on, a pin that drops the group node fails with the type
named rather than leaving nine corpus documents to explain it.

The two ablations beside the assertion still hold: the sweep refuses a type no
engine emits, and requires the produced set to be at least as large as the
record.

Measured with CARVE_SPEC_CORPUS against carve 1436f39a, artifact rebuilt with
`wasm-pack build --target nodejs`: 1025/1025 byte-identical, 58 node types,
field names match the schema, smoke green.
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