Ground truth: feature records
Applies to every fixture directory under packages/ai_dealer_connect/tests/data/code_guides/parser/fixtures/.
CodeGuideFeatureRecord is derived, not transcribed — one record can draw on six
different tables. Hand-transcribing all of them is not possible (the RAM 2500 guide
yields roughly 20,000 records), so the fixtures split the problem into parts that are each
fully checkable:
| layer | fixture | what it pins |
|---|---|---|
| annotation grammar | annotation_corpus.json + expected_rules.json | every distinct annotation block a guide contains → its OptionRules. Derived twice, independently, from rule-grammar.md; both reviewers agreed with each other before the parser was run against it. |
| cell recovery | expected_grid_p*.json | one whole STEP #3 page transcribed from the rendered image — every code, price, cell value and P mark |
| the rubric | projects/ai_stellantis/tests/code_guides/test_extract.py (synthetic) | each is_configurable branch, on hand-written primitives, with no PDF in the loop |
| the join | expected_features_spotcheck.json | specific finished records, each chosen to pin one rubric branch and each traced to a page in its source field |
| drift | manifest.json → feature_counts | the counters, re-derived from the pages after the original figures turned out to have been taken from parser output. A counter the pages do not settle is omitted rather than guessed, so it is simply not asserted. See fixture-verification.md. |
expected_features.json — the complete record set
Every fixture now carries the whole record set of its guide rather than a sample:
73,641 records across the ten, with expected_features_provenance.json naming the
printed structure and page behind each one. test_parses_every_expected_feature compares
the parser's output against it in full — no subset, no count-only check. That is what closed
the hole the table above was built around: while only a few dozen records per guide were
asserted, the totals were free to drift 10% unnoticed.
Completeness here is derived, and the layers above are what make it checkable. Nobody typed 73,641 records off a page. What was read by eye is the rule behind each class of record — on the pages the judgment calls below cite — plus the grid pages transcribed cell by cell. Each record's provenance entry names the structure and page it came from, so any disagreement can be taken back to the page that claims it.
Provenance strength is not uniform, and the file does not mark which is which. The
1,091 records added 2026-08-21 across the four RAM guides carry citations that were
located programmatically: GROUP DETAIL (*) inclusion (784), STEP #3 FLEET ONLY group
(214), standalone companion (72), STEP #3 INCLUDED W/ (13), and STEP #2 powertrain (8).
The structure named is real and printed and the page is where it
sits, but no reader re-read that page to write the line. The classes themselves were
established by eye: RAM 2500 p9 for FLEET ONLY contents, p70 for the INCLUDED W/ entry
that is not also a column, and RAM 3500 p137 for the (*) fan-out. Each was corroborated
by a sibling guide's independent hand read. Read those entries as "the page establishes
that this class of record exists here", not as "a person checked this record".
Row identity
One record per (option_code, model_code, model_year, trim_identifier, parent_package_code). parent_package_code is null only where the guide prints the code
as a line of its own — a STEP #3 / #4 row, STANDARD EQUIPMENT, a CPP, a powertrain offer.
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 claim the code is orderable alone;
is_orderable_standalone carries that, and is False on a row printed only as P.
A code printed both ways gets both records. RAM 2500 279402 prints XBJ at $495 with
only_one_of [XBJ, XF1], and again under p169's XBH GROUP DETAIL header with no price and no
rules. Containment goes in parent_package_code, a condition on it goes in rules.
The is_configurable rubric
Configurable means the buyer chooses it — not merely that the vehicle has it.
| source | configurable when |
|---|---|
| CPP codes (STEP #2) | the trim has more than one CPP; a lone CPP is not a choice |
| powertrain codes (STEP #2) | the trim offers more than one engine / transmission of that kind |
| PACKAGE CONTENT (STEP #2) | never — bundled into the package |
| STEP #3 grids | the row prints its own code in some column (orderable). A row that is only ever P is packaged; blank everywhere is not offered and is skipped |
| STEP #4 color & trim | always, unless every variant that applies to the trim is FLEET ONLY |
| GROUP DETAIL items, and the STEP #3 "Included in Equipment Groups" list | wherever the group is itself configurable for that (model_code, trim) — never fanned out blindly |
| STANDARD EQUIPMENT | never — fitted, not chosen |
When a record exists at all
The table above says when a source marks something configurable. It also decides whether a record is emitted, and the two are not the same question:
-
GROUP DETAIL and "Included in Equipment Groups" items produce a record for every
(model_code, trim)pair where the trim has the group — not only where the group is configurable. A trim has the group three ways: its STEP #3 cell prints the group's own code (a buyer's choice), or printsP(packaged — the trim has the content without choosing it), or the group arrives as standard content of that trim's quick order package.is_configurableisTrueonly in the first case.The narrower reading — a record only where the group itself is configurable — deletes options the guide prints. RAM 1500 p6 lists
ESD 6.2L SUPERCHARGED HEMI V8 SRT ENGINEwith bulletsBA7,JLW,MZP,NMX,MJC; p2 putsESDin the SRT column against CPP29G.ESDis that trim's engine, delivered by the package, so its five items exist on trimG.BA7 MZP NMX MJC MGT MGG JPE CFNappear nowhere else in the guide, so the narrow reading loses eight option codes outright.An option still unreachable on a trim has no record there: a blank STEP #3 cell where the group is listed means no record, even if the group is
E-marked for the model code. -
A group's own
Emark counts as evidence that it is configurable there. GROUP DETAIL marks each group header withEagainst the model codes that can order it at extra cost, and that is the guide stating the group is a choice on those codes. Color and seat groups (TX,V9,X3,XN,AL…) are evidenced only that way — they never appear as a STEP #3 orderable row — so requiring an earlier source drops every one of their items. Requiring theEmark is what keeps this from being a blind fan-out: an unmarked model code still gets no record. -
A STEP #3 / STEP #4 row produces a record only for the trims whose column actually offers it. A blank cell means no record, not a record marked unavailable.
-
STANDARD EQUIPMENT and PACKAGE CONTENT do emit records everywhere they are marked; they are simply never configurable.
-
A paint-type box emits no record of its own.
MONOTONE PAINT - APA,LOWER TWO TONE PAINT - APDand their kin are boxed labels that scope the color table printed beneath them — the same structural role asMOPAR OPTIONSandFLEET ONLY. Measured, the box isx 50.6 → 329.2on every guide in the set, stopping short of the$FWPedge at 356.2: no price cell, no trim cell, nothing to extract. The paint code itself gets its records from STANDARD EQUIPMENT, where it is marked.What the box contributes is two implicit rules on every color row in its block:
requires_one_of [the box's code], because choosing a color from that table means choosing that paint type; and anonly_one_ofacross the paint-type codes of the section, because only one paint type can be selected. Neither is printed on the color rows; both follow from the block structure.
Which record carries the rules
An annotated row's OptionRules go on the record for the row's own code. The standalone
companion of a parented record does not inherit them, and a group's items do not inherit
the group's rules — an item carries only the rules printed against the item itself.
Clauses bind the row whose cell they sit in, and only that row's printed codes. They do
not fall through to later rows, and they do not rise to the block. On RAM 279402 p10's
PRIMARY COLOR table, CERAMIC GREY CLEAR COAT carries M/H ASH; M/H -X9 and
HYDRO BLUE PEARL COAT carries M/H ASH. Both rows are blank in the left code column, so
each set of clauses attaches to a single code (PDN, PBJ). BRIGHT WHITE CLEAR COAT,
DIAMOND BLACK CRYSTAL P/C and the rest carry none.
A paint-type box is a cell of exactly this kind, so the clauses printed inside it — on RAM
279402 p10, M/H A7K OR A7M; N/A W/A7A; N/A W/A7J; N/A W/A7L; N/A W/AL9 — bind the box's
own row and do not distribute to the colors beneath it.
Merging: True beats False; the first non-null description and MSRP win; rules union
and de-duplicate. Two overrides run last:
- codes beginning
5U/5Vare shipping/routing codes and are forced non-configurable; - a code bundled by PACKAGE CONTENT outranks a GROUP DETAIL "configurable" mark — but a real orderable / CPP / color signal still wins.
Judgment calls (deliberate, flag if you disagree)
-
STEP #3 columns mean different things in different guides. The RAM guide heads them with equipment-group tiers (
BASE,A7B,A7C,A7D) that apply to every trim of the model code, so a row is scoped to the model code and availability is aggregated across the tiers. A row is orderable if its own code is printed anywhere, and packaged if it is only everP. The Jeep guide heads them with LLP designators (2_E), so each column is a trim and is read on its own.Grid.columns_are_trimsdecides which. -
N/Cis a price of0.0, notNone.Nonemeans the guide printed no price at all. A credit prints in parentheses and becomes negative —(400)→-400.0.A price answers "what does this option cost in this record's context", and a parented record's context is inside that package. A row prices itself once on its own and again for the packages that include it, the second band naming them in its qualifier. RAM 2500 p27 prints
BLIND SPOT AND CROSS PATH DETECTION (XAN)at 640 / 695 unqualified, then a band qualifiedW/AAN OR AAU OR A7G OR A7HatN/C; the annotation beside it readsINCLUDED W/AAN OR AAU OR A7G OR A7H; N/A W/XBC. SoXANcosts 695 on its own and 0.0 under any of those four. The same row structure givesDSA495 on its own and 0.0 underA7E/A7F/A7G/A7H.This is what distinguishes the three values, and all three are readings of the page:
value the guide prints Noneno price for that context — a GROUP DETAIL, PACKAGE CONTENT or STANDARD EQUIPMENT item, where none is printed at all 0.0N/Cfor that contexta figure that figure for that context The transcriptions gave three different answers for one row:
XANbare 695 (right),XANunderA7G/A7H695 (the standalone price on a packaged record), andXANunderAAN/AAUNone(which claims nothing was printed). 460 records across the three RAM heavy-duty guides were corrected to the band the page prints for them, each checked against that row's own bands. -
A sales code is the last parenthesis of an item's name, not the first.
UCONNECT 5 NAV W 12.0" DISPLAY (USA) (UBQ)is codedUBQ, and8-SPD AUTO 880RE TRANS (MAKE) (DC1)is codedDC1. Taking the first parenthesis silently yieldsUSAandMAKE, both of which validate as option codes, so nothing downstream would have complained. -
A row's MSRP is its first variant that applies to the trim. Rows carry several price sub-rows: a base price then discounted or fleet-only alternates, each tagged with a qualifier like
W/XBC,FLEET ONLYorW/2_A. The first sub-row whose qualifier does not exclude the trim is that trim's price. This is what makesV9X9cost $410 fleet-only on trimAand nothing on trimC— one printed row, two genuinely different answers. -
FLEET ONLYmakes a row non-configurable for the trims it covers: it is not a retail-buyer choice.is_orderable_standalonestaysTrue— the code is still a real standalone order code. -
(*) Package X includes package Y contentfans Y's items under X, wherever X is configurable. Y's own configurability is irrelevant to the fanned-out copies. -
Trailing
W/<CPP>on a rule scopes it to one trim (seerule-grammar.md); rules scoped to a trim that the row does not belong to are dropped, not kept and ignored. -
A powertrain matrix clause belongs to the CPP, not to the engine or transmission. The clause is printed inside a trim's cell beneath the CPP code —
29EoverM/H WRE. Grand Wagoneer p17 settles which record carries it: one powertrain row (EL2/DCD) spans three CPP columns,24Q,24Zand24K, and only24Kis markedM/H RJA. The engine and transmission are the same in all three cells, so nothing about them can be what the clause qualifies. It goes on the(trim, CPP)record. -
AP9 SPECIAL PAINT PROCEDUREscopes a color table but is not a paint type. On the Charger it heads a block exactly asAPAandAPPdo, so a color printed under it does require it and appears in that color'srequires_one_of. It is held out of theonly_one_ofrule, which states that only one paint type may be chosen: a special-order procedure is not an alternative to monotone or two-tone paint. Flag this if you read it differently — it is the one paint code in the set whose treatment is not mechanical. -
short_descriptionneeds a precedence rule, because the guide prints several names for one option. Of the 110 distinct disagreements between the ten transcriptions and the parser, 88 are cases where both texts are printed, in different places:
- RAM 279402 names
ESLENGINE - 6.4L V8 HEMI HD ENGINE (ESL)in STANDARD EQUIPMENT on p172 and6.4L V8 HEMI HD ENGINE (ESL)in the STEP#2 powertrain cell. APAisMONOTONE PAINT - APAin the STEP#4 box andMONOTONE PAINT (APA)in STANDARD EQUIPMENT on p174.- Grand Cherokee L prints
POWER SUNROOFin a STEP#3 row andPOWER SUNROOF (GWA)in PACKAGE CONTENT.
There is no fact of the matter to read off the page — only a choice — and the ten readers made it inconsistently, including within a single fixture.
The rule: STANDARD EQUIPMENT's name wins where it names the option, because that
table is the guide's own alphabetical catalog of equipment names, and because a code
like APA has a record at all only by being marked there. Otherwise the first source
to name it stands.
This is the one field where the fixtures were aligned to a stated rule rather than to a
unique printed string, and it is recorded here because that is a weaker kind of ground
truth than the rest of this document rests on.
11. A CPP's price is the FWP/MSRP cell its powertrain maps to. One powertrain per
package, so the CPP printed in a trim's cell on a POWERTRAIN AVAILABILITY row maps to
exactly one (prefix row × trim column) of the price tables beneath it. RAM 1500 p2 puts
25Y in the RUMBLE BEE column of the EZH / DFR row, and p4's 25- row of that column
reads 56,156 / 59,995. Both figures are captured. The same page also prints
NET PACKAGE VALUE PRICE — 0 / 9,200 / 18,400 / 32,200 — which is what the package is
worth, not what the CPP costs. One transcription took that row and was corrected.
12. A paint-type box contributes to a record, it does not create one. Following from the
ruling above that the box emits nothing of its own: its heading and its clauses merge into
whatever record another source makes for that code, and where none does the box adds
nothing at all. RAM 279402 p125 boxes MONOTONE PAINT - APA inside DJ7X91's section, and
STANDARD EQUIPMENT does not mark APA on DJ7X91, so that model code has no APA record.
36 such records across six guides were removed. The heading only fills a description that
is otherwise empty; STANDARD EQUIPMENT's catalog name still wins.
13. A (*) includes package row's marks govern the fan-out, narrowed by the item's own
where they overlap. The row is marked in the model columns like any other. RAM 2500 p162
fans AAU into A7S. Several AAU item rows are marked only on the L- and H-series,
while A7S is offered on the M-series, so the row's marks stand there. RAM 3500
p137/p138 marks (*) Package A82 includes package A81 content on all four L-series codes
but A81's WD4 and WMG rows only on the two …92s, so A82 brings those two wheels
on the 92s alone. Intersecting unconditionally loses 1,284 records; ignoring the item's
marks invents 8. This is the one place two sets of marks combine rather than one governing.
14. A row whose qualifier names no trim may still name one in its note. RAM 1500 p5 heads
its STEP#4 color table with a single BLACK column — no column is a trim — and prices
CLOTH BUCKET SEATS (C5X9) under (S), with Included on Rumble Bee (2TY) beneath. The
2Tx reading is corroborated on the same page: the two rows below carry both a note and an
LLP designator and they agree, (2T1) with W/2_1 and (2T2) with W/2_2_.
15. Where the columns are trims, a row can price them by position. The Charger p23 prints
TRACK PACKAGE on two bands, ANN under 2_S at 4,995 and under 2_F at 4,495, neither
band qualified. The band that belongs to a trim is the one marking that trim's column.
16. Two PACKAGE CONTENT rows for one code: the permissive row wins. GC 2-row p2 prints
115V AUXILIARY POWER OUTLET (JKV) twice, once plain and once with W/O ABD set flush
right, marking different trims. Where a trim is marked on both, it has the item
unconditionally, and the qualified sibling constrains nothing, so the record carries no
rule. Where a trim is marked only on the qualified row — LAREDO ALTITUDE on p4 — the
rule applies. The record cannot hold the two rows apart, so unioning their rules
would attach excludes [ABD] to an availability that does not depend on it. Same
principle as True beating False on is_configurable.
A decision that looked open and was not
is_configurable for an item of a group evidenced only by its GROUP DETAIL E marks —
TX, V9, X3, XN and the other seat/color families, which never appear as a STEP #3
orderable row.
Pacifica p23 marks -X3
with E under exactly five model codes and -XN under two. The item rows carry no marks at
all, and neither group appears anywhere else in the guide except as M/H -X3 annotations and
inside STEP #4 codes (SJX3, ULXN). The E marks are the only evidence there is, so the
rule stated above — a group's own E mark counts as evidence that it is configurable there —
is what the guide supports. Records exist and are configurable: five CXT, two CEG, which
is what the parser produces.
A judgment call cannot move a total
Both defensible positions above still emit the record, so the decision can only move the
configurable counter — never total, with_parent or with_rules. Where those moved, it
was a defect every time. Six rules were read off the pages and the parser was corrected
against them; fixture-verification.md records the rules and what was wrong.
The feature_counts fixture was one of the casualties: it had been taken from parser output
rather than from the pages, so it enshrined the parser's own errors — the Pacifica total was
1,653 where the page gives 1,607. It now lists only counters the pages settle.
What is not covered
- No fixture transcribes the RAM guide's ~20,000 records; correctness rests on the layered argument above, not on a single golden file.
- GROUP DETAIL does not repeat every equipment group's contents, so the STEP #3
"Included in Equipment Groups" table is read as a source in its own right.
UBQ(UCONNECT 5 NAV W 12.0" DISPLAY (USA) (UBQ)) appears only there. is_fleet_onlyis decided per price variant. A row whose variants disagree resolves per trim, which is right for STEP #4 but has no test case in STEP #3, where it does not occur.