Repository navigation
feat(docx-markdoc): verify create output with an independent round-trip oracle - #1189
Merged
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
LLM gate (advisory)All evaluated rules passed - 5 pass, 0 warn, 0 error, 11 skipped, 16 total FindingsNone. All 16 rules (5 evaluated, 11 skipped)
|
stevenobiajulu
added a commit
that referenced
this pull request
Oct 8, 2026
… shape Review of #1189 found two false passes and two false/missed failures in the PDF word check. Generated text is now derived from the source: - list labels (1., (a), (i), bullets) are expected words, so a generated label cannot stand in for a deleted literal one; - each page must begin with exactly its section's effective footer text and its own page number, checked against the section of the content aligned on that page: a missing, wrong-section or misnumbered footer fails, and a numeric footer is never taken for the page number; - a table header row is accepted again only at the top of a page where that table continues on both sides of it. The shape-based list-number leniency and edge peeling are gone. Refs #1185
stevenobiajulu
added a commit
that referenced
this pull request
Oct 8, 2026
Confirmation review of #1189 found two more false passes and one false failure in the PDF word check: - a header-shaped run at a page head that is not a validated repetition must align with its table's own header row, or it is reported as an unverified table header; generated header text can no longer stand in for a lost header-equal body row; - every table with an identical header is tried as the identity of a continuation header; - a page with no aligned text takes every section a consistent page order allows (sections run in order, each on a new page) and must carry the footer of all of them; a section with no page fails. Refs #1185
stevenobiajulu
added a commit
that referenced
this pull request
Oct 9, 2026
Review of #1189 showed that header and footer rules checked separately can be satisfied by generated text in layouts the text cannot disambiguate (a table with empty trailing rows followed by a table with the same header), and can reject valid ones. Each page break is now explained jointly: between two aligned source words, every page that starts there must start at a point in the source (including inside wordless rows), which fixes its section footer and page number, the header LibreOffice repeats when a table continues, and the source words at the top of the page. Sections start on new pages. Two observed LibreOffice behaviours are modelled: a header row stays with the first body row, and a last row can spill its empty remainder onto the next page with the header repeated. The check passes only when every fitting placement explains the same source words; ambiguity fails. Refs #1185
…ip oracle The read-back and footer checks compare the DOCX with the projection of the lowered DocumentSpec, so a lowering bug that drops content from both passes them. create now also: - re-imports the DOCX through docx-markdoc import and compares body and footer paragraph sequences with text read from the original Markdoc alone (its own AST walk; NFC and whitespace collapsing only), with deleted-word, -paragraph, -table-cell and -footer negative controls and word-level missing/extra spans recorded in the certificate; - aligns a rendered PDF's text layer (pdftotext -raw, document order) with the source word for word, allowing only each page's footer and page number at a page edge and list numbers (CREATION_PDF_WORDS_MISMATCH); - leaves <stem>.failed-verification.json, named in the error, when a check finds a mismatch; nothing is published, the previous certificate is kept, and the next successful build removes the report. Refs #1185
…ge footer probes Refs #1185
… shape Review of #1189 found two false passes and two false/missed failures in the PDF word check. Generated text is now derived from the source: - list labels (1., (a), (i), bullets) are expected words, so a generated label cannot stand in for a deleted literal one; - each page must begin with exactly its section's effective footer text and its own page number, checked against the section of the content aligned on that page: a missing, wrong-section or misnumbered footer fails, and a numeric footer is never taken for the page number; - a table header row is accepted again only at the top of a page where that table continues on both sides of it. The shape-based list-number leniency and edge peeling are gone. Refs #1185
Confirmation review of #1189 found two more false passes and one false failure in the PDF word check: - a header-shaped run at a page head that is not a validated repetition must align with its table's own header row, or it is reported as an unverified table header; generated header text can no longer stand in for a lost header-equal body row; - every table with an identical header is tried as the identity of a continuation header; - a page with no aligned text takes every section a consistent page order allows (sections run in order, each on a new page) and must carry the footer of all of them; a section with no page fails. Refs #1185
When one table's header is a prefix of another's, a continuation header removed only the shorter prefix, leaving the rest available to stand in for a lost body word. Each page's candidate now offers every matching header length, longest first, advancing only when one fails its continuation check. A PDF with no pages fails the footer check. Refs #1185
Review of #1189 showed that header and footer rules checked separately can be satisfied by generated text in layouts the text cannot disambiguate (a table with empty trailing rows followed by a table with the same header), and can reject valid ones. Each page break is now explained jointly: between two aligned source words, every page that starts there must start at a point in the source (including inside wordless rows), which fixes its section footer and page number, the header LibreOffice repeats when a table continues, and the source words at the top of the page. Sections start on new pages. Two observed LibreOffice behaviours are modelled: a header row stays with the first body row, and a last row can spill its empty remainder onto the next page with the header repeated. The check passes only when every fitting placement explains the same source words; ambiguity fails. Refs #1185
stevenobiajulu
force-pushed
the
1185-independent-roundtrip-oracle-20261008
branch
from
October 9, 2026 00:55
e0d2cae to
4063e0a
Compare
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
docx-markdoc rose above its floor by more than the allowed drift (lines 92.25 -> 93.32, branches 79.06 -> 80.97) with the round-trip oracle tests; values are taken from the workspace-test (20) coverage artifact for this head. Refs #1185
…mpty pages Final review of #1189: - pdftotext -raw emits a row split across pages as each cell's fragment on each page. Such a row is read back in cell order only when the end of one page and the start of the next (and any whole pages between) partition exactly into the row's cells, every cell starting on the first page; words are only reordered, so a lost or duplicated word still fails. - Every page must hold content (source between its start and the next, a spilled row remainder, or the aligned text that follows), so an empty page cannot be invented to move a section start and its footer. - Running out of search budget is an explicit failure. Refs #1185
…rs; bound the search Final review round 2 of #1189: - The pages holding the rest of a split table row stay pinned to it: each must start inside the row, belong to its section and carry that section's footer, and show the repeated header unless the split row is the header itself. Reading the row back in cell order no longer erases which pages held it. - Header rows that split across pages are joined like body rows. - The fragment search tries a candidate only when it holds exactly the row's words, is memoized and pruned, and runs under a shared budget whose exhaustion fails the check. Refs #1185
Final review round 3 of #1189: a pinned continuation page at a section boundary could stand in for the next section's first page, and text from that next section on the same physical page was not checked against the row's section. Pinned pages now never satisfy a section start, and any other text aligned on a pinned page must belong to the row's section. Refs #1185
This was referenced Oct 9, 2026
stevenobiajulu
added a commit
that referenced
this pull request
Oct 9, 2026
Releases the docx-markdoc create follow-ups: explicit fill-in markup with literal bare brackets (#1187, #1184), the independent round-trip oracle, word-level PDF check and failure report (#1189, #1185), and the edited-DOCX guard for --replace (#1190, #1186), plus the header/footer carry-forward fix (#1188). Versions bumped with scripts/bump_version.mjs; the CHANGELOG entry is hand-written. Refs #1162
This branch was successfully deployed
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.
Problem (#1185)
create's read-back and footer checks compare the DOCX with the plain-text projection of the lowered DocumentSpec. A lowering bug that drops a word, paragraph, table cell or footer from both the spec and its projection passes them. The optional PDF check only looks for the first and last paragraphs and the footers, so it can't see a dropped middle word either.Change
create/oracle.ts, certificatechecks.roundTrip).docx-markdoc import.missing,extraorchanged, each with its source line. Achangedmismatch also records the missing and extra word spans, with four words of context on each side.pdf.words).pdftotext -raw) is aligned word by word with the source.1.,(a),(i), bullets) are expected words, computed from the grammar's numbering.CREATION_PDF_WORDS_MISMATCH. Texts too different to align fail asover-budget.limitationsstring names the remaining ambiguity (page-to-section assignment). It can only cause false failures, never false passes.<stem>.verification.json.<stem>.failed-verification.jsonand names it in the error.Close calls (raised with Lantern 3 before merge)
<stem>.failed-verification.json, as described above. Missing PDF tools under--require-pdfand validation errors write no report: they are not mismatches, and validation errors already carry line numbers.--require-pdf. A rendered PDF that fails its existing check already fails the build in default mode, and publishing a PDF that dropped content would be worse.renderPlainPdfnow runspdftotext -raw.-rawreads content-stream order, which for LibreOffice output is document order.PlainPdfVerdictgainspageTexts. The certificate omits it.Verification
.peer-review/greenfield-pr1189-20261008-164211-review.md): two P1s and two P2s, all fixed in 8a31bb4 with regression tests:1.;-raw)pdftotext.123;Refs #1185
Refs #1162