spec: rule the two-caption collision the wrapper clause left open - #1236
Merged
Conversation
A `table` carries an optional `caption`, so a `figure` targeting a captioned table is a schema-valid tree with two captions and one `^ ` slot. The clause said the writer emits "the target and its caption" without saying whose, which is only unambiguous when the table has none. Measured on carve-js. With the slot free the figure's caption fills it and the output reparses as a `table`. With the slot taken the writer emits a second `^ ` line, and that line is not a caption: a `^ ` line after a caption line is an ordinary paragraph, so the output carries "Outer" as literal body prose and renders it inside a `<p>`. That is a worse outcome than losing the wrapper, and it is the outcome this clause exists to keep a writer from discovering on its own. The rule follows from what the clause already says rather than adding a new decision: the wrapper is lost, and the figure's caption is part of the wrapper, so it goes with it and the writer emits no second line. Losing it cleanly keeps the loss observable through the diagnostic; spelling it as prose hides it in the output.
dereuromark
force-pushed
the
spec/two-caption-collision
branch
from
August 16, 2026 09:25
f969118 to
e7787d4
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.
Split out of
markup-carve/carve#1211so the descriptive part of that PR can land without carrying a normative decision with it.#1211 documents that a
figuremay wrap atableand that no Carve source spells it. This is the case that clause left open, and it is normative rather than descriptive, which is why it is separated.The collision
A
tablecarries an optionalcaption, sofigure{ target: table{ caption }, caption }is a schema-valid tree with TWO captions and one^slot to write them into. The wrapper clause says the writer emits "the target and its caption" without saying whose, which is unambiguous only while the table has none.What happens today, measured on carve-js
With the slot free, the figure's caption fills it and the output reparses as a
table. With the slot taken, the writer emits a second^line - and that line is not a caption. A^line following a caption line is an ordinary paragraph, so the outer caption lands in the output as literal body prose:That is worse than losing the wrapper. Losing the wrapper is observable through the diagnostic the clause already requires; spelling the caption as prose hides the loss inside the rendered output, where nothing reports it and a reader sees a stray line of text.
Why this follows rather than decides
The rule is that the figure's caption is lost with the wrapper, and a writer must not emit a second
^line. That is what the clause already says applied one step further: the wrapper is lost, the figure's caption is part of the wrapper, so it goes with it.The one thing worth your explicit nod
It is still a normative sentence, and carve-js currently violates it - that is how the behavior above was measured. Merging this creates a conformance gap that carve-js has to close. If you would rather state it as a note than as a MUST, say so and it becomes one; the argument for the behavior does not change either way.
Nothing else in the spec is touched: one 12-line addition to
resources/grammar.ebnf, in the clause #1211 adds.