Verifying the feature fixtures against the PDFs
On
tools/paths below. The one-off measurement scripts this document cites —accuracy.py,measure_page.py,evaluate.py,crosscheck.py— were removed when the repository was cut back to what the test suite needs. The measurements they produced are quoted here and the checks that mattered have since moved intotests/. Treat atools/...command as a record of how a figure was obtained, not as something to run.
The feature_counts fixture was originally taken from parser output rather than from the
pages, so it enshrined four of the parser's own errors as the thing to match. This document
records the re-derivation: the page decides, and where a fixture disagreed with the page
the fixture was wrong.
Method: render the page, read the rule off it by eye, check the parser against the rule, fix whatever disagrees. Not "what does the parser say" — its output is never the source.
The seven rules below are the ones that re-derivation produced. They are the reason the fixtures are now a complete hand read rather than a set of totals.
The rules, as read off the pages
R1 — a group's own E mark makes it a choice on that model code
Pacifica p23. -X3 BLACK/BLACK/BLACK carries E under exactly RUCL53, RUCH53, RUCT53,
RUFH53 and RUFT53; -XN BLACK/BLUE AGAVE under RUCS53 and RUFS53. Their item rows
(BLACK SEATS WITH AXIS II (CXT), BLUE AGAVE SEAT COLOR (CEG)) carry no marks at all,
and neither group appears anywhere else in the guide except as M/H -X3 annotations and as
the interior half of STEP #4 codes (SJX3, GJX3, ULXN). The E marks are therefore the
only evidence, and they stand on their own → 5 CXT records and 2 CEG records, all
configurable.
features-ground-truth.md states this rule, and it is not an open decision: the E marks
are the only evidence the guide offers, so they decide.
R2 — an item's own ■ mark restricts which model codes it applies to
Pacifica p22. ADG FAMILY TECH GROUP is E on RUCH53/RUCT53/RUFH53/RUFT53, but
STOW N VAC INTEGRATED VACUUM (CJW) is ■ on only RUCT53/RUFT53. A group's contents differ
by model code, and a blank ■ means the item is not in the group there — the same principle
the spec already states for grid cells ("a blank cell means no record").
Whether items carry marks is a property of the group, not the item: ABR marks every
item, AAC, AD4, AGF and the seat/color families mark none. An unmarked item inside a
marked group is not in the group there; a group with no item marks at all falls back to R1.
All 29 items on Pacifica p22–23 were checked by eye; the parser was wrong on 10 of them before R2 was written down.
R3 — a group listed as an option is offered trim by trim
Grand Cherokee p11, model code WLJP74, trims E and R. TECHNOLOGY GROUP II prints ADR
under the 2_E column and nothing under 2_R; likewise LIMITED ALTITUDE PACKAGE
(ACA), so neither group is offered on trim R and its items get no record there.
Where the guide lists a group as an option somewhere in the model code, that listing decides
per trim. Only where it is never listed at all does R1's E-mark fallback apply. The
fallback is for a group's own items: a (*) Package X includes package Y content host
fans Y's items wherever X is configurable, so an unlisted host is not evidence of anything.
Conflating the two inflated the RAM guide by 2,312 records before it was caught.
R4 — the page legend is a footer, not group content
E - EXTRA COST N/C - NO CHARGE N/A - NOT AVAILABLE P - PACKAGED is printed at the foot of
every page, and the two back-matter tables look alike.
R5 — a paint-group box is not a parent package
Pacifica p3. MONOTONE PAINT - APA is a boxed section label above the PRIMARY COLOR
sub-header — the same structural role as MOPAR OPTIONS and FLEET ONLY. Each color
prints its own code and price in the trim column (PW7, PXJ, PR6), so the color is
what you order. MONOTONE PAINT (APA) is itself listed under STANDARD EQUIPMENT.
R6 — is_orderable_standalone is the record's own answer
A code named only inside a package's contents gets parented records and no standalone one, so
a null parent_package_code does not by itself say the code can be ordered alone. Each record
carries is_orderable_standalone, which is False where the guide prints the code only as
P.
R7 — a rule that stops short of the description column divides a row, not two rows
This is the error that has cost the most: six tables across four guides recorded wrongly, in both directions, and reading the page by eye produced the wrong answer every time — at page scale a band rule and a row rule are indistinguishable. It is settled by arithmetic instead:
Corrected after hand-reading all ten guides. The test is not "does the rule cross the description column" but does it reach BOTH table edges. Six distinct narrower extents exist, and one of them —
50.58→329.22, the boxed paint-type label — touches the left edge and is not a row boundary. On the RAM guides50.58→446.22is a band where a tier cell spans both halves (311 of them on one guide) and374.58→446.22is a price sub-row. Requiring only the left edge splitsAGSon RAM 2500 p6 and loses nine packages' tier cells per section; requiring both edges is correct on all ten. Merge abutting collinear segments first — this family draws each cell's bottom edge as its own path, so50.6→313.2+313.2→356.2is one rule with no divider at 313.2.
$ python tools/measure_page.py 279384 14 --at 203.4
290.70 ROW 50.6 → 590.2
302.10 band 356.6 → 590.2
313.40 ROW 50.6 → 590.2
The description column runs x 50.4 → 356.4. The rule at y 302.1 begins at x 356.6, so it never enters that column: y 290.7 → 313.4 is one row, in two bands. Three patterns occur, and all three are one row:
| pattern | example | what the row holds |
|---|---|---|
| a code per trim column | 279384 p14 MOPAR BLACK SIDE STEPS, MRU under 2_E then under 2_R, 875 on both | the union of the bands' cells |
| a price per variant | 279384 p14 DUAL-PANE PANORAMIC SUNROOF, 1,595 then N/C W/ACA | the first price — the schema has one msrp, so the variant is lost |
| the same cell repeated | 279384 p21 PALERMO LEATHER SEATS, four bands ECT7 ECT7 ECX7 ECX7 | the union, deduplicated |
A first pass twice went the other way and split a row the page rules as one: GC 277944 p3 into two, and GC-L p17 into five. It then reported "the parser merges split rows" as the dominant defect. The rules said otherwise. The geometry decides this, not a reading of the rendered page — at page scale a band boundary and a row boundary look identical.
What the parser had wrong, and why
| defect | cause |
|---|---|
fanned every group item across every E-marked code (R2) | its mark test was the character set {"E","X","■"}, but the ■ is glyph I in ZapfDingbats. It found no item marks at all and silently fell back to the group's scope. Now tested by font. |
| let an item's mark stand in for the group being a choice (R3) | emitted 17 Grand Cherokee records on a trim the group is not offered on, and marked them configurable. |
| 366 descriptions carried the page legend, 45 of them running to 11,661 characters (R4) | the whole back-matter range was handed to both back-matter parsers, and the span list had no bottom bound, so STANDARD EQUIPMENT lines and page footers folded onto the last GROUP DETAIL item. |
read STEP#2's PACKAGE CONTENT as an option grid | a table under no STEP banner was kept as "continues the page before", and a two-page PACKAGE CONTENT table is that pattern. Every bundled item came out orderable on every trim — GWA a buyer choice on a trim that only receives it inside 22J. Now excluded by its printed label. |
| a cell spanning several bands kept its text once per band (R7) | "P P", "GWJ GWJ", "V9X9" ×7. Cell.lines() now splits a cell by baseline and each distinct line is kept once. |
| a wrapped column heading lost every line but the last | BLACK/EBONY RED read as RED. The $FWP/$MSRP labels sit on the heading's own line, so the band is now read from that line downwards. |
stopped after the first page of a PACKAGE CONTENT table | an unconditional break in the page loop. The RAM 1500's table runs p3–p4; 26 of its 86 rows were never read, and 29 option codes the guide lists had no record at all. |
| resolved a multi-trim block's scope by print order (ruling 6) | scoped_to_trim has one slot and RAM 279402's ASQ block scopes clauses to three trims; the parser kept the last and the grammar said nothing. Now documented as first-in-print-order. |
Where the fixture stands
expected_features.json is now a complete hand read: every record of every guide, read off
a rendered page, 73,641 of them, reproduced exactly — all twelve fields, including record
order. manifest.json's feature_counts are derived from that transcription rather than
from parser output, and
projects/ai_stellantis/tests/code_guides/test_contract.py::test_feature_counts_are_stable
asserts them.
What a complete transcription still does not cover is worth saying plainly:
- Grid transcriptions reach STEP #3 and STEP #4 only. A record derived from STEP #1/#2,
GROUP DETAIL, STANDARD EQUIPMENT, the powertrain matrix or PACKAGE CONTENT is compared
field by field against the hand read but never against the page's own cells — roughly 60%
of the record count on the RAM guides.
test_contract.py::test_every_source_is_transcribed_cell_by_cellskips with that reason, so it is stated in every run rather than only here. - 1,091 provenance citations were located programmatically, not typed by a reader. The
class of source was read by eye; the individual page reference was not.
test_feature_provenance_covers_every_expected_recordsays so where the file is read.