pubid 2.0.21 → 2.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,368 @@
1
+ # ISO historic identifier schemes — old faces, modern counterparts, and the DAM family
2
+
3
+ **Status: IMPLEMENTED — the owner ruled Option B (decode-to-modern) for the
4
+ TYPE-first ballot faces and `Am1` on 2026-11-12 (this branch carries the
5
+ ruling). §1–§8 are the study as committed on `study/iso-historic-schemes`
6
+ (92befebe); §9 records the implementation, the replay/corpus gates, and the
7
+ per-anchor stored-twin verification, and corrects two study findings
8
+ (§9.1).** Companion to `data/iec/legacy-notation.md` (the IEC-side study;
9
+ that doc owns the ISO/IEC joint rows' cross-check and the IEC-only faces)
10
+ and to `data/iec/stage-codes.md` (the house precedent for
11
+ committed-provenance reference documents). This document owns the
12
+ **pure-ISO** rows of the owner's anchor list and the ISO-wide scheme
13
+ history.
14
+
15
+ ## Provenance (all sources local; iso.org serves 403 to non-browser clients and yields no fetchable evidence)
16
+
17
+ | Source | What it evidences | State used |
18
+ |---|---|---|
19
+ | `relaton-data-iso` (fresh clone of git@github.com:relaton/relaton-data-iso, main @ `dd368d8de2` "update documents", crawl state 2026-11-12) | the real stored docidentifier faces; 80,202 record files; 200,189 distinct ISO-prefixed docidentifier face strings (primary + `iso-undated` + `iso-reference` variants) | grepped + bulk-probed 2026-11-12 |
20
+ | same repo, history deepened to 560 commits (`git fetch --deepen=500`) | whether historic faces EVER occurred in stored docidentifiers; when normalized away | `git log --all -S` 2026-11-12 |
21
+ | pubid `main` @ `be6a43c2` (2.0.21) — `data/parg/iso.parg` edition-2 artifact, `typed_stages` (115 rows) and `typed_stages_supplements` (64 rows) tables, builder, live probes | the current decode of every face probed below, and the two-position token split | probed live 2026-11-12 |
22
+ | `pubid-iso` **1.15.21** installed gem (the v1 reference implementation; parslet grammar `lib/pubid/iso/parser.rb`, stage ladders in `identifier/{amendment,corrigendum,supplement,addendum}.rb`) | the historical grammar's treatment; the v1 ladder that folded FPDAM into DAM | probed live 2026-11-12 |
23
+ | `spec/fixtures/iso/identifiers/{full,pass,fail}` (the 7,650-input corpus; classification identical to `pubid-testsuite/reference-docs/iso/SUMMARY.txt`: 7,647 pass / 3 fail, none DAM-related) | what the curated corpus pins | read 2026-11-12 |
24
+ | `data/iso/update_codes.yaml` | the pre-parse normalizations already shipped for legacy faces (`ISO/R 657/IV` → `ISO/R 657-4:1969`; `DIS Amd` → `DAM`; `FDIS Amd` → `FDAM`) | read 2026-11-12 |
25
+ | `references/iso-urn-specification.adoc` (house URN spec, RFC 5141-bis draft) | the stage-label table (40.00 = DIS/DAM, 50.00 = FDIS/FDAM per ISO Guide 69:1999) and the "legacy abbreviations accepted on parse, never emitted" rule | read 2026-11-12 |
26
+ | iso.org catalogue pages (the plausible origin of the owner-cited TYPE-first ballot faces) | **not locally verifiable** — `https://www.iso.org` returns 403 (curl, 2026-11-12); Wayback not attempted (rate-limited host, and the owner's citations stand in) | — |
27
+
28
+ ## 1. Scheme eras
29
+
30
+ | Era | Printed face | Evidence |
31
+ |---|---|---|
32
+ | ISO/R Recommendations (1951–1972) | `ISO/R 185:1961`, parts `ISO/R 105-1`, roman-numeral part `ISO/R 657/IV` | 1,734 `iso-r-*` records in relaton-data-iso, all stored modernized (`ISO/R 185:1961/Amd 1:1969`); the roman-numeral slash-part only survives as an `update_codes.yaml` rewrite (`ISO/R 657/IV` → `ISO/R 657-4:1969`) |
33
+ | ISO (1972–) | `ISO 1234`, parts `-N` modern; old printed parts with `/N` (`ISO 9075/1`) | slash-part face: **0 occurrences in any stored docidentifier** (current + 560-commit history), yet the grammar carries it as a first-class branch (`part_legacy_body = slash [space] !supp_stage_abbrev 1*word_digit`, `data/parg/iso.parg:79`) and normalizes: `ISO 9075/1` → `ISO 9075-1`, `ISO/IEC FDIS 9075/1` → `ISO/IEC FDIS 9075-1` |
34
+ | Supplement attachment | modern `base/Amd 1:2005` | every stored supplement record — published (3,790 `-amd-N` files) and ballot (835 `-dam`, 313 `-pdam`, 194 `-fdam`) — is base-first with a slash |
35
+ | Ballot faces (iso.org pages, not stored) | TYPE-first: `ISO/IEC FDAM 1 14496-12:2004` | owner-cited; **zero** occurrences in relaton-data-iso (current **and** 560-commit history, `git log -S` on all nine owner forms) |
36
+
37
+ The part separator `/` → `-` and year separator `-` → `:` flips are
38
+ accepted-on-parse today ("legacy" branches in the grammar): `ISO 1234-1988`
39
+ → `ISO 1234:1988` (parg `date = (colon / dash)`, comment "legacy dash form");
40
+ Unicode dashes (`‑` `‐`) accepted for parts.
41
+
42
+ ## 2. The DAM-family letter ladder
43
+
44
+ The letters are ISO/JTC1 ballot-stage faces of an amendment project. The
45
+ house URN spec (`references/iso-urn-specification.adoc`, per ISO Guide
46
+ 69:1999) fixes the semantics: the label is a function of (deliverable
47
+ type, stage code) — 40.00 is DIS for an IS project and DAM for an
48
+ amendment project; 50.00 is FDIS / FDAM / FDTS.
49
+
50
+ | Letters | Full name | Stage (pubid ladder) | relaton-data-iso records | URN stage codes carried (whole-corpus grep of those records) | Occurs in stored primary docidentifier? |
51
+ |---|---|---|---|---|---|
52
+ | PDAM | Proposed Draft Amendment | 30.00–30.99 (`pdam`) | 313 files (**308 joint ISO/IEC**, 5 pure-ISO) | 40.00 ×204, 30.00 ×109 | yes — `ISO 8485:1989/PDAM 1`, `ISO/IEC TR 9573-11:2004/PDAM 1` |
53
+ | DAM | Draft Amendment | 40.00–40.99 (`damd`) | 835 files (246 joint) | 40.00 ×834, 30.00 ×2 | yes — `ISO 10140-3:2010/DAM 2` |
54
+ | FDAM | Final Draft Amendment | 50.00–50.99 (`fdamd`) | 194 files (69 joint) | 50.00 ×194 | yes — `ISO 10328:2006/FDAM 1` |
55
+ | FPDAM | Final Proposed Draft Amendment | 50.00–50.99 (`fpdam`) | **0 files** | — | no (appears once, inside one abstract: `iso-iec-21000-14-2007.yaml`) |
56
+
57
+ - v1 (pubid-iso 1.15.21) folded FPDAM into `damd` as a legacy abbreviation
58
+ (`amendment.rb`: `damd: {abbr: "DAM", legacy_abbr: ["DAmd", "FPDAM"]}`,
59
+ harmonized 40.xx). pubid 2 gives FPDAM its own `fpdam` stage entry at
60
+ 50.00–50.99. The drift is v1-only history; no stored record pins either
61
+ choice (FPDAM: zero stored records).
62
+ - DAM-family is overwhelmingly joint vocabulary on the PDAM side (98% of
63
+ PDAM records are ISO/IEC); pure-ISO DAM/FDAM records are plentiful.
64
+ *(joint-row cross-check for the IEC study.)*
65
+ - `update_codes.yaml` already normalizes the long-form spellings
66
+ (`DIS Amd` → `DAM`, `FDIS Amd` → `FDAM`), i.e. the DAM face IS the
67
+ short form of the DIS/FDIS-stage amendment.
68
+
69
+ ## 3. Historic → current face mapping (ISO)
70
+
71
+ | # | Historic face | Example | Today's decode | Status today |
72
+ |---|---|---|---|---|
73
+ | 1 | Old part separator `/` | `ISO 9075/1`, `ISO/R 657/IV` | part `9075-1`; roman → `657-4:1969` (update_codes) | **accepted, normalized on render**; 0 stored occurrences |
74
+ | 2 | Old year separator `-` | `ISO 1234-1988` | `ISO 1234:1988` | accepted, normalized; 0 stored occurrences |
75
+ | 3 | Undated markers | `ISO 1234:--`, `ISO 1234:—` | `undated` flag | accepted |
76
+ | 4 | TYPE-first ballot face | `ISO/IEC FDAM 1 14496-12:2004`, `ISO/PDAM 19973-2` | — | **rejected** (see §5); 0 stored occurrences in 80,202 records / 560 commits |
77
+ | 5 | Space supplement face | `ISO 15877-5:2009 FDAM 1` | base 15877-5:2009 + FDAM 1 | **accepted**, renders `ISO 15877-5:2009/FDAM 1` |
78
+ | 6 | On-base slash numbered | `ISO 15877-5:2009/Amd 1:2010` | canonical amendment | accepted (canonical) |
79
+ | 7 | Glued number | `ISO 19776-2:2005/Amd1:2006`, `/AMD1` | `/Amd 1:2006` | accepted, rendered spaced |
80
+ | 8 | Dot spelling (old style) | `ISO 10993-4:2002/Amd.1:2006(E)`, `Cor.1` | `/Amd 1:2006(E)` (dot stored as `TypedStage#original_abbr` facet, not serialized; render emits `abbr.first` — for `Amd.` the facet selects short-vs-long only) | accepted |
81
+ | 9 | Uppercase spelling | `/AMD 1:1998`, `/COR 1:1995` | canonical `Amd`/`Cor` | accepted (30 `/AMD n` faces stored) |
82
+ | 10 | Legacy draft spellings | `/DAmd 1`, `/FDAmd 1`, `/pDCOR.2`, `/DAD 1`, `/DSuppl 1.2` | `DAM 1`, `FDAM 1`, `CD Cor 2`, DAD (Addendum 40.xx), DSuppl (Supplement 40.xx) | accepted |
83
+ | 11 | Long-form draft spellings | `/DIS Amd 1`, `/FDIS Amd 1` | `DAM 1`, `FDAM 1` (update_codes pre-parse) | accepted |
84
+ | 12 | `Am` spelling | `/Am1:2006` | — | **rejected** (`Am` absent from the 64-entry supplement table); 0 stored occurrences |
85
+ | 13 | ISO/R | `ISO/R 185:1961/Amd 1:1969` | ISO/R prefix + modern supplement | accepted (1,734 records) |
86
+ | 14 | ISO/R dash supplement | `ISO/R NNN — STAGE N` | grammar branch `iso_r_supplement_identifier` (separators `/`, ` — `, ` - `) | accepted by rule; **no fixture and no stored record exercises it** |
87
+
88
+ Where the legacy spellings survive in the corpus: only in **secondary**
89
+ positions — `relation → bibitem → formattedref` (DAmd: 600 files, FDAmd:
90
+ 95 files) and abstract prose (`/Amd.1`: 14 files, e.g.
91
+ `ISO/IEC 21000-5:2004/Amd.1:2007`). Primary docidentifiers carry 0
92
+ DAmd/FDAmd/FPDAM/`Amd.` faces — the crawler normalized them.
93
+
94
+ ## 4. The no-number rule (DAM-family with no number = Amendment 1)
95
+
96
+ Owner's rule under verification. Local evidence:
97
+
98
+ - **Direct stored evidence: none.** Across 80,202 records there is not a
99
+ single numberless DAM-family face (`/(P|F)?DAM` not followed by a
100
+ number: 0 in primary docidentifiers; 0 in relations; every one of the
101
+ 5,843 DAM-family docidentifier contents carries a number). Same for the
102
+ fixture corpus: 0 numberless supplement faces in `full/`.
103
+ - **The rule is consistent with everything stored**: the owner-cited
104
+ numberless ballots name documents whose FIRST amendment was published —
105
+ `ISO/IEC PDAM 12207` → `ISO/IEC 12207:1995/Amd 1:2002` (stored,
106
+ URN `amd:1`); `ISO/PDAM 19973-2` names a ballot of the undated
107
+ `19973-2` base (the corpus has no published amendment of the 2007
108
+ edition; the amendment path continues on the 2015 edition,
109
+ `iso-19973-2-2015-amd-1-2019.yaml`).
110
+ - **Both grammars make the number optional** (parg
111
+ `supplement_identifier_no_third`: the number branch is one of three
112
+ `.maybe` tails; v1: `digits.as(:number).maybe`), and today
113
+ `ISO 19973-2/PDAM` parses with `number` **nil** and renders
114
+ `ISO 19973-2/PDAM` — no implied 1 anywhere.
115
+ - **Modeling decision if the rule is adopted**: an implied supplement
116
+ number is identity-bearing (two amendments differ by number), so it
117
+ belongs in the model as **number = "1" at build time** (a builder
118
+ default when a DAM-family token carries no number), NOT as a
119
+ render-time fallback — `to_hash` must agree between parse and
120
+ `from_hash`, and relaton matching treats number as identity.
121
+
122
+ ## 5. The owner-cited anchor forms — decode table
123
+
124
+ "Decode today?" = parses with `Pubid::Iso.parse` on `main` @ `be6a43c2`
125
+ (2.0.21). Joint rows (`ISO/IEC …`) are cross-checked against the IEC
126
+ study (`data/iec/legacy-notation.md` §4); pure-ISO rows are owned here.
127
+
128
+ | # | Input (historic face) | Joint? | Decode today | Historic identity | Stored counterpart (relaton-data-iso, file → primary face) |
129
+ |---|---|---|---|---|---|
130
+ | 1 | `ISO/IEC FDAM 1 14496-12:2004` | joint | **no** | FDAM **1** of base `14496-12:2004` | `iso-iec-14496-12-2004-fdam-1.yaml` → `ISO/IEC 14496-12:2004/FDAM 1` (URN `stage-50.00:amd:1`); published as `ISO/IEC 14496-12:2005/Amd 1:2007` |
131
+ | 2 | `ISO/IEC FDAM 19776-2:2005/Am1:2006` | joint | **no** | FDAM (unnumbered → **1**) of base `19776-2:2005`; `/Am1:2006` = the on-base stamp in the `Am` spelling (amendment 1, year 2006) | `iso-iec-19776-2-2005-amd-1-2008.yaml` → `ISO/IEC 19776-2:2005/Amd 1:2008` |
132
+ | 3 | `ISO/IEC FPDAM 10373-6/Amd 9:2001` | joint | **no** | FPDAM (unnumbered) of base `10373-6`; on-base `Amd 9:2001` carries the number → **9** | `iso-iec-10373-6-2001-dam-9.yaml` → `ISO/IEC 10373-6:2001/DAM 9` (URN `stage-40.00:amd:9`) — the same ballot stored as DAM, not FPDAM (website face vs stored face drift) |
133
+ | 4 | `ISO/IEC FPDAM 9075/1` | joint | **no** | FPDAM (unnumbered → **1**) of base `9075/1` — old slash-part → `9075-1` | `iso-iec-9075-1-1999-amd-1-2001.yaml` → `ISO/IEC 9075-1:1999/Amd 1:2001` |
134
+ | 5 | `ISO/IEC 19772:2009/DAM 1:2019(E)` | joint | **yes** (both flavors) | DAM **1** of base `19772:2009` | `iso-iec-19772-2009-dam-1.yaml` → identical face (URN `stage-40.00:amd:1`); the amendment died in ballot (2020 edition published instead) |
135
+ | 6 | `ISO/FDAM 19119:2005/Amd.1` | pure ISO | **no** | FDAM of base `19119:2005`; on-base dot spelling → amendment **1** | `iso-19119-2005-amd-1-2008.yaml` → `ISO 19119:2005/Amd 1:2008` |
136
+ | 7 | `ISO/PDAM 19110:2005/Amd.1` | pure ISO | **no** | PDAM of base `19110:2005`; on-base → amendment **1** | `iso-19110-2005-amd-1-2011.yaml` → `ISO 19110:2005/Amd 1:2011` |
137
+ | 8 | `ISO 15877-5:2009 FDAM 1` | pure ISO | **yes** | FDAM **1** of base `15877-5:2009`, space attachment | renders `ISO 15877-5:2009/FDAM 1`; published as `ISO 15877-5:2009/Amd 1:2010` (`iso-15877-5-2009-amd-1-2010.yaml`) |
138
+ | 9 | `ISO/PDAM 19973-2` | pure ISO | **no** | PDAM, **no number anywhere → Amendment 1** (owner rule), base `19973-2` undated | no amendment record of the 2007 edition; path continues `iso-19973-2-2015-amd-1-2019.yaml` |
139
+ | 10 | `ISO/IEC PDAM TR 9573-11/Amd.1` | joint | **no** | PDAM of base `TR 9573-11`; on-base → amendment **1** | `iso-iec-tr-9573-11-2004-pdam-1.yaml` → `ISO/IEC TR 9573-11:2004/PDAM 1` (URN `stage-30.00:amd:1`) |
140
+ | 11 | `ISO/IEC PDAM 9541-1/Amd.4` | joint | **no** | PDAM of base `9541-1`; on-base → amendment **4** | `iso-iec-9541-1-1991-amd-4-2009.yaml` → `ISO/IEC 9541-1:1991/Amd 4:2009` |
141
+ | 12 | `ISO/IEC PDAM 2 10646:2012` | joint | **no** | PDAM **2** (number on the TYPE token) of base `10646:2012` | `iso-iec-10646-2012-amd-1-2013.yaml` → `ISO/IEC 10646:2012/Amd 1:2013`; the same ballot's FDAM round is stored: `iso-iec-10646-2012-fdam-2.yaml` → `ISO/IEC 10646:2012/FDAM 2` |
142
+ | 13 | `ISO/IEC PDAM 12207` | joint | **no** | PDAM, **no number → Amendment 1**, base `12207` undated | `iso-iec-12207-1995-amd-1-2002.yaml` → `ISO/IEC 12207:1995/Amd 1:2002` |
143
+
144
+ Score: **11 of 13 rejected today** (2, 3, 4 because of BOTH the TYPE-first
145
+ face and the on-base stamps; 10, 11 because of the TYPE-first face — their
146
+ on-base stamps alone would parse); the two that parse are the
147
+ modern-shape rows (5 on-base DAM, 8 space FDAM).
148
+
149
+ The TYPE-first faces come in three sub-shapes the decode must cover:
150
+
151
+ - (a) number on the TYPE token: `FDAM 1 14496-12:2004`, `PDAM 2 10646:2012`
152
+ - (b) number on the base stamp: `FDAM 19776-2:2005/Am1:2006`,
153
+ `FPDAM 10373-6/Amd 9:2001`, `PDAM TR 9573-11/Amd.1`,
154
+ `PDAM 9541-1/Amd.4`, `FDAM 19119:2005/Amd.1`, `PDAM 19110:2005/Amd.1`
155
+ - (c) no number at all: `PDAM 12207`, `PDAM 19973-2` (→ Amendment 1)
156
+
157
+ In (b) the number rides on-base and the stage token is bare — if the
158
+ builder takes the number from the on-base stamp, the DAM-family token
159
+ must not double-consume it (the current IEC-flavor behavior for
160
+ `IEC/FDAM 60038-1`, where the base's part migrates into the supplement
161
+ slot, is the analogous hazard documented in the IEC study §3).
162
+
163
+ ## 6. Which of today's branches serve historic faces, and what today rejects
164
+
165
+ **Served (parse paths):** DAM-family exists ONLY in the
166
+ supplement-position table `typed_stages_supplements` (64 entries — PDAM,
167
+ DAM, DAmd, FDAM, FDAmd, FPDAM, Amd/AMD/Amd., Cor/COR/Cor., DAD family,
168
+ DSuppl family, pDCOR, Ext). The TYPE-position table `typed_stages`
169
+ (115 entries) has **no DAM-family token** — `ISO/FDAM 19119:2005` raises
170
+ while `ISO/FDIS 19119:2005` parses. The supplement layer accepts three
171
+ number carriers: `[space] supplement_second_part` (numbered, incl. the
172
+ space face), `dot (1*digit)` (the `/Amd.1` dot face), and a date tail.
173
+ Legacy separators (`part_legacy_body` slash-part, dash-year) and ISO/R
174
+ are first-class branches.
175
+
176
+ **Rejection counts for historic faces (measured):**
177
+
178
+ - relaton-data-iso bulk probe: **200,188 of 200,189** distinct stored
179
+ docidentifier faces parse with today's ISO flavor; the single failure
180
+ is `ISO/NP TS 6041-_` (a corrupted trailing underscore, a data
181
+ artifact, not a historic face). Today's grammar serves everything the
182
+ crawler ever stored.
183
+ - The testsuite corpus (7,650 inputs, classification 2026-10-22): 3
184
+ fails, none historic-DAM (`EN 12643:2014`, `ISO/IEC Directives, JTC 1
185
+ Supplement:2021`, `CEN/TS 15370-1:2006` — misfiled and comma-form
186
+ rows).
187
+ - **What history shipped that today rejects is exactly the ballot-page
188
+ set**: the nine TYPE-first forms (zero stored occurrences — the
189
+ rejection blast radius is human-typed input and iso.org page faces,
190
+ not stored records) and the `Am1` spelling (0 stored occurrences).
191
+
192
+ ## 7. Gap list (current grammar/model vs the historic scheme)
193
+
194
+ 1. **TYPE-first DAM-family faces raise**: `[P|F]?DAM [N] BASE(/stamp?)` —
195
+ no rule in `grammars`/parg for either sub-shape (a) or (b), and no
196
+ implied-1 handling for (c). Both flavors raise (iso probed here, iec
197
+ probed in the IEC study).
198
+ 2. **`Am` spelling** (`/Am1:2006`) is not in the supplement table;
199
+ rejected.
200
+ 3. **No implied supplement number anywhere**: `ISO 19973-2/PDAM` stores
201
+ `number = nil` and renders `ISO 19973-2/PDAM`; under the owner's rule
202
+ it should decode as Amendment 1.
203
+ 4. **Slash-part and dash-year** are served as normalizing branches —
204
+ nothing missing, recorded here so the mapping is committed.
205
+ 5. **v1→v2 FPDAM stage drift** (40.xx vs 50.xx): zero stored records to
206
+ arbitrate; the v2 ladder (50.00–50.99) matches the letters' meaning
207
+ (Final PDAM) and the URN spec's 50.00 = FDAM row.
208
+
209
+ ## 8. Disposition options and recommendation
210
+
211
+ **Option A — ISO `original_abbr` spelling-facet style**: parse historic
212
+ faces, store modern identity, keep input spelling as an
213
+ `equality_ignored` facet so `to_s` can reproduce it. Fits when both
214
+ spellings live in the wild and round-tripping matters. The house facet
215
+ plan (#674/#685) already does exactly this for `Amd.` vs `Amd`.
216
+
217
+ **Option B — IHO legacy-decode-to-modern style**: decode straight to the
218
+ modern canonical identity, no spelling kept; render emits the modern
219
+ face. Fits ballot/era artefacts the modern catalogue never stores.
220
+
221
+ **Option C — drop**: keep raising on TYPE-first faces.
222
+
223
+ **Recommendation: B for the TYPE-first ballot faces and `Am1`; the
224
+ existing facet machinery (A) stays for what it already covers.** The
225
+ TYPE-first faces and `Am1` have **zero** stored corpus occurrences
226
+ (measured above) — they are ballot-page/human-typed artefacts, so a
227
+ spelling facet buys nothing for matching, and decode-to-modern gives one
228
+ identity per document (`ISO/IEC FDAM 1 14496-12:2004` and
229
+ `ISO/IEC 14496-12:2004/FDAM 1` must be the SAME identifier; the corpus
230
+ file for that document already settles the target face). The decode
231
+ rule: `[P|F]?DAM [N] BASE [stamp]` → amendment with stage
232
+ (P|F)DAM-family, number taken from the TYPE token or the on-base stamp,
233
+ defaulting to **1** when neither carries one (owner's rule), base kept
234
+ whole. Where the on-base stamp carries the number, the builder must not
235
+ let the base's part migrate into the supplement slot (IEC study §3
236
+ hazard). Dot-spelling `/Amd.1` and `DAmd`/`FDAmd` stay exactly as they
237
+ are — already parsed, already faceted (#685), already rendering
238
+ canonical.
239
+
240
+ **Owner calls needed before any implementation:**
241
+ 1. The no-number rule → implied number 1 at build time (§4 modeling).
242
+ 2. Whether the already-accepted space face (`ISO 15877-5:2009 FDAM 1`)
243
+ should keep rendering with the space (current) or normalize to the
244
+ slash face; and whether `ISO/FDIS 9075/1`-style slash-part renders
245
+ modern (`9075-1`, current) — both are normalization choices today,
246
+ consistent with Option B's spirit.
247
+ 3. FPDAM stage: keep the v2 ladder (50.00–50.99) or restore v1's
248
+ fold-into-DAM (40.xx); the corpus cannot arbitrate (0 records).
249
+
250
+ ## 9. Implementation (owner ruling 2026-11-12: Option B decode-to-modern)
251
+
252
+ The disposition: the TYPE-first ballot faces and the `Am` spelling decode
253
+ straight to the modern canonical identity — no spelling facet, one
254
+ identity per document; the existing facet machinery (Option A) stays for
255
+ what it already covers (`Amd.`/`AMD`). Option C (drop) is not taken.
256
+
257
+ ### 9.1 Corrections to the study's findings (evidence found while implementing)
258
+
259
+ 1. **The space-attached face does NOT parse today.** §3 row 5 / §5 row 8
260
+ recorded `ISO 15877-5:2009 FDAM 1` as "accepted today"; it RAISES on
261
+ `main` @ `c86928fc` (the grammar has no space-supplement branch for the
262
+ DAM family). The anchor score is therefore **12 of 13 rejected today**,
263
+ not 11 — only §5 row 5 (`ISO/IEC 19772:2009/DAM 1:2019(E)`, the on-base
264
+ DAM face) parses. The space face is now served by the new
265
+ base-first ballot branch.
266
+ 2. **The 9075/1 ballot HAS a stored ballot record.** §5 row 4 listed only
267
+ the published counterpart (`iso-iec-9075-1-1999-amd-1-2001.yaml` →
268
+ `ISO/IEC 9075-1:1999/Amd 1:2001`). The corpus also holds
269
+ `iso-iec-9075-1-1999-pdam-1.yaml` → `ISO/IEC 9075-1:1999/PDAM 1`
270
+ (stage 30.00:amd:1) — the same amendment's ballot round, so the record
271
+ of record for the FPDAM 9075/1 ballot face is PDAM 1: the website face's
272
+ letters and the stored round disagree, the same drift §5 row 3 names.
273
+ 3. **Engine observation (parsanol 1.3.126, PG semantics)**: in the
274
+ supplements-table alt, the dot-bearing token (`Amd.`) wins over its
275
+ bare sibling (`Amd`) for a `/Amd.1` stamp, so the dot-form stamp rides
276
+ the number capture (`ballot_stamp_number`), not the dot-number capture —
277
+ the pinned grammar example records the delivered capture.
278
+
279
+ ### 9.2 What was built
280
+
281
+ - **pubid-grammar** (`grammars/iso.parg` v0.5.0, baked at the CI pin
282
+ parsanol 1.3.126 / ruby 3.4.8; artifact, schema and corpus regenerated,
283
+ iso files only):
284
+ - `ballot_supplement_identifier` — TYPE-first:
285
+ `prefix [slash] ballot-token [ballot_number] [base type_with_stage]
286
+ second_part [ballot_stamp]`. `ballot_number` is space-bounded on both
287
+ sides so `PDAM 19973-2` keeps 19973 in the base (the trailing space is
288
+ mandatory; with no second space the group backtracks). The base is
289
+ kept WHOLE — the legacy slash-part of `FPDAM 9075/1` stays a base part,
290
+ the base's own type token (`PDAM TR 9573-11`) stays the base's; nothing
291
+ migrates into the supplement slot (the §3 hazard of the IEC study).
292
+ The on-base stamp's number/date capture as `ballot_stamp_*` while its
293
+ token letters match uncaptured (decode-away).
294
+ - `ballot_space_supplement_identifier` — base-first space attachment
295
+ (`ISO 15877-5:2009 FDAM 1`). The base front is inlined, not reused from
296
+ `supplement_base_no_third`, whose trailing `base_third_part_edition`
297
+ would consume the separator space.
298
+ - Both branches sit LAST in the identifier alternation — purely additive;
299
+ an input an existing member parses keeps that exact parse.
300
+ - `typed_stages_ballots` carries the ballot token set (PDAM, DAM, DAmd,
301
+ FDAM, FDAmd, FPDAM) so the TYPE-first branch stays scoped to the ballot
302
+ family. `Am` joined `typed_stages_supplements` (after the Amd entries).
303
+ - **pubid** (the builder is the decode):
304
+ - `Builder#build_ballot_supplement` — ONE amendment layer over the whole
305
+ base (the stored twins keep one level; never a second supplement
306
+ layer). Stage from the ballot token; number precedence TYPE token →
307
+ on-base stamp → implied **1**; the stamp's date becomes the ballot
308
+ supplement's date.
309
+ - `Builder#apply_implied_amendment_number` — the owner's no-number rule
310
+ on every builder path (parse, URN, bundle members): a numberless
311
+ DAM-family ballot-stage amendment (`typed_stage.code` ∈
312
+ `{pdam, damd, fdamd, fpdam}` — by `#code`, not `#stage_code`) names
313
+ Amendment 1. Identity-bearing: set at build time, serialized, so
314
+ `parse == from_hash(to_hash)`. The published stage (`Amd`) is out of
315
+ scope — a numberless `ISO 1234/Amd` keeps its nil number.
316
+ - `Am` in the published stage's abbr list; `detect_stage_format_long`
317
+ treats an original spelling that is a prefix of the long abbr as the
318
+ long family, so `Am` renders canonical `Amd` while `AMD` keeps its
319
+ short facet and `Amd.` keeps rendering `Amd` (unchanged).
320
+ - **pubid fixtures/specs**: `spec/fixtures/iso/identifiers/full/
321
+ iso-pubid-ballot-faces.txt` (the 13 anchors + `Am` alias + numberless
322
+ rows), `spec/pubid/iso/ballot_face_decode_spec.rb` (60 examples).
323
+
324
+ ### 9.3 Gates
325
+
326
+ | Gate | Result |
327
+ |---|---|
328
+ | Fixture replay (baseline `c86928fc` vs branch, 15,307 inputs) | **0 diff lines** — `to_s`/`to_urn`/`to_hash`/`to_mr_string` byte-identical for every already-parsing input; pass/raise counts unchanged (15,303 / 4, none ballot-related) |
329
+ | Stored-corpus probe (213,414 distinct docidentifier faces greppped from the local relaton-data-iso clone @ dd368d8de2, superset of the §6 count) | **0 diff lines** baseline vs branch; 199,471 parse / 13,943 raise on both (the raise set is this probe's cruder extraction — abstract prose, secondary `formattedref` faces — identical both sides, so 0 changed identities) |
330
+ | Ballot faces | 12 raise→parse (all §5 rows but row 5); `Am1` accepted; the 3 pre-existing fail fixtures stay fail |
331
+ | Specs | `spec/pubid/iso/` 3,188 examples green, incl. the 60 new |
332
+
333
+ ### 9.4 Per-anchor decode → stored-twin verification
334
+
335
+ Decode rule applied: stage from the ballot face's letters; number from the
336
+ TYPE token, else the on-base stamp, else implied 1; base kept whole. Where
337
+ the ballot face's letters and the stored record disagree, the disagreement
338
+ is recorded (drift), the stored record stays the identity of record.
339
+
340
+ | # | Input | Decode (render) | Stored twin (relaton-data-iso) | Verdict |
341
+ |---|---|---|---|---|
342
+ | 1 | `ISO/IEC FDAM 1 14496-12:2004` | `ISO/IEC 14496-12:2004/FDAM 1` (stage 50.00:amd:1) | `iso-iec-14496-12-2004-fdam-1.yaml` → `ISO/IEC 14496-12:2004/FDAM 1` | **EXACT** — face, stage, number, base+year all match |
343
+ | 2 | `ISO/IEC FDAM 19776-2:2005/Am1:2006` | `ISO/IEC 19776-2:2005/FDAM 1:2006` | `iso-iec-19776-2-2005-amd-1-2008.yaml` → `ISO/IEC 19776-2:2005/Amd 1:2008` | number 1 ✓, base ✓, ONE level both ✓; drift: ballot FDAM vs published Amd, ballot date 2006 vs published 2008 |
344
+ | 3 | `ISO/IEC FPDAM 10373-6/Amd 9:2001` | `ISO/IEC 10373-6/FPDAM 9:2001` | `iso-iec-10373-6-2001-dam-9.yaml` → `ISO/IEC 10373-6:2001/DAM 9` (stage 40.00:amd:9) | number 9 ✓, base+year ✓; drift: website FPDAM vs stored DAM (§5's named drift) |
345
+ | 4 | `ISO/IEC FPDAM 9075/1` | `ISO/IEC 9075-1/FPDAM 1` | ballot `iso-iec-9075-1-1999-pdam-1.yaml` → `ISO/IEC 9075-1:1999/PDAM 1`; published `…/amd-1-2001.yaml` → `ISO/IEC 9075-1:1999/Amd 1:2001` | part-in-base ✓ (9075-1), number 1 ✓; drift: website FPDAM vs stored PDAM ballot round (new evidence, §9.1.2) |
346
+ | 5 | `ISO/IEC 19772:2009/DAM 1:2019(E)` | unchanged (parses today) | `iso-iec-19772-2009-dam-1.yaml` → `ISO/IEC 19772:2009/DAM 1` | face matches; stored carries no supplement date/language |
347
+ | 6 | `ISO/FDAM 19119:2005/Amd.1` | `ISO 19119:2005/FDAM 1` | `iso-19119-2005-amd-1-2008.yaml` → `ISO 19119:2005/Amd 1:2008` | number 1 ✓, base+year ✓; drift: ballot FDAM vs published Amd, no ballot record stored |
348
+ | 7 | `ISO/PDAM 19110:2005/Amd.1` | `ISO 19110:2005/PDAM 1` | `iso-19110-2005-amd-1-2011.yaml` → `ISO 19110:2005/Amd 1:2011` | number 1 ✓, base+year ✓; drift: ballot PDAM vs published Amd |
349
+ | 8 | `ISO 15877-5:2009 FDAM 1` | `ISO 15877-5:2009/FDAM 1` | `iso-15877-5-2009-amd-1-2010.yaml` → `ISO 15877-5:2009/Amd 1:2010` (no ballot record stored) | number 1 ✓, base+year ✓; drift: ballot FDAM vs published Amd |
350
+ | 9 | `ISO/PDAM 19973-2` | `ISO 19973-2/PDAM 1` | **no twin** — no amendment record of the 2007 edition (path continues `iso-19973-2-2015-amd-1-2019.yaml`) | implied 1 per the owner rule (§4); undated base matches by partial reference |
351
+ | 10 | `ISO/IEC PDAM TR 9573-11/Amd.1` | `ISO/IEC TR 9573-11/PDAM 1` (stage 30.00:amd:1) | `iso-iec-tr-9573-11-2004-pdam-1.yaml` → `ISO/IEC TR 9573-11:2004/PDAM 1` | **stage + number + TR base all match**; base undated vs stored 2004 (partial-ref match) |
352
+ | 11 | `ISO/IEC PDAM 9541-1/Amd.4` | `ISO/IEC 9541-1/PDAM 4` | `iso-iec-9541-1-1991-amd-4-2009.yaml` → `ISO/IEC 9541-1:1991/Amd 4:2009` | number 4 ✓, base ✓; drift: ballot PDAM vs published Amd |
353
+ | 12 | `ISO/IEC PDAM 2 10646:2012` | `ISO/IEC 10646:2012/PDAM 2` | FDAM round stored: `iso-iec-10646-2012-fdam-2.yaml` → `ISO/IEC 10646:2012/FDAM 2` (also published `…/amd-1-2013.yaml` → `Amd 1:2013`) | number 2 ✓, base+year ✓; drift: ballot PDAM round vs stored FDAM round — the owner's §1 example pins the face letters (`/PDAM 2`) |
354
+ | 13 | `ISO/IEC PDAM 12207` | `ISO/IEC 12207/PDAM 1` | `iso-iec-12207-1995-amd-1-2002.yaml` → `ISO/IEC 12207:1995/Amd 1:2002` | number 1 ✓; drift: ballot PDAM vs published Amd; undated base matches by partial reference |
355
+
356
+ Exact stored-face agreement: rows 1 and 10 (plus row 5's already-parsing
357
+ face). Every other anchor agrees on everything the ballot face carries
358
+ (number, base structure, dated bases) and records the round-letters drift
359
+ against the stored record of record.
360
+
361
+ ### 9.5 Released state
362
+
363
+ One PR per repo: pubid-grammar (grammar + artifact + contracts),
364
+ pubid (vendored artifact + builder decode + fixtures/specs + this doc),
365
+ pubid-testsuite (corpus round). No version bumps (the parent cuts 2.0.22 /
366
+ 0.1.38 after the FIPS + IEC + ISO waves merge). The FPDAM stage ladder
367
+ stays at 50.00–50.99 (§8 call 3: the v2 ladder stands; zero stored records
368
+ arbitrate it).