- Status: Accepted
- Date: 2026-08-22
smud-ci-tod1 prices its rates for 2028 and beyond in a table none of the
existing recognizers claims, on Section VIII, "Transition Schedule":
Season and Charge Component Unit 2028*
CITS-0: C&I Secondary 0-20 kW
System Infrastructure Fixed Charge per month $44.45
Maximum Demand Charge per kW $4.101
Non-Summer Peak per kWh $0.1506
...
*Subject to future rate increases.
Every other priced table this parser reads states a row's unit inside the
row's own label (unit_tail, read to the end of the label) or once, in a
heading over a whole block (sheet_rates.py, dated_charge.py's
parenthetical heading). Here the unit sits in a column of its own, headed
literally "Unit". And every other table dates a price from a row of its own
("Effective May 1, 2025 $8.597") or from the sheet's own footer. Here the
date is a bare year printed once, in the header, over the whole column: the
table prices rates after this sheet's own effective date, so the sheet's
footer cannot date it and no row states a date of its own either.
ADR 0007 already found
this table has no ruled border, so reading its cells off pdfplumber's grid
detection the way the proration table's cells are read was not available.
ADR 0004 is what applies
instead: an unruled table's columns are read from the x positions of the
header's own words, the same way the holiday table's and the standby
charge's columns already are.
A new recognizer, transition_table.py, claims a section whose header line
divides into a label column, a column literally headed "Unit", and one or
more columns each headed with a bare year (optionally carrying a footnote
asterisk, read and kept exactly as printed: "2028*", not "2028"). Both
conditions are required together. Requiring the word "Unit" specifically,
rather than any column past the label, is what keeps this from firing on an
ordinary table that happens to end a row in a number; requiring a year
header on top of that is what keeps it from firing on "Unit" appearing for
some unrelated reason.
Every row's label is whatever sits left of the "Unit" column, read as one
run of text the way a table row's label always is here. Everything to its
right is assigned to the "Unit" column or a year column by nearest centre,
assign, the same
attribution rule every other unruled table in this parser uses. A word that
fits no column refuses the row whole: this is the same discipline ADR 0004
already established for the standby charge, applied to a third column
shape rather than a second.
A rate category caption above a run of rows, "CITS-0: C&I Secondary 0-20
kW", is the same shape rate_table.py already reads off a caption row.
Reading it required only calling the same function; _category_code moved
out of rate_table.py into recognizers/base.py as category_code, public
now that two recognizers use it, rather than being copied.
The header row is credited only once a row was actually dated from it, the same rule ADR 0007 applies to the proration table's header: a table this recognizer found the shape of but could not read a single price from is not "understood", and its header should not look otherwise.
smud-ci-tod1gains all seven prices Section VIII states for its first rate category (CITS-0): recognized lines 128 → 142. The other three SMUD schedules and the golden output of every schedule that carries no such table are unaffected, because the shape this claims does not appear on them.- Every emitted charge's
effective_fromis the bare year exactly as printed, footnote asterisk included. What the asterisk means -- the footnote text two lines below it -- is not connected to the charge it marks; doing that would need a general mechanism this parser does not yet have for tying a footnote marker to what it marks, and inventing one narrowly for this table risked getting the general case wrong later. The footnote's own two lines are left fornotes, same as before. smud-ci-tod1's rate categories pastCITS-0are not addressed by this change; nothing about the recognizer is specific toCITS-0; it happened to be the only category priced on the page this table currently occupies.