Repository navigation
cnd: decorative content yields no footnote or citation - #8
Merged
Merged
Conversation
Content inside pdf.artifact is decorative and never emitted (cnd-sdk spec §2, "Decorative content"). Every element kind under an artifact was already dropped, but footnotes and citations still leaked: - a footnote declared in an artifact became a pool entry, and a kept re-reference to it got an edge; - a footnote re-reference or a citation written in an artifact put an edge on the kept node around it. resolve_footnotes skips declarations and markers located in an artifact; resolve_cite_edges skips citations located in one. decorative_content.typ wraps every element kind, labelled targets referenced from kept text, a decorative footnote numbered before a kept one, a decorative re-reference to the kept note and a decorative citation. The suite test asserts no decorative word reaches the CND, the kept note keeps ordinal 2 and is the only one pooled and linked, and the citing paragraph cites only its kept key. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Closes the remaining gaps of the "Decorative content" rule (cnd-sdk spec §2): - A reference or link written inside an artifact records no marker (extract tracks open artifacts), and the bare-content collectors and the index-correlation fallback skip artifact subtrees (`extract::traverse_kept`, equivalent to `Content::traverse` outside artifacts). Skipping them also keeps a decorative paragraph from shifting the fallback's paragraph indices. - A label on decorative content names nothing: `resolve_label` returns no node for it instead of falling back to the first table figure. - A float written inside an artifact is drawn away from the artifact's tags. The float pre-pass now records whether its `place` sits in an artifact, and what such a float surely holds is decorative: a paragraph by source, any other element by body span and source range. Content reused from a `#let` stays kept where it shows. decorative_content.typ gains decorative references, internal and external links (in a paragraph and a list item), a decorative block referring to a kept figure, a decorative float with a footnote and a list, and a `#let` body shown both kept and in a decorative float. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XxXUiDn2QyzNTYnZpeVFVS
Review found kept text lost with the previous rule. A float built by a helper (`#let f(x) = pdf.artifact(place(..)[#x])`) has its `place` in the helper and its body at the call site, so its source range reaches across the paragraphs in between, and content reused from a `#let` shares its spans with the kept copy. Both made kept paragraphs, lists and tables count as decorative. A float written inside an artifact now counts as decorative only when every span of its body lies inside its own `place` call, and an element is decorative only when its own source range lies inside such a call (a float nested in one included). Anything else is emitted: a helper- built decorative float leaks, kept text is never at stake. decorative_float_helpers.typ pins the review's cases: helper-built decorative floats with kept text around them, a helper used both kept and decorative, a `#let` list and table shown kept and in a decorative float, and a float nested in a decorative one. On the 40 corpus and 10 audit documents the output is unchanged from main (ids aside). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XxXUiDn2QyzNTYnZpeVFVS
Two losses of kept text, found in review: - Content written inside a decorative float's `place` call but shown in the flow (a `metadata` value queried, a `state` read in `context`) has its range inside the call. An element now counts as decorative through such a float only if every span in it — tag elements included — and every open ancestor's range lie inside the call; the `context` that shows it carries a span from outside. - A kept float drawn at the top of a page while a multi-page artifact is still open in the frames was marked decorative. An artifact open on the stack now counts only when it encloses the element in the sense of `Open::encloses` (the float-aware test containers use), for the element itself and for the glyph count of open paragraphs. This loss predates the branch. decorative_multipage.typ pins both. On the 40 corpus and 10 audit documents the output is unchanged from main (ids aside); a 5000- paragraph, 500-float document exports in 1.3 s (main: 1.8 s). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XxXUiDn2QyzNTYnZpeVFVS
Review kept finding kept text lost or decorative text leaked by the span- and range-based float rules: helper-built floats, `#let` content shared between a float and the flow, `context` bodies with detached spans, kept floats drawn while a multi-page artifact is still open. Layout already marks where a float's body is drawn: the frame group it pushes carries the `place` as its logical parent (`FrameParent`). The ancestry walk now uses that: - `FloatPlaces` finds every floating `place` and which are decorative: written inside an artifact opened in their own drawing scope, or inside the body of a decorative float. - `DrawScope`: inside a float's body group, the body is decorative exactly when its `place` is; artifacts open on the stack from the page's flow no longer reach into it, only artifacts opened inside the body do. This replaces the `decorative_range`/`spans_within`/`encloses` heuristics of the previous commits with ancestry.rs back on main's version plus these two pieces. An element whose every glyph is drawn inside artifacts is decorative for headings, lists, term lists and quotes too, not only paragraphs: a show rule wrapping a heading in `pdf.artifact` leaves the heading's own tags outside the artifact (this leak predates the branch). Figures and tables are left out, since an image carries no glyphs. Fixtures now assert the stronger behaviour: helper-built decorative floats are dropped, a `#let` shown kept and decorative appears once, a `context`-built kept float survives a multi-page artifact, and a show-rule-wrapped heading is dropped. 40 corpus and 10 audit documents are unchanged from main (ids aside); the 5000-paragraph, 500-float document exports in 1.75 s (main 2.28 s). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XxXUiDn2QyzNTYnZpeVFVS
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.
Content inside
pdf.artifactis decorative and never emitted. See the cnd-sdk spec §2, "Decorative content" (cosmyc-lab/cnd-sdk#3, proposal 0011).Changes
resolve_footnotesskips footnote declarations and markers located in an artifact, andresolve_cite_edgesskips citations located in one.@refor#linkwritten inside an artifact records no marker. The bare-content collectors and the index-correlation fallback skip artifact subtrees throughextract::traverse_kept, which behaves exactly likeContent::traverseoutside artifacts.resolve_labelno longer falls back to the first table figure for them.placeas itsFrameParent, and the ancestry walk now uses that.placewas written inside an artifact, or inside the body of a decorative float.pdf.artifact. Figures and tables are excluded, since an image carries no glyph.Tests
decorative_content.typcovers:#letshown both kept and decorative.decorative_float_helpers.typcovers:#letlist and table shown in a kept float and in a decorative block;decorative_multipage.typcovers:context, drawn while a multi-page artifact is open;metadataandstatecontent written in a decorative float and shown in the flow.cargo test --workspace,clippy -Dwarningsandfmtare clean.main, ids aside.main.Remaining leak (known)
A show rule that puts only part of an element in an artifact while drawing the rest outside still yields the element with its text. The typical case is a heading whose body is wrapped but whose number is not.
🤖 Generated with Claude Code
https://claude.ai/code/session_01XxXUiDn2QyzNTYnZpeVFVS