Repository navigation
Release 0.3.0b2 - #5
Merged
Merged
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…left-tab fields, text-font boxes
R1 Word's page colour (one fill over the whole sheet of every page) was read
as a cover band and a 792pt running header strip: the text went into the
header and the body section's top margin passed the page, which Word
cannot export (holdout-2 h2_04, 3 -> 6 pages, char recall 0.37).
`_page_colour` reads the colour from its repetition over every later page
and removes its sheets in an infer pre-pass; the writer sets it as the
document background (w:background + displayBackgroundShape), so light text
on a dark page colour stays visible. `top_bands` never takes a full-sheet
fill on pages >= 2; `_strip_page_border` drops a page border's four rules
and corner joints. Backstops: a running strip past mid-page is no header
(detect_hf, logged), and `_config_section` clamps margins that leave less
than a body line (counted, logged; fires on no corpus document).
R2 A resume's skills list set flush right beside a coursework list ran four
right-aligned lines, tripping WP49's sidebar guard, and the page's dates
went behind a column break (h2_01 p7). `_two_sided_run`: a longer run is
still fields when every line stands beside a short left line at the
margin. The fields' edge is the band of right edges holding most lines,
not the widest line (the name block ended 23pt further right).
R3a Fields at a LEFT tab (`_left_tab_pairs`): >= 3 sharing the x, the x the
fields' own (every line starting there is a field beside a margin label),
a style contrast in at least half the pairs (a contents page sets its
numbers in the titles' face), and _ROW_MAX_SHARE never waived.
Text-font boxes (`_true_text_boxes`): a text face whose glyph boxes are 5.6x
its size (Word's Cambria) stood every line tens of points above its
baseline, and box-top order set entries above their headings. Only a
face that sets words; maths and symbol faces keep their boxes.
All behind infer(tab_layouts=...) -- off under the gdocs profile -- with a
switch each in infer.WP59_RULES. Fixtures: tests/wp59_fixtures.py; tests:
tests/test_wp59_resumes.py. Cut per the plan review: R3b (typed underscores),
R4 (column rules), FontAwesome ActualText, h19's underlined-section table.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-identical) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lock once Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l is no page colour) holdout-2 h2_29 (NPS brochure, 2 pages, a full sheet on page 2 only) went 3 -> 4 pages when that sheet was read as the page colour. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ests - `_true_text_boxes` neither counts nor corrects a rotated line (its baseline is not a y), and re-boxes only blocks it changed. - A page colour is an opaque sheet (a translucent one is a tint over the page). - `_strip_page_border`: the frame lies in the paper's edge band (0.08 of the page, where Word measures a page border from; one-inch text margins are 0.118), its four sides close one rectangle, no other rule ends on a side (a table's row and column rules end on its frame), and every text line lies inside it. Control fixture: a table drawn to 36pt from the paper's edge. - Tests: `_true_text_boxes` (text face corrected; maths face, ordinary boxes, a rotated line and the flag off left alone) and detect_hf's tall-strip guard (past mid-page no header; a real strip kept; flag off as before). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ers, and the h2_29 and h19 acceptances Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts, left-tab fields, text-font boxes) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A release review found the exam harness hard-wired to holdout-2 (a BLOCKER): run for 0.3.0b2 it would refuse, or --rerun-reason would record b2's exam as a rerun of holdout-2 and void single-use. Each sealed set is now one row of EXAMS in holdout_exam.py, selected with --exam (default holdout3; holdout2 kept for history): the release, the sealed SHA-256 and document count (holdout-3's read from its registration, docs/evidence/holdout3-registration-2026-10-12.json: 07d62299..., 41), the set directory (C:\lotmp\rv_hold3), the manifest name (found by its SHA-256 if the name differs), the promise fields (promised_b_narrowed, else promised; a field some documents lack is refused), the ledger (holdout3-exam.ledger.jsonl), the state suffix (.holdout3), the evidence prefix (holdout3-) and the container tag. `resolve` prints the row for the shell without opening the set; holdout_exam.sh, holdout_sweep.py (no default manifest any more), holdout_word.py and holdout_dryset.py take everything from it. The guard takes the exam's prefix and counts only <prefix><date>[-rerunN] .json (and the dry run's) as results, so holdout3-registration-....json is not mistaken for one; with its own ledger, holdout-2's finished exam does not block holdout-3, and a second holdout-3 run refuses. R2's and R3's denominators are the exam's own promised documents, recorded with the field they came from. --expect-promised N refuses before anything is converted unless N documents are promised (holdout-3: 35, Chrome prints included). RELEASE is 0.3.0b2 in beta_readiness.py and final_set.sh; the scorecard's "not tagged" line names the release it read. beta_waivers.json is untouched: the y37 waiver granted for 0.3.0b1 reads as stale under 0.3.0b2 until the owner renews or deletes it, and test_the_committed_waiver_file says so rather than demanding every waiver name the current release. The host Python in holdout_exam.sh and final_set.sh is $EXACTDOC_PY, else a .venv in the tree or in the main checkout (git's common dir), else python3, instead of one user's absolute path. Tests: holdout-3's SHA pinned and read from its registration; every path and prefix resolves together and differs from holdout-2's; holdout-2's finished ledger and results do not block holdout-3; a second holdout-3 run refuses (a resume does not); a wrong SHA and a wrong document count refuse; the promise field and R2's denominator; --expect-promised; locating the manifest by name then by hash; the results pattern. Synthetic directories only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…single use per exam Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tale release docs, no local paths
S4 The section backstop clamped only the top margin, so a bottom margin
within one line of the page height still left a section with no body
(Word refuses it). Both margins are now clamped: the bottom gives way
only when it alone leaves no body, the top takes what remains. The gdocs
switch is reset in write_docx's finally. Tests reach the bottom-margin
case, sane margins, and the reset. The trigger is unchanged, so no
document that did not fire before moves (it fired on none).
S1 CHANGELOG: WP59's entry under Unreleased, naming page colour, tall strip,
page border, two-sided lists, left-tab fields and text-font boxes.
S3 docs/releasing.md: 0.3.0b1 is published; the classifier is already Beta;
the Unreleased heading form; the v1.0.1 "Latest" retirement is marked
historical; examples use 0.3.0b2. docs/install.md: --pre is for betas,
not a pinned version. The step-3 "Protect main" text is unchanged.
Privacy: infer.py and testkit/churn.py no longer cite local paths.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ELOG, stale release docs, no local paths) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rule, off (WP60)
Long Word-made audit reports (DHS and OPM OIG) lost a page on their
highlights page and every later page sat one place late. The page is a
shaded panel (x 37-191) with the findings column beside it from x 196. It
was not read side by side, for three reasons, each now its own switch in
WP60_RULES (keyed on _TAB_LAYOUTS like WP49's, so the gdocs profile is
byte-identical):
* panel_gutter -- _side_splits asked for SBS_MIN_GUTTER (6pt) of white
beside every item. A drawn panel's edge is itself the boundary (as a
box edge already is to _split_lines_at_box_edges); beside a panel the
white need only be PANEL_MIN_GUTTER (2pt). DHS OIG: 5pt.
* sidebar_per_box -- _sidebar_cut judged each cut on its own piece, so
one panel could keep some cuts and have others welded back, and the
welded line read across sidebar and column. A bullet's hanging
continuation (25pt off the panel, 2.1em) failed SIDEBAR_GAP_EM where
the column itself starts 5pt off. _sidebar_boxes decides per panel,
measuring to the column's edge (lowest x0, or highest x1 for a panel on
the right, of the flow lines within the panel's height, cut pieces left
out).
* foot_not_band -- a footer line the furniture pass left in the flow (the
highlights page prints its footer without the folio) joined the band
and broke the panel's cell. A foot-band line that starts below every
panel is presented to the split search as spanning the measure.
R1b's hypothesis (Word's square wrap reaching under the sidebar) was traced
on DHS OIG 25-46 p3 and did not hold: the right column was dropped from the
band by the same 6pt gutter, and panel_gutter mends it. R1c's (a tint
panel whose art leaves text) was traced on OPM OIG p2-3 and does not match
either: p2's panel is part of a grid cluster with the table beside it, p3's
is an EMPTY panel that seeds a figure. Neither is implemented.
figure_frame (_frames_to_figures): a box that contains built figures and
holds only short labels (no run of text as wide as half of it), with its
figures covering FRAME_FIG_MIN_SHARE of it and not the page itself, is the
chart's frame: the figure grows to it and owns its labels. It removes the
labels drawn twice on DHS OIG 25-46 p9 and Llama 2 p17, and it is OFF: by
construction it turns the labels' text into the picture that already paints
them, so the page's text reading-order LCS falls, which the acceptance does
not allow, and its census flips 40 boxes across 9 documents.
Census (both corpora, holdout-1, holdout-2; per page boxes, figures,
layouts, text drawn twice, reading-order LCS): with the shipped switches 5
documents change a page, none worse on LCS or text drawn twice: DHS OIG
25-49 p4 and DOE OIG 26-22 p4 (box -> side-by-side), y28 p3 (the footer
leaves the sidebar's cell, the panel becomes the shaded cell), y06 p68 (the
"2025 Tax Table" tab beside its heading), CISA IR p21 (LCS +4).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the footer (WP60)
foot_not_band kept the highlights page's folio-less footer ('DOE-OIG-26-22')
out of the sidebar band, and the region reader then wrote it as a paragraph
AFTER the band: a line the page had no room for, and the footer printed
twice (the document's footer part states it already). Measured on DOE OIG
26-22 in the canonical LibreOffice: 4 -> 5 pages, the one line of the last
paragraph and the footer echo on a page of their own.
_drop_footer_echoes (same switch, foot_not_band): on a page with a drawn
panel, a line in BOTZ below every panel whose text (letters only) the
footer parts already print is consumed as that footer.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
_drop_footer_echoes fired on every page with a drawn panel. MMWR's summary boxes fill a column of two (0.48 of the measure, the population SIDEBAR_MAX_FRAC already separates), and on y60 the dropped echoes moved the product render's placement: dy_p50 32.6 -> 43.7, a criterion-8 regression against final-71af76e. The rule is about sidebar pages; it now counts only panels narrower than SIDEBAR_MAX_FRAC of the measure. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With panel_gutter, a layout row puts the shaded panel in its own cell, and the text side after it owned the gutter before it: its cell began at the panel's fill (x 239 on DOE OIG 26-22's highlights page) while the column's text starts at 252. Read 13pt in from its cell's edge, every paragraph of the column was pinned to its source line breaks by the writer, and in the canonical face, a hair wider than the source's, each pinned line wrapped one word early: the page ran over, 4 -> 5 pages (raw lane). Before WP60 the left side was not a single panel (the folio-less footer had joined it), so the column cell began at its text and no break was pinned. The white between a panel's fill and the column beside it is now a cell of its own, exactly as the white between two panels already was, and the column's cell starts at its text (same switch, panel_gutter). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…enough (WP60, review S5) _drop_footer_echoes tested the foot line's letters as a SUBSTRING of the footer's: a foot-band line 'of' or 'Page' that the footer happens to contain was consumed. It now tests whole tokens (letters and digits) as a contiguous run of one footer paragraph's tokens, and only a line of at least FOOTER_ECHO_MIN_TOKENS (2) tokens or FOOTER_ECHO_MIN_CHARS (6) characters: 'DOE-OIG-26-22', 'www.oig.dhs.gov' and 'OIG-25-49' still are their footers' echoes; 'of', 'Page', a bare folio and a partial word are not. Synthetic fixture in TheFooterEcho. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ers, and the h2_21 acceptance The strict gate at b5c1eac in both lanes (2136 tests OK, FAILED_STEPS=0); criterion 8 against the sweep of record (only the y37 waiver, stale since RELEASE is 0.3.0b2, at WP59's numbers); gdocs word/*.xml identical on all 161 convertible corpus + holdout documents; the raw-profile mover census (9 of 161) with every mover's lanes and Word rows; R2 shipped off with its reason; R1b and R1c traced and not implemented; the h2_21 acceptance (coordinator, 2026-10-13); and the follow-ups. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…review)
The final review probed _drop_footer_echoes and confirmed silent text loss:
on a page with a narrow callout and the footer 'Audit of Border Security
Operations 4', a paragraph whose short last line ('Border Security.' or
'Operations.') sits in the foot band is whole words of the footer, and was
consumed. A footer is the last thing on its page: a line now counts only
when no line of the page's blocks, consumed or not, starts below its foot.
The probe is a negative fixture in TheFooterEcho; it fails on the previous
code and passes now, with the five earlier footer-echo tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ow; gate and movers re-checked 0cffc5b: _drop_footer_echoes counts only the page's lowest text row. Strict gate on 0cffc5b PASS in both lanes (2137 tests OK, FAILED_STEPS=0); the 9 movers' word/*.xml identical to b5c1eac in raw and product. Follow-up logged: foot_not_band's widening has no SIDEBAR_MAX_FRAC width check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…r column, footer echoes, gutter cells (R2 shipped off) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… what is there The holdout-3 exam on 34d4413 refused at step (a), before any start was recorded (no ledger, no conversion): the sealed manifest (SHA-256 and 41 documents as registered) carries neither promised_b_narrowed nor promised, the two names WP63 guessed without opening the set. The refusal now names the boolean fields every document carries, with how many are true, and every document's field names -- never a document's own classification -- so the row can be corrected without anyone opening the sealed manifest. Run through the harness (verify-manifest, read-only), it said: "boolean fields on every document: promised_narrowed (35 true)". The holdout3 row now reads exactly that field; 35 is the count the coordinator expected (--expect-promised 35), and verify-manifest now passes: "41 documents, 35 promised under promised_narrowed". Tests: the diagnostic names fields and counts and no document; holdout-3 reads promised_narrowed only (holdout-2's field is refused for it); the generic verdict CLI test names its exam (holdout2). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…efused at step a, before any start) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ence Release measurement, integration 34d4413, final_set.sh steps b,c,d,f on a clean detached worktree in the canonical image (bab1cfc0): - strict gate PASS (2,137 tests; both lanes 16/16); product and raw sweeps 90/90 with no error rows; Word 90/90 on both flavours with no error, hang or repair prompt; every quiet serial timing within criterion 2 (worst: y06 product 163.9 s of 189). - Docs lane reused, not re-flown: this head's gdocs DOCX have byte-identical parts to the ones b1's live sweep uploaded on all 90 convertible documents (the 5 unsupported refused on both sides). - beta-readiness-2026-10-13.json: NOT READY (9 pass, 1 fail, 3 reported), run with --allow-mixed-commits and --allow-stale for the reused Docs rows. The only failure is criterion 8: y37 dy_p50 27.64 -> 32, whose waiver was granted for 0.3.0b1 and is stale under 0.3.0b2 -- an owner decision, recorded as pending; beta_waivers.json is not edited. - release-measurement-2026-10-13.json: inputs by path and SHA-256, the timings, the Docs identity, and why the exam ran on 61044f1. Holdout-3, run once on 61044f1 (34d4413 + the a4fac77 tooling fix; the first start on 34d4413 refused at step (a) before recording anything): holdout3-2026-10-13.json/.txt, announce: no. R1 PASS (0 crashes, 0 refusals, Word 41/41 clean); R2 FAIL (short promised page-exact 20/26 in LibreOffice and 18/26 in Word; word recall >= 0.9 20/26 and 18/26); R3 FAIL (4 short catastrophic: h3_15, h3_17, h3_22, h3_34; 1 long: h3_28). The sealed manifest is published after its single run, byte-identical (SHA-256 07d62299..., as registered), marked -text; like holdout-2's it names local scratch paths used for the exclusion check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ut-3 exam (announce: no) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/beta-bar.md records amendment 7, adopted by the owner on 2026-10-13: (a) R3's new wording, verbatim, for holdout-4 on; (b) its legitimacy conditions (it lands before holdout-4 exists; holdout-3 is re-scored under it as a reported line only, 0.3.0b2 stays announce NO); (c) the stop rule (one fix round, freeze Fri 31 Oct, holdout-4 sealed before it and run once by Wed 5 Nov, 0.3.0b3 tagged and announced by ~Tue 10 Nov whatever it shows; after b3 holdouts are a report card and R2's 80% the 1.0 target); (d) the owner's caveat that the dates are deadlines, not waits; (e) y37. holdout_exam.py: each exam names its R3 rule. v1 (amendment 6) stays with holdout2 and holdout3 for their recorded verdicts; v2 (amendment 7) is holdout4's: no output, doc_recall < 0.9, or |dpages| > max(1, 20% of the source), in either lane, with the reasons recorded per document. Lanes now carry doc_recall (every sweep and Word row already has it); a measured row without it makes a v2 exam invalid. The holdout4 row (release 0.3.0b3, rv_hold4, holdout4-* ledger and results) is refused as "not registered" until exactly one docs/evidence/holdout4-registration-*.json exists, which supplies its SHA-256, count and, if it names one, its promise field. `rescore-r3` re-reads a recorded exam's R3 under another rule from its saved rows -- reported, not a verdict -- and refuses unless the recorded rule reproduces the recorded verdict. docs/evidence/holdout3-rescore-r3v2-2026-10-13.json (reported, not a verdict; nothing converted): R3 v1 catastrophic h3_15, h3_17, h3_22, h3_34 and the long h3_28; under v2 h3_13 (doc_recall 0.89), h3_15 (0.77), h3_34 (15/12 pages) and the long h3_28 (109/50). R3 would still fail and R2 fails anyway; the recorded verdict stands. y37: the 0.3.0b1 waiver is deleted (testkit/beta_waivers.json is empty); criterion 8's accepted sweep is b1's final product sweep, final-71af76e-prod.sweep.json (SHA-256 4fb4ff9a...); 4(e) deferred to b3. Step g re-run by hand on the 0.3.0b2 release measurement final-34d4413: docs/evidence/beta-readiness-2026-10-13b.json, READY (10 pass, 0 fail, 0 unmeasured, 3 reported); criterion 8: 0 documents worse. Tests: R3 v2's thresholds and edges, either lane, the one-page floor, right- page recall left to R2, no output, the long allowance, doc_recall required under v2 only; the holdout4 row unregistered/registered/ambiguous; the re-score's label and both readings; the committed waiver file now strict again. Whole suite on the host: 2153 OK. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ow, holdout-3 re-score (reported only), y37 re-based to b1's final sweep Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ence (WP67) - pyproject.toml / uv.lock: 0.3.0b2 (classifier stays Beta). - CHANGELOG: `## Unreleased` becomes `## 0.3.0b2 — 2026-10-1x` (the tag day goes in on tagging), with the holdout-3 verdict (announce: no) and its known issues, and a new empty `## Unreleased` above it; the WP60 and WP63 entries in plain language. - README: the status callout names 0.3.0b2; the "What works" rows for short Word/Docs documents, résumés, long Word reports, long LaTeX, long documents in Docs and web prints read from holdout-3 (41 PDFs, 35 promised); "How we know" cites the 13 October release measurement and scorecard; the never-seen section is holdout-3's, with its four unusable short results named, a marked placeholder for WP66's re-scored R3 sentence, and 0.3.0b3 as the fix release. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-score, amendment 7); sealed registration digests - README: the scorecard link is the 13 October READY re-score (beta-readiness-2026-10-13b.json); the placeholder becomes the plain-language R3 v2 re-score of holdout-3 (3 short unusable instead of 4, and 1 long; the verdict stands), linked to holdout3-rescore-r3v2-2026-10-13.json. - CHANGELOG: one bullet for amendment 7 -- R3 for holdout-4, the stop rule, y37 re-based to b1's final sweep with its waiver removed -- linking the re-score and the READY scorecard. - tests/test_gate_mutations.py: a sealed artifact's digest also counts as pre-registered when a docs/evidence/*-registration-*.json committed before its exam records it as manifest_sha256. Holdout-3 was registered that way (holdout3-registration-2026-10-12.json, cae222a), not in docs/beta-bar.md, so the strict gate failed on the integration head itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… holdout-3's numbers) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Release commit for 0.3.0b2. It will be published quietly, not announced.
docs/evidence/holdout3-2026-10-13.txt.docs/evidence/beta-readiness-2026-10-13b.json.The strict gate passes in both lanes (2,153 tests). The Google Docs output is byte-identical to 0.3.0b1's.
After merge,
v0.3.0b2is tagged on the release commit, as described indocs/releasing.md.🤖 Generated with Claude Code