From e7d21dad70c650d88f719485fb7d8f0098de2a83 Mon Sep 17 00:00:00 2001 From: docushell-admin Date: Tue, 16 Jun 2026 20:42:24 +0530 Subject: [PATCH] Add heading export fixture coverage Signed-off-by: docushell-admin --- crates/ethos-cli/tests/pdf_parse.rs | 49 ++++++++++++++++++ docs/execution-status.md | 4 +- fixtures/README.md | 5 +- fixtures/manifest.json | 13 +++++ .../synthetic/heading-export/document.pdf | Bin 0 -> 655 bytes .../synthetic/heading-export/extraction.json | 1 + .../synthetic/heading-export/fixture.json | 16 ++++++ fixtures/synthetic/heading-export/layout.json | 1 + fixtures/synthetic/heading-export/markdown.md | 3 ++ fixtures/synthetic/heading-export/text.txt | 3 ++ 10 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 fixtures/synthetic/heading-export/document.pdf create mode 100644 fixtures/synthetic/heading-export/extraction.json create mode 100644 fixtures/synthetic/heading-export/fixture.json create mode 100644 fixtures/synthetic/heading-export/layout.json create mode 100644 fixtures/synthetic/heading-export/markdown.md create mode 100644 fixtures/synthetic/heading-export/text.txt diff --git a/crates/ethos-cli/tests/pdf_parse.rs b/crates/ethos-cli/tests/pdf_parse.rs index 7c59767f..0738521b 100644 --- a/crates/ethos-cli/tests/pdf_parse.rs +++ b/crates/ethos-cli/tests/pdf_parse.rs @@ -94,6 +94,10 @@ fn two_line_fixture_pdf() -> PathBuf { fixture_pdf_by_id("synthetic-two-lines") } +fn heading_export_fixture_pdf() -> PathBuf { + fixture_pdf_by_id("synthetic-heading-export") +} + fn two_column_fixture_pdf() -> PathBuf { fixture_pdf_by_id("synthetic-two-columns") } @@ -342,6 +346,51 @@ fn doc_parse_text_and_markdown_exports_match_fixture_goldens_when_pdfium_is_conf } } +#[test] +fn parses_heading_fixture_and_exports_markdown_when_pdfium_is_configured() { + if !pdfium_configured() { + eprintln!( + "skipping heading export fixture test: ETHOS_PDFIUM_LIBRARY_PATH is not configured" + ); + return; + } + + let fixture = heading_export_fixture_pdf(); + let doc = parse_json(&[ + "doc", + "parse", + fixture.to_str().unwrap(), + "--format", + "json", + ]); + let elements = doc["payload"]["elements"].as_array().unwrap(); + assert_eq!(elements.len(), 2); + assert_eq!(elements[0]["type"], "heading"); + assert_eq!(elements[0]["heading_level"], 1); + assert_eq!(elements[0]["text"], "Alpha Overview"); + assert_eq!(elements[1]["type"], "text_block"); + assert_eq!(elements[1]["text"], "Trust loop evidence stays explicit"); + + let output = run_ethos(&[ + "doc", + "parse", + fixture.to_str().unwrap(), + "--format", + "markdown", + ]); + assert!( + output.status.success(), + "ethos doc parse --format markdown failed for heading fixture\nstatus: {:?}\nstderr:\n{}\nstdout:\n{}", + output.status.code(), + String::from_utf8_lossy(&output.stderr), + String::from_utf8_lossy(&output.stdout) + ); + assert_eq!( + String::from_utf8(output.stdout).expect("markdown stdout is UTF-8"), + "# Alpha Overview\n\nTrust loop evidence stays explicit\n" + ); +} + #[cfg(debug_assertions)] #[test] fn doc_parse_timeout_kills_pdfium_worker() { diff --git a/docs/execution-status.md b/docs/execution-status.md index 07e51a48..67de83b5 100644 --- a/docs/execution-status.md +++ b/docs/execution-status.md @@ -14,7 +14,7 @@ The committed implementation now includes: - A pinned Phase 1 PDFium profile in `docs/pdfium-profile.md` and `profiles/ethos-deterministic-v1.json`: `chromium/7881`, V8/XFA disabled, platform artifact hashes, runtime library hashes, and provenance are recorded. - Runtime checks that reject missing or mismatched PDFium versions, release artifacts, and extracted libraries with stable errors before dynamic loading. - `ethos doc parse` / `ethos fingerprint` PDF execution through a worker process with `max_parse_ms` timeout enforcement, stable error-envelope relay, diagnostics-gated worker stderr, and page-range validation/filtering. -- Quantized page/span extraction at the backend boundary, plus a basic deterministic layout pass that assembles paragraph `text_block` elements and simple column reading order for the current born-digital fixtures. Fixture validation binds selected `fixture.json` expectations to committed extraction/layout goldens and binds current alpha text/Markdown exports to committed layout output so current read-order, element-type, and export cases fail closed on drift. +- Quantized page/span extraction at the backend boundary, plus a basic deterministic layout pass that assembles paragraph `text_block` elements, fixture-backed alpha heading elements, and simple column reading order for the current born-digital fixtures. Fixture validation binds selected `fixture.json` expectations to committed extraction/layout goldens and binds current alpha text/Markdown exports to committed layout output so current read-order, element-type, heading-export, and export cases fail closed on drift. - Schema/example/profile validation is green through `schemas/validate_examples.py` using `jsonschema` draft 2020-12 validation, including the crop descriptor artifact contract plus referential-integrity and bbox sanity checks outside JSON Schema. - `ethos verify` now produces non-empty quote, value, presence, and table-cell verification checks over native Ethos document JSON and synthetic OpenDataLoader-style JSON through `--grounding opendataloader-json`; it also verifies quote/value/presence citations over pinned real OpenDataLoader 2.4.7 JSON, including grounded and ungrounded cases. Citation/config inputs are rejected when they drift outside the closed schemas. The public demo harness covers grounded, ungrounded, split-quote, not-found, stale-fingerprint, unsupported non-v1 claim, capability-limited, malformed-citation, malformed OpenDataLoader-style input, and summary-format reject paths. - Verification semantics are now trust-honest at alpha scope: quote containment is explicitly labeled, value/table-cell checks require normalized equality, fingerprint-pinned citations fail closed when source fingerprints are unavailable, and structured capability limits explain why a run is downgraded. @@ -49,7 +49,7 @@ Milestone A has an accepted internal Gate Zero decision for roadmap control, so | PDFium Phase 1 profile | Landed: pinned profile, V8/XFA-disabled state, platform hashes, runtime library hashes, and provenance are recorded | Phase 2 project-maintained builds still block Public Beta | | PDFium loader/runtime checks | Landed: missing/mismatched version, artifact, and runtime library hashes fail deterministically | Release packaging and operator setup path still need hardening | | Real PDF backend | Landed for simple born-digital PDFs: page count, quantized spans, worker execution, timeout, page filtering, and fingerprint path exist | Wider corpus coverage, failure fixtures, memory-limit behavior, quirk log, and Gate Zero run are still missing | -| Layout groundwork | Landed: basic paragraph text blocks, simple column reading order over quantized spans, fixture metadata checks against committed extraction/layout goldens for current read-order and element-type expectations, and alpha text/Markdown export goldens derived from committed layout output | Tables, headings, lists, rotation/quirk handling, and confidence policy remain future work | +| Layout groundwork | Landed: basic paragraph text blocks, fixture-backed alpha heading elements, simple column reading order over quantized spans, fixture metadata checks against committed extraction/layout goldens for current read-order and element-type expectations, and alpha text/Markdown export goldens derived from committed layout output | Tables, lists, rotation/quirk handling, and confidence policy remain future work | | Font policy groundwork | Partially landed: substitution table and profile policy are present; fixture output uses deterministic substitution IDs | Bundled fallback asset hashing and broader font/CID validation remain open | | Schema/example validation | Landed: schemas, examples, deterministic profile, referential integrity, and bbox sanity pass the `jsonschema` validation gate | Contract changes still require explicit versioning and compatibility review | | Trust-layer implementation | Landed: `ethos verify` quote/value/presence/table-cell checks, explicit quote-containment labeling, normalized equality for value/table-cell checks, stale and unverifiable fingerprint handling, unsupported claim reporting, structured capability limits, native Ethos JSON path, ODL-style adapter path with synthetic table/cell mapping, pinned real OpenDataLoader 2.4.7 grounded/ungrounded fixtures, foreign fixture manifest hash validation, crop-ref evidence plumbing, stable logical native crop refs, native crop descriptor artifacts, raw BGRA crop rendering in `ethos-pdf`, CLI PNG crop artifact production for bound native source PDFs, same-host rendered crop repeatability check, rendered-crop run comparison helper, strict citation/config input validation, citation input schema, split-quote fixture coverage, explicit unsupported non-v1 claim reporting, OpenDataLoader-style structure diagnostics for malformed bbox and unknown-page references, verify-alpha case inventory checks, and demo fixtures | Still needed: real OpenDataLoader table-cell grounding, additional adapter hardening against broader real output shapes, future claim-kind expansion outside the current v1 alpha policy, and a decision on whether cross-platform rendered crop artifact equality is worth pursuing after the current macOS/Linux bbox drift finding | diff --git a/fixtures/README.md b/fixtures/README.md index f01d497c..05f52a17 100644 --- a/fixtures/README.md +++ b/fixtures/README.md @@ -37,9 +37,8 @@ Successful parse fixtures also carry c14n stage goldens: - `extraction.json`: `ethos_core::traits::Extraction` after the PDF backend boundary. - `layout.json`: `ethos_core::traits::LayoutOutput` after deterministic layout grouping. - `text.txt`: current alpha plain-text export rendered from `layout.json` element text order. -- `markdown.md`: current alpha Markdown export rendered from `layout.json`. The current - committed synthetic fixture set contains text blocks, so these files mirror the same block - order as `text.txt`. +- `markdown.md`: current alpha Markdown export rendered from `layout.json`, including heading + prefixes for committed heading elements. For successful fixtures, `validate_fixtures.py` also binds selected `fixture.json` expectations to those committed goldens: diff --git a/fixtures/manifest.json b/fixtures/manifest.json index cc5fc9f0..e0a4b122 100644 --- a/fixtures/manifest.json +++ b/fixtures/manifest.json @@ -5,6 +5,7 @@ "born_digital", "failure", "fonts", + "headings", "hyphenation", "ligatures", "multi_column", @@ -66,6 +67,18 @@ "provenance": "Synthetic password-protected PDF generated by Ethos maintainers from the simple-text fixture with qpdf 256-bit encryption for stable password-protected error-envelope tests.", "license": "CC0-1.0" }, + { + "id": "synthetic-heading-export", + "file": "synthetic/heading-export/document.pdf", + "sha256": "e5692f6b236da48f85790bffa68787a2d30ec2bcadf4e2d1e0ef9626787537b4", + "pages": 1, + "subsets": [ + "born_digital", + "headings" + ], + "provenance": "Synthetic PDF generated by Ethos maintainers for Milestone B alpha heading export fixture coverage.", + "license": "CC0-1.0" + }, { "id": "synthetic-hyphenated-line-break", "file": "synthetic/hyphenated-line-break/document.pdf", diff --git a/fixtures/synthetic/heading-export/document.pdf b/fixtures/synthetic/heading-export/document.pdf new file mode 100644 index 0000000000000000000000000000000000000000..5146443acc43a358a04db707eda6756c433f173b GIT binary patch literal 655 zcmZWn%WlFj5WM><_JY(NXyT9rRYFLm1yyP*D##7>V3Gxl(m1jWh5lp@{e$*Lx(+$9us{ik{lQ#3z2t18Cm{q&GsN zRyw7IT&Dv?71f4A=p4z%ePml$QZo?M=)P^oI(4r!yzy#ktJp3)2d^g#?rt72U!#?& zILhEu_bSg=%