Skip to content

render-pipeline: verbatim and abstract are \trivlist boundaries too (#722 follow-up) - #728

Merged
d-q222 merged 3 commits into
mainfrom
agent/daniel-parent/trivlist-rest
Sep 17, 2026
Merged

d-q222 merged 3 commits into
mainfrom
agent/daniel-parent/trivlist-rest

Conversation

@d-q222

@d-q222 d-q222 commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

What

#722 closed the \trivlist boundary for center, flushleft, flushright,
quote, quotation, verse and every \newtheorem/proof, and left two
gaps open on purpose:

  1. verbatim and abstract are \trivlist-derived too, but were never
    measured, so they were deliberately not added to TRIVLIST_ENVS;
  2. only the 10 pt base size was swept.

Both are now measured, over 918 probe documents against pdflatex (pairs in
both directions, with and without a blank line, six-deep chains, solo and
paragraph controls, at 10 pt, 11 pt and 12 pt). Measurement, not
pattern-matching: three of the four things a "they're all trivlists" guess
would have predicted were already right, and the two real bugs have different
causes.

verbatim diverged, but only where the pipeline reads the pair as one
ParaStyle run.
\@verbatim is \trivlist \item\relax ... and
\endverbatim is \endtrivlist (latex.ltx), so \end{verbatim} leaves
vertical mode and the next \begin takes \partopsep. verbatim/verbatim
and verbatim/flushleft were short by exactly that: -1.992 bp at 10 pt,
-2.989 bp at 11 pt and 12 pt. It accumulates — six verbatims in a row
drifted 0, -1.992, -3.985, -5.978, -7.970, -9.963 bp at 10 pt.
verbatim against center, quote, quotation, verse, flushright, a
theorem or any of the three lists was already correct in both directions.

abstract diverged on both sides, for two different reasons.
\end{abstract} is \endquotation -> \endlist -> \endtrivlist, so it is
the same boundary, and what followed it was short by one \partopsep
(-1.992 / -2.989 / -0.996 bp at 10/11/12 pt). The other direction is not
a TRIVLIST_ENVS problem at all: the pipeline builds the \small
\abstractname head as a block inserted in front of the body the compiler
produced, and the boundary skip the compiler had hung on that body stayed
there and fired a second time below the head. \end{itemize}\begin{abstract}
came out +5.305 / +5.729 / +6.273 bp long, while
\end{center}\begin{abstract} was already right (a center's closing skip
rides on its own env_close, not on the next block). take_lead moves that
leading skip to the head with the position it belongs to.

No boundary diverged only at 11 pt or 12 pt, so the size sweep found no
third bug — but it did find that one boundary is not size-invariant at all,
which a 10 pt-only sweep would have pinned as a single rule:
\end{abstract}\begin{thm} is \topsep-only at 10 pt and 11 pt but
26.401 bp at 12 pt, because the abstract's \@topsepadd was fixed inside
\small (6/9/12 pt) while a theorem opens with \thm@preskip =
\normalsize's \topsep (8/9/10 pt) and no \partopsep, and which of the
two \addvspaces wins changes with the class option. That is pinned as its
own constant.

Why

The same \addvspace rule #706 and #712 found reaches every \trivlist, and
#722 said so but only measured the environments it had probes for. A
verbatim block between two paragraphs of a listing-heavy document, or an
abstract after a list, is not exotic; the errors accumulate down the page
and move page breaks.

Pipeline-only, as #706/#712/#722 were: nothing under vendor/ changed, no
re-pin.

Overlap check

Test results

cargo test --test vmode_boundary_skips
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

cargo test --test abstract_env
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

cargo test --no-fail-fast, this branch (a12ec1d3):  480 passed; 13 failed (107 suites)
cargo test --no-fail-fast, base       (237b95fa):   479 passed; 13 failed (107 suites)

Diffing every test <name> ... <result> line between the two runs gives
exactly one line — the added
an_abstract_shares_one_addvspace_with_the_environment_on_either_side ... ok.
No other test's result changed in either direction, so "0 new failures" is a
line-by-line fact rather than a count that happens to match. The same 13
failures on both sides are the known adapter.rs:1308 vendor/compiler pin
(every_line_is_where_pdflatex_put_it, three letter_class_geometry,
the_raggedleft_group_closes_no_trivlist,
shared_colorbox_path_draws_border_with_content_inside, two uline, four
sout/underline, xcolor_fixtures_match_pdflatex).
hw2_sets_three_pages_with_the_reference_breaks ... ok on both.

Oracle: MacTeX 2026 pdfTeX 1.40, T1 Latin Modern, PyMuPDF glyph origins, 918
probe documents at the 10 pt / 11 pt / 12 pt article base sizes (every
ordered pair of 9 shapes x 12 partners, with and without a blank line,
six-deep chains, solo/paragraph/blank-line controls), plus nine twocolumn
probes. pdflatex is an oracle only and never runs in the product path.

Not done

  • A \begin{abstract} on the line directly after body text, with no blank
    line, is not recognised at all
    — measured, and left alone. The compiler
    does not break the paragraph there, so it reports one text run across the
    \begin, the pipeline finds no block inside the body to restyle, and no
    \abstractname head is set; the body lands 32.553 / 36.115 / 42.744 bp
    high, on the previous line. Two adjacent abstracts fail the same way
    (the second is dropped). This is a compiler-side paragraph-splitting gap
    rather than a boundary skip (\begin{center} in the same position does
    break the paragraph), and fixing it means a change under vendor/ plus a
    re-pin, which this line of work has stayed out of. It is the only
    divergence left in the 918-document sweep and is documented at the top of
    tests/vmode_boundary_skips.rs.
  • lstlisting, Verbatim (fancyvrb) and alltt are \trivlist-derived
    too and are not added here: they were not measured, which is exactly
    the reason render-pipeline: every \trivlist boundary shares one \addvspace, not two (#712 follow-up) #722 gave for leaving verbatim out.
  • The twocolumn abstract after a list sets its body 3.985 / 4.483 /
    4.981 bp low
    at 10/11/12 pt — measured, and left alone because it is
    untouched by this PR: base and branch print byte-identical baselines for
    all nine two-column probes. The \abstractname head is exact, and the
    center-before and standalone cases are exact. That branch is a
    \section* and ordinary paragraphs rather than a quotation, so it is a
    different shape, not a \trivlist boundary.
  • Only the article class was swept. report/book share \@listI but
    have their own abstract, and were not measured.

🤖 Generated with Claude Code

https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz

What changed:
`\end{<list>}` is `\@endparenv`'s `\addvspace\@topsepadd` and the
`\begin{<list>}` beside it is `\@trivlist`'s `\addvspace\@topsep`. Both are
`\addvspace`, so they keep the larger natural skip; the pipeline summed them.
`\@endparenv` also leaves TeX in vertical mode, so the second `\begin` is read
in vertical mode and takes `\partopsep` with no blank line in between; the
pipeline read it as horizontal mode and dropped `\partopsep`.

At 10pt that made a list/list boundary 10pt + 8pt = 18pt where pdflatex puts
max(10pt, 10pt) = 10pt. Every list after the first sat 8pt low and the error
grew 6pt at each further boundary (4 adjacent lists: +7.971, +13.948,
+19.925 bp).

The environment never mattered: `itemize`, `enumerate` and `description` are
all `\list` with `\@listI`'s skips, and `list_seps_from` does not branch on the
name for vertical spacing. #706 saw only `itemize` match because `itemize` came
first in its fixture.

New `tests/adjacent_list_skips.rs` pins the adjacency coverage #705's oracle
left out: all six orderings of the three environments, all nine ordered pairs,
four of a kind and a mixed chain of four, plus the `\topsep`-only vs
`\topsep`+`\partopsep` pair that separates the two lengths.

What was run:
  cargo test -p render-pipeline --test adjacent_list_skips
    test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
  same test on unfixed origin/main (dbe3cac), to show it catches the bug:
    test result: FAILED. 1 passed; 3 failed; 0 ignored; 0 measured
    ied: bravo: 164.653 bp, pdflatex 156.682 bp (+7.970)
  28-document pdflatex sweep (MacTeX 2026, PyMuPDF glyph origins), engine vs
  pdflatex: worst |dy| = 0.0001 bp, worst |dx| = 0.0379 bp, all within 0.5 bp.

Next step:
Full `cargo test -p render-pipeline` diffed against origin/main for regressions.

Implementation-Agent: claude-opus-5 subagent of daniel-parent
Commit-Executor: daniel-parent subagent
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz
…two (#712 follow-up)

What changed:
#712 fixed the list-to-list boundary. Neither half of that fix was about
lists: `\@endparenv` is what every `\endtrivlist` ends with, and article.cls
builds `center`/`flushleft`/`flushright` as `\trivlist \centering \item\relax`,
`quote`/`quotation`/`verse` as `\list{}{...}\item\relax`, and amsthm builds a
theorem as a `\trivlist`. Measured against pdflatex, three boundaries were off:

  * `\end{<list>}` then `\begin{center|quote|quotation|verse|<theorem>}` summed
    the list's closing `\@topsepadd` onto the environment's opening `\@topsep`:
    10pt + 8pt = 18pt where pdflatex puts max(10, 10) = 10pt, +7.971 bp;
  * `\end{<theorem>}` then any `\trivlist` environment, and every pair the
    pipeline reads as one `ParaStyle` run (`center`/`center`, `quote`/`verse`,
    ...), was read in horizontal mode and dropped `\partopsep`: -1.992 bp;
  * both accumulated. Six `center`s drifted -1.992/-3.985/-5.978/-7.970/-9.963
    bp; alternating `center`/`itemize` drifted +5.978/+3.985/+9.963/+7.970 bp.

`gap_has_trivlist_end` extends #712's "the previous `\end` left vertical mode"
test from the four `LIST_ENVS` to every `\trivlist`-derived environment, and
feeds both the list `\begin`'s `list_vmode` and the styled `\begin`'s
`EnvOpen::vmode`. In `typeset`, the environment's own opening `\addvspace`
now resolves against the `\lastskip` that `addvspace_before` already raised,
instead of being added on top of it -- `\@xaddvskip` keeps the larger skip
whole, so its flex comes with it and the other's does not.

An amsthm theorem is the one case that legitimately steps by `\topsep` alone:
`\@thm` assigns `\@topsep`/`\@topsepadd` from `\thm@preskip`/`\thm@postskip`,
so it never takes `\partopsep`. Two adjacent theorems stay 19.925 bp apart and
that is pinned, so this fix does not sweep them up.

Pipeline-only, as #706/#712 were: nothing under vendor/ changed, no re-pin.

What was run:
  cargo test --test vmode_boundary_skips
    test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
  the same test on the unfixed base (6763a09, #712 head):
    test result: FAILED. 2 passed; 4 failed; 0 ignored; 0 measured
    thm -> itemize (adjacent): bravo: 154.690 bp, pdflatex 156.682 bp (-1.993)
    itemize -> center: bravo: 164.653 bp, pdflatex 156.682 bp (+7.970)
    center -> center: bravo: 154.690 bp, pdflatex 156.682 bp (-1.993)
    ["center" x6]: bravo: 154.690 bp, pdflatex 156.682 bp (-1.993)
  cargo test --no-fail-fast, this branch:  479 passed; 13 failed
  cargo test --no-fail-fast, base 6763a09: 473 passed; 13 failed
    same 13 names both sides (frame_env, letter_class_geometry x5, uline x2,
    underline_sout x3, xcolor_oracle x2); +6 passed is this commit's new test.
    0 new failures. hw2_sets_three_pages_with_the_reference_breaks ... ok
  pdflatex oracle: MacTeX 2026 pdfTeX 1.40, 10pt T1 Latin Modern, PyMuPDF
    glyph origins; 101 probe documents (pairs both directions with and without
    a blank line, six-deep chains, solo/paragraph/blank-line controls).

Next step:
None; the boundary is measured in both directions and the chains are flat.

Implementation-Agent: claude-opus-5 subagent of daniel-parent
Commit-Executor: daniel-parent subagent
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz
…722 follow-up)

What changed:
#722 closed the \trivlist boundary for center/flushleft/flushright/quote/
quotation/verse and every \newtheorem, and left two gaps open on purpose:
`verbatim` and `abstract` are \trivlist-derived too but were never measured,
so they were deliberately not added to TRIVLIST_ENVS; and only the 10 pt base
size was swept. Both are now measured, over 918 probe documents against
pdflatex at 10/11/12 pt. Three of the four things a "they're all trivlists"
guess would have predicted were already right; the two real bugs differ:

  * verbatim diverged only where the pipeline reads the pair as one
    ParaStyle run. \@verbatim is `\trivlist \item\relax ...` and
    \endverbatim is \endtrivlist, so \end{verbatim} leaves vertical mode and
    the next \begin takes \partopsep: verbatim/verbatim and
    verbatim/flushleft were short by exactly that, -1.992 bp at 10 pt and
    -2.989 bp at 11 and 12 pt, accumulating to -9.963 bp over six
    verbatims. verbatim against center/quote/quotation/verse/flushright, a
    theorem or any list was already right in both directions.
  * abstract diverged on both sides, for two different reasons.
    \end{abstract} is \endquotation -> \endlist -> \endtrivlist, and what
    followed it was short by one \partopsep (-1.992/-2.989/-0.996 bp). The
    other direction is not a TRIVLIST_ENVS problem: the pipeline inserts the
    \small \abstractname head as a block in front of the body the compiler
    produced, and the boundary skip the compiler had hung on that body
    stayed there and fired a second time below the head, so
    \end{itemize}\begin{abstract} was +5.305/+5.729/+6.273 bp long.
    \end{center}\begin{abstract} was already right, because a center's
    closing skip rides on its own env_close rather than on the next block.

TRIVLIST_ENVS gains verbatim, verbatim* and abstract; `take_lead` moves the
abstract body's leading skip to the head that now occupies its position.

No boundary diverged only at 11 pt or 12 pt, so the size sweep found no third
bug -- but it did find one boundary that is not size-invariant at all, which a
10 pt-only sweep would have pinned as a single rule:
\end{abstract}\begin{thm} is \topsep-only at 10 pt and 11 pt but 26.401 bp at
12 pt, because the abstract's \@topsepadd was fixed inside \small (6/9/12 pt)
while a theorem opens with \thm@preskip = \normalsize's \topsep (8/9/10 pt)
and no \partopsep, and which \addvspace wins changes with the class option.
That is pinned as its own constant.

Pipeline-only, as #706/#712/#722 were: nothing under vendor/ changed, no
re-pin.

What was run:
  cargo test --test vmode_boundary_skips
    test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
  cargo test --test abstract_env
    test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
  cargo test --no-fail-fast, this branch:  480 passed; 13 failed (107 suites)
  cargo test --no-fail-fast, base 237b95f: 479 passed; 13 failed (107 suites)
    diffing every `test <name> ... <result>` line between the two runs gives
    exactly one line, the added
    an_abstract_shares_one_addvspace_with_the_environment_on_either_side ...
    ok. Same 13 failures both sides (every_line_is_where_pdflatex_put_it,
    letter_class_geometry x3, the_raggedleft_group_closes_no_trivlist,
    shared_colorbox_path_draws_border_with_content_inside, uline x2,
    sout/underline x4, xcolor_fixtures_match_pdflatex) -- the known
    adapter.rs:1308 vendor/compiler pin. 0 new failures.
    hw2_sets_three_pages_with_the_reference_breaks ... ok on both.
  pdflatex oracle: MacTeX 2026 pdfTeX 1.40, T1 Latin Modern, PyMuPDF glyph
    origins; 918 probe documents at the 10pt/11pt/12pt article base sizes
    (every ordered pair of 9 shapes x 12 partners, with and without a blank
    line, six-deep chains, solo/paragraph/blank-line controls), plus nine
    twocolumn probes.

Next step:
Two measured divergences are left and deliberately not fixed here, both
documented at the top of tests/vmode_boundary_skips.rs: a \begin{abstract} on
the line directly after body text is not recognised at all (the compiler does
not break the paragraph there, so it needs a vendor/ change and a re-pin),
and the twocolumn branch sets an abstract body after a list 3.985/4.483/4.981
bp low -- base and branch print byte-identical baselines for all nine
twocolumn probes, so that one is untouched by this commit.

Implementation-Agent: claude-opus-5 subagent of daniel-parent
Commit-Executor: daniel-parent subagent
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz

@d-q222 d-q222 left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Independent verification — VERIFIED

Reviewed at a12ec1d3, base 237b95fa (#722). Worktrees ~/flashtex-wt/vrfy-728 and
~/flashtex-wt/vrfy-722, per-PR CARGO_TARGET_DIR=…/target-vrfy-728 and
…/target-vrfy-722, CARGO_BUILD_JOBS=4, fetched as
git fetch origin pull/N/head:refs/review/pr-N.

I was pointed at this PR to confirm a claimed self-caught regression in the
two-column abstract
. That regression does not exist, and — importantly — this PR
never claims it does
; it says the opposite, twice, and the measurement backs the PR.
The claim it does make (a line-by-line test diff) reproduces exactly.


1. Line-by-line test diff — VERIFIED, to the line

Whole crates/render-pipeline suite, --no-fail-fast, debug, both sides:

$ cargo test --no-fail-fast          # branch a12ec1d3
$ cargo test --no-fail-fast          # base   237b95fa
$ grep -oE '^test [A-Za-z0-9_:]+ \.\.\. [a-zA-Z]+' <log> | sort > <side>.lines
$ diff base.lines branch.lines
111a112
> test an_abstract_shares_one_addvspace_with_the_environment_on_either_side ... ok
base lines:   496
branch lines: 497
base:   passed=479 failed=13 suites=107
branch: passed=480 failed=13 suites=107

Exactly one added line, zero changed lines, in either direction. That is the claim
the PR makes and it holds. The 13 failures are name-for-name identical on both sides:

$ diff <(grep '\.\.\. FAILED' base.log | sort) <(grep '\.\.\. FAILED' branch.log | sort)
(no output)

every_line_is_where_pdflatex_put_it              opening_lines_are_one_paragraph_not_one_each
letter_paragraph_gaps_are_the_class_parskip      page_one_texttop_fil_pushes_the_first_baseline_down
the_raggedleft_group_closes_no_trivlist          shared_colorbox_path_draws_border_with_content_inside
sout_paints_a_rule                               sout_with_ulem_is_not_an_unsupported_error
text_mode_underline_paints_a_rule                text_mode_underline_is_not_an_unsupported_error
uline_paints_a_rule_under_the_argument           uline_with_ulem_is_not_an_unsupported_error
xcolor_fixtures_match_pdflatex

$ grep -c 'adapter.rs:1308' branch.log   ->  13

— the known stale vendor/compiler pin, not this PR's. (Counted in debug
deliberately: these are debug_asserts and a --release run would have shown a
misleading green.)

Targeted, on the branch:

$ cargo test --test vmode_boundary_skips
test result: ok. 7 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
$ cargo test --test abstract_env
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

2. The fix genuinely fails on the base

I copied this branch's tests/vmode_boundary_skips.rs into the #722 worktree unchanged
and ran it against #722's source:

$ cargo test --test zz_review_vmode728          # 728's tests, 722's code
test an_abstract_shares_one_addvspace_with_the_environment_on_either_side ... FAILED
test the_boundary_error_does_not_accumulate_across_six_environments ... FAILED
test two_adjacent_paragraph_shape_environments_step_by_topsep_plus_partopsep ... FAILED
test result: FAILED. 4 passed; 3 failed

panicked at tests/zz_review_vmode728.rs:201:5:
10pt itemize -> abstract (adjacent): bravo: 176.608 bp, pdflatex 171.303 bp (+5.305)

+5.305 is the exact 10 pt figure in the PR body. The other two failures are the
verbatim cases the PR added to pre-existing tests — right, since verbatim is not in
TRIVLIST_ENVS on the base (adapter.rs:5004, 6 entries -> 9).

3. The two-column abstract — measured independently, and it is NOT regressed on #722

I rendered six abstract shapes on both revisions and compared every word's x and
baseline (scratch probe, deleted afterwards):

probe #722 base #728 branch
twocolumn + \maketitle + abstract identical identical
twocolumn, \end{itemize} -> abstract identical identical
twocolumn, \end{center} -> abstract identical identical
twocolumn, \end{abstract} -> itemize identical identical
one-column, \end{itemize} -> abstract body 176.608 body 171.303
one-column, \end{abstract} -> itemize identical identical

Literal, for the two rows that matter:

722  TC 4-twocol-end-then-itemize n=9 :: Abstract@72.000/134.765 Abstract@72.000/156.585 body@113.318/156.585 … •@86.945/178.503 two@96.907/178.503 Tail.@81.963/200.421
728  TC 4-twocol-end-then-itemize n=9 :: Abstract@72.000/134.765 Abstract@72.000/156.585 body@113.318/156.585 … •@86.945/178.503 two@96.907/178.503 Tail.@81.963/200.421

722  TC 5-onecol-after-itemize  … Abstract@172.498/176.608 body@210.710/176.608 … after.@222.795/194.540
728  TC 5-onecol-after-itemize  … Abstract@172.498/171.303 body@210.710/171.303 … after.@222.795/189.235

So on #722 the two-column \end{abstract} sets nine words in the right places — it does
not expand to nothing, and there is nothing for this PR to have fixed there. The only
behavioural change is the one-column \end{itemize}\begin{abstract} shift of exactly
5.305 bp, which is the one the PR describes.

This matches what the PR says of itself — abstractenv.rs:276-282 ("measured it changes
nothing there … every two-column probe comes out byte-identical with and without this")
and the "Not done" bullet ("base and branch print byte-identical baselines for all nine
two-column probes"). The PR's own account of the two-column branch is accurate; the
claim I was asked to check was not a claim of this PR.
Anyone carrying that
description forward should drop it.

Reading the change

take_lead (abstractenv.rs:269-320) is the right shape: the compiler hangs the
incoming skip on the block that was first, and inserting the head in front of it makes
the head that block, so the skip has to travel with the position. It moves all six
fields (eject_before, vspace_before, addvspace_before, addvspace_flex,
vspace_flex, endlist_adjust) rather than the one that happened to be wrong, and the
let … else { return } makes it a no-op for any non-Paragraph pair. Applying it to
the two-column branch on a positional argument, with the measurement recorded as showing
no change, is documented honestly at :276-282.

The \end{abstract}\begin{thm} size-dependence note is the kind of thing a 10 pt-only
sweep would have got wrong, and it is pinned as its own constant rather than folded into
a rule.

Verdict

VERIFIED — the line-by-line diff is exactly one added line, the new test fails on
the base with the stated figure, and the two-column branch is byte-identical across the
pair as the PR states.

Nothing was edited, pushed or merged; both scratch probe files deleted, both worktrees
clean.

— independently verified by daniel-parent's review lane

@d-q222
d-q222 merged commit a12ec1d into main Sep 17, 2026
21 of 22 checks passed
ItsAkilesh pushed a commit to ItsAkilesh/flashtex that referenced this pull request Sep 18, 2026
…#728 follow-up)

What changed:

flash-tex#728 left four things explicitly unmeasured. All four are measured now, over
1482 further probe documents against pdflatex, and nothing was added to
TRIVLIST_ENVS for looking like a \trivlist.

* `lstlisting` (listings) diverged twice and is *not* a \trivlist: `\lst@Init`
  opens no list. What it has is a `\par` and a `\vspace` at each end, and both
  were read as nothing. `\end{lstlisting}\begin{<trivlist>}` dropped
  `\partopsep` (-1.993/-2.989/-2.989 bp at 10/11/12 pt) because
  `\lst@DeInit`'s `\par` leaves vertical mode as `\@endparenv`'s does — a new
  `VMODE_END_ENVS`, deliberately kept apart from TRIVLIST_ENVS, since
  `\end{lstlisting}\begin{thm}` was right all along.
  `\end{<list>}\begin{lstlisting}` lost the whole closing `\@topsepadd`
  (-9.963/-11.955/-12.951) because `listings` zeroed the block's
  `addvspace_before` along with the `flushleft` lowering, although
  `\lst@Init`'s skip is a `\vspace` that adds to it rather than competing.
  The same after `flushleft` and `verbatim`, where the pair reads as one
  `ParaStyle` run and the closing skip is never materialised at all.

* `Verbatim` (fancyvrb) and `alltt` are unimplemented and are left alone. The
  engine says so itself ("packages fancyvrb are recognised but not
  implemented"); measured, six `alltt` blocks land on one baseline (-21.917
  bp by the second, -109.589 by the sixth at 10 pt). There is no \trivlist
  there to give a boundary skip to, so no test pins a number for them.

* `report` and `book` measure exactly like `article` over the whole 8x8
  boundary matrix at all three sizes, `lstlisting` included; `\@listI` does
  not differ by class. `book` differs only in its first baseline. The two
  class exceptions are outside a boundary and already declared: book.cls has
  no `abstract`, and `report`'s default `titlepage` branch sets one on a page
  of its own (head 317.407 bp, body 339.325, following material on the next
  page), which `abstractenv::Branch::TitlePage` does not set.

* No boundary in this scope is size-dependent, and not by luck: both listings
  skips are `\vspace`s, so nothing competes for a class option to flip.
  Forcing `belowskip` to 4, 9 and 20 pt still measures a plain sum at all
  three sizes.

The two-column `abstract` flash-tex#728 filed is fixed rather than filed again. It was
one line of flash-tex#728's own argument: `\if@twocolumn`'s head is a `Block::Heading`,
which has no `addvspace_before`, so `take_lead` fell out of its `let else` and
left the list's `\@topsepadd` below the head to fire there (+3.985/+4.483/
+4.981 bp). `\@startsection`'s negative before-skip has already spent it above
the head — pdflatex puts the head the same distance below an `itemize`, a
`center`, a `verbatim`, a theorem and a plain paragraph — so it is dropped,
with a real `\section*{Abstract}` in the same position as the control.

The same sweep caught a regression flash-tex#728 introduced: `abstract` went into
TRIVLIST_ENVS unconditionally, but article.cls 386 closes the environment with
`\if@twocolumn\else\endquotation\fi`, so a two-column `\end{abstract}` expands
to nothing. Every two-column `\end{abstract}\begin{<trivlist>}` was one
`\partopsep` long (1.992/2.989/2.988 bp) against flash-tex#722, which was right.
Whether that `\end` is an `\endtrivlist` is now a class-option question
(`abstractenv::end_is_endtrivlist`), not a name lookup.

What was run:

  cargo test --test vmode_boundary_skips
  test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 8.55s

  full crate --no-fail-fast, branch and base (a12ec1d) built identically,
  `test <name> ... <result>` lines diffed rather than totals:
  497 lines on base, 501 on branch; 0 lines changed, 4 added, all ok:
    test a_lstlisting_boundary_is_the_neighbour_skip_plus_listings_own|ok
    test a_two_column_abstract_is_a_section_head_and_its_end_is_not_a_trivlist|ok
    test report_and_book_share_the_class_boundary_skips|ok
    test the_listings_skips_add_and_never_compete|ok
  13 failures on both, all panicking at adapter.rs:1308 (stale vendor/compiler pin).

  every glyph of all 22 renderable fixtures under fixtures/real-world/
  (HW1 and HW2 included), base vs branch: 15567 lines, 0 differences.
  listings-manual is the 23rd and panics at adapter.rs:1308 on both.

  probe sweeps against /Library/TeX/texbin/pdflatex with PyMuPDF glyph
  origins, 0.5 bp gate: 531 package documents, 891 class documents, 60
  two-column documents; every remaining divergence involves only `alltt`
  or `Verbatim`, or `report`'s titlepage `abstract`.

Next step:

  File report's `titlepage` abstract (204.560/205.624/209.875 bp plus a lost
  page break) and the `alltt`/`fancyvrb` gap as their own issues; neither is a
  boundary skip.

Implementation-Agent: claude-opus-5 subagent of daniel-parent
Commit-Executor: daniel-parent subagent
Claude-Session: https://claude.ai/code/session_012c9XLkHjePPGBuarrmE2mz
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