pubid 2.0.0.pre.alpha.47 → 2.0.0.pre.alpha.49

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cdd72b807af6dfe0a054a41ea6936289d3a7baa1d9f52dd6ca526a2092fe74ed
4
- data.tar.gz: 766936316a70dde70df50e5090555a8004663ed33cc21e2058e8a34ac9b56917
3
+ metadata.gz: 497049ade2827d12f1d6a7774b14c033e0915d21b657a78b5290056f84a3a9d2
4
+ data.tar.gz: 6f09327d056fa64a8e7656ba277a09258bc60335a4e92b28aeef1fa2833f9e2f
5
5
  SHA512:
6
- metadata.gz: 001d0cec827dee1c30411aed304e52e14eda70989fa180575f9dff377e34022dd3d37caf2b6bf4f20415102c38e4aeb1b52531351c741544f80a587e413cfbc2
7
- data.tar.gz: 100d3d5314550b4fbb535440e7002e070e205b28f5faf3f4cd66dae6cd054790d41c202ab239c3f961d2ac0db253eefcc9fc5737aa69cc666cb340fdd8a5c213
6
+ metadata.gz: 136a58459836eaf728383f00e34214c3015e7abfcf7ffac60fec64900fe1c44d029af4f4309c41d7f404d30b4865f443136cb6cb5dc56152ca3ef720fdbc9c45
7
+ data.tar.gz: 638e743baf216904da4fbf08b764f33ed6bd0784f58b17865f5cafb501ebe2ca9f2b86acc35dc9651415055dcc376682b98bd8ce4e53a0db67bcbf3bec81dd8c
@@ -0,0 +1,157 @@
1
+ # IEC stage codes — extracted from IEC's own published API documents
2
+
3
+ This directory's stage-code reference. The authoritative source is IEC's
4
+ own documentation of its project-stage vocabulary, committed here as an
5
+ extract because the primary document cannot be fetched programmatically.
6
+
7
+ ## Provenance
8
+
9
+ - **Primary (unreachable):** IEC Data Feeds,
10
+ <https://www.iec.ch/system/files?file=2020-08/IEC_DATA_FEEDS.pdf>
11
+ (19 pages, August 2020). `www.iec.ch` serves an AWS WAF JS challenge to
12
+ non-browser clients and the Wayback Machine holds no snapshot, so the
13
+ bytes could not be retrieved (attempts of 2026-10-21).
14
+ - **Used instead — IEC's own successors of Data Feeds**, both fetched
15
+ 2026-10-21:
16
+ - *IEC/ISO Harmonized APIs*, v0.0.8, 60 pages, June 2023,
17
+ <https://api-portal.iec.ch/files/APIsLegacyCorrespondence_0_0_8.pdf>
18
+ — section 3 "IEC/ISO Stage Code correspondance" (p. 37–38).
19
+ - *IEC/ISO Harmonized APIs*, v0.0.7, 32 pages, May 2021,
20
+ <https://iec-apiportal.apigee.io/files/APIsLegacyCorrespondence.pdf>
21
+ — same table (p. 31–32); its change log (2021-01-18) introduces the
22
+ "CSV Feeder and IEC/ISO Stage Code correspondence".
23
+ - The 2021 document lists "IEC Data Feeds (<https://www.iec.ch/datafeeds>)"
24
+ among the *legacy feeders* these Harmonized APIs replace, and maps the
25
+ legacy feed's fields (STAGE_CODE, STAGE_CODE_NAME, ISO_LEVEL) onto the
26
+ modern stageCode/harmonizedStageCode pairs.
27
+ - **Also held: *Stage Codes — International Standard (IS)*, v2.0, 2023-06**
28
+ (`StageCodes_IS_v2.pdf`, cited by metanorma/pubid-iec#127). The bytes
29
+ are withheld from retrieval by the same WAF; a human-downloaded copy
30
+ is preserved outside the repository. It is the IS-lifecycle flow
31
+ diagram; the abbreviations it defines are transcribed below.
32
+ - **Extraction:** `pdftotext -layout`; table transcribed verbatim below,
33
+ including IEC's own spelling of the section title ("correspondance").
34
+
35
+ ## IEC/ISO Stage Code correspondance (section 3, v0.0.8 p.37–38; identical in v0.0.7)
36
+
37
+ | Harmonized Stage | IEC Stage Code | Meaning | Deliverable |
38
+ |---|---|---|---|
39
+ | 20.99 | ACD | Approved for CD | IS, TR, TS |
40
+ | 30.99 | ACDV | Approved for CDV | IS |
41
+ | 40.99 | ADTR | Approved for DTR | TR |
42
+ | 40.99 | ADTS | Approved for DTS | TS |
43
+ | 40.99 | AFDIS | Approved for FDIS | IS |
44
+ | 50.99 | APUB | Approved for publication | IS, TR, TS, PAS, ISH, COR |
45
+ | 60.00 | BPUB | Being published | IS, TR, TS, PAS, ISH, COR |
46
+ | 30.98 | CAN | Draft Cancelled | IS, TR, TS |
47
+ | 50.98 | CAN | Draft Cancelled | PAS, ISH, COR |
48
+ | 40.20 | CCDV | Draft circulated as CDV | IS |
49
+ | 30.20 | CD | Draft circulated as CD | IS, TR, TS |
50
+ | 50.20 | CDISH | Draft circulated as DISH | ISH |
51
+ | 30.60 | CDM | CD to be discussed at meeting | IS, TR, TS |
52
+ | 50.20 | CDPAS | Draft circulated as DPAS | PAS |
53
+ | 50.20 | CDTR | Draft circulated as DTR | TR |
54
+ | 50.20 | CDTS | Draft circulated as DTS | TS |
55
+ | 40.91 | CDVM | Rejected CDV to be discussed at meeting | IS |
56
+ | 50.20 | CFDIS | Draft circulated as FDIS | IS |
57
+ | 50.00 | DECDISH | DISH at editing check | ISH |
58
+ | 50.00 | DECFDIS | FDIS at editing check | IS |
59
+ | 60.00 | DECPUB | Publication at editing check | IS, TR, TS, PAS, COR |
60
+ | 10.98 | DEL | Deleted/abandoned | IS, TR, TS |
61
+ | 20.98 | DEL | Deleted/abandoned | ISH |
62
+ | 99.60 | DELPUB | Deleted publication | IS, TR, TS, PAS, ISH, COR |
63
+ | 50.92 | DTRM | Rejected DTR to be discussed at meeting | TR |
64
+ | 50.92 | DTSM | Rejected DTS to be discussed at meeting | TS |
65
+ | 30.97 | MERGED | Fragment merged | IS, TR, TS, PAS, COR |
66
+ | 40.91 | NCDV | CDV rejected | IS |
67
+ | 50.92 | NDTR | DTR rejected | TR |
68
+ | 50.92 | NDTS | DTS rejected | TS |
69
+ | 50.92 | NFDIS | FDIS rejected | IS |
70
+ | 30.60 | PCC | Preparation of CC | IS, TR, TS |
71
+ | 10.20 | PNW | New work item proposal | IS, TS |
72
+ | 60.60 | PPUB | Publication issued | IS, TR, TS, PAS, ISH, COR |
73
+ | 40.60 | PRVC | Preparation of RVC | IS |
74
+ | 50.60 | PRVD | Preparation of RVD | IS |
75
+ | 50.60 | PRVDISH | Preparation of RVDISH | ISH |
76
+ | 50.60 | PRVDPAS | Preparation of RVDPAS | PAS |
77
+ | 50.60 | PRVDTR | Preparation of RVDTR | TR |
78
+ | 50.60 | PRVDTS | Preparation of RVDTS | TS |
79
+ | 10.60 | PRVN | Preparation of RVN | IS, TS |
80
+ | 00.00 | PWI | Preliminary work item | IS, TR, TS |
81
+ | 50.00 | RDISH | DISH received and registered | ISH |
82
+ | 50.00 | RFDIS | FDIS received and registered | IS |
83
+ | 60.00 | RPUB | Publication received and registered | IS, TR, TS, PAS, COR |
84
+ | 30.96 | SPLIT | Project Fragmented | IS, TR, TS, PAS |
85
+ | 40.00 | TCDV | Translation of CDV | IS |
86
+ | 50.00 | TDISH | Translation of DISH | ISH |
87
+ | 50.00 | TDTR | Translation of DTR | TR |
88
+ | 50.00 | TDTS | Translation of DTS | TS |
89
+ | 60.00 | TPUB | Translation of publication | IS, TR, TS, COR |
90
+ | 95.99 | WPUB | Publication withdrawn | IS, TR, TS, PAS, ISH, COR |
91
+
92
+ **Absent from every source in this file** (the correspondence table,
93
+ the IS ladder, and the legacy examples — all full-text searched):
94
+ `AWG`, `CANV`, and — as bare codes — `AWI`, `DIS`, `PUB`. Neither AWG
95
+ nor CANV is part of the issue's list either (pubid#237's STAGE_CODES
96
+ has 55 entries and contains neither); earlier analysis that attributed
97
+ them to the issue was wrong. The modern API spells the enquiry/FDIS
98
+ ladder with the C-prefixed circulation codes (CCDV/CFDIS/RFDIS/NFDIS…)
99
+ rather than DIS/FDIS.
100
+
101
+ ## The IS-lifecycle ladder (StageCodes_IS_v2.pdf, v2.0 2023-06)
102
+
103
+ The IS diagram chains these abbreviations; the intermediate registered
104
+ states do not all appear in the correspondence table above:
105
+
106
+ PRELIMINARY: PWI → PROPOSAL: prePNW ("Preparation of NP"), PNW/NP,
107
+ DEL, PRVN, RVN, RR → COMMITTEE: ACD, CD, PCC, CC, CDM →
108
+ ENQUIRY: ACDV, TCDV, CCDV, CDV, PRVC, RVC → APPROVAL: CFDIS, FDIS,
109
+ DECFDIS, RFDIS, CAN → PUBLICATION: BPUB, PPUB, APUB, RPUB, TPUB →
110
+ WPUB, DECPUB, DELPUB.
111
+
112
+ Notable: the ladder runs through **NP** (not bare "DIS") for the
113
+ proposal, and the enquiry/approval drafts are the C-prefixed
114
+ circulation codes plus **RVC/RFDIS** — bare "DIS" and "FDIS" appear
115
+ only as the delivered drafts, and **ADIS appears nowhere** (it is a
116
+ legacy-Data-Feeds-only code; the diagram uses RFDIS at 50.00).
117
+ "prePNW", "CC" and "RVN/RVC/RVD" are diagram codes with no
118
+ correspondence-table row.
119
+
120
+ ## Legacy IEC Data Feeds stage codes (use-case examples, v0.0.8 p.24–25; v0.0.7 p.18–19)
121
+
122
+ The documents' legacy-feeds example is the only place a code outside the
123
+ table above appears:
124
+
125
+ | Legacy field | Value | Modern counterpart in the same example |
126
+ |---|---|---|
127
+ | STAGE_CODE | `ADIS` | stageCode `RFDIS` |
128
+ | STAGE_CODE_NAME | "Approved for FDIS circulation" | harmonizedStageCode `50.00` |
129
+ | ISO_LEVEL | `40.99` | |
130
+
131
+ `ADIS` is the legacy Data Feeds spelling of what the modern API calls
132
+ `AFDIS` ("Approved for FDIS", harmonized 40.99): same meaning, same
133
+ ISO_LEVEL. The example pairs it with the modern project's then-CURRENT
134
+ stage RFDIS/50.00 — a snapshot of the project's plan, not a code rename.
135
+
136
+ ## Relationship to the pubid stage registry
137
+
138
+ - `ADIS` is registered as an alias on the IEC `cdv` typed stage, whose
139
+ harmonized list covers 40.99.
140
+ - `AWG` and `CANV` appear in neither this table nor the legacy examples —
141
+ IEC's own Data-Feeds successors do not know them, and neither does
142
+ #237's list; they are fictitious (pubid#572).
143
+ - **Reconciliation (2026-10-22, applied):** `DELPUB`'s code 99.60 is
144
+ claimed by the `is` ladder; `SPLIT`/`MERGED` are claimed at
145
+ 30.96/30.97 by the `cd` ladder; `PCC`, `RPUB`, `TPUB`, `DECPUB`,
146
+ `DEL` (10.98 and 20.98) and `PRVN` are registered as typed-stage
147
+ abbreviations; every per-deliverable row — the translation/preparation
148
+ families, `DECDISH`, `PRVDISH`/`PRVDPAS`/`PRVDTR`/`PRVDTS`, `CDTR`/
149
+ `CDTS`/`CDISH`/`CDPAS`, `ADTR`/`ADTS`, `DTRM`/`DTSM`, `NDTR`/`NDTS`,
150
+ `RDISH`, `TCDV`, `PRVC`, `AFDIS`, … — is queryable through
151
+ `Pubid::Iec.locate_stage` via the classes' PROJECT_STAGES;
152
+ `Pubid::Iec.locate_stage_by_harmonized_code` resolves the codes with
153
+ the IS-family preference (the pubid#570 deterministic rule). The
154
+ review/withdrawal ladder rows (90.xx/95.xx) stay claimed: the
155
+ harmonized system is joint ISO/IEC and #237's own list maps RR to
156
+ 9092 — IEC's table simply does not name IEC stage codes for the
157
+ review sub-states. Locked by `spec/pubid/iec/stage_reconciliation_spec.rb`.
data/data/parg/jcgm.json CHANGED
@@ -1 +1 @@
1
- {"bindings":null,"version":"0.1.0","grammar":"PubidJcgm","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Entity":{"atom":36}},{"Entity":{"atom":3}},{"Str":{"pattern":"JCGM"}},{"Named":{"name":"publisher","atom":2}},{"Entity":{"atom":5}},{"Str":{"pattern":" "}},{"Entity":{"atom":9}},{"Entity":{"atom":8}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":7,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"number","atom":6}},{"Entity":{"atom":16}},{"Str":{"pattern":"st"}},{"Str":{"pattern":"nd"}},{"Str":{"pattern":"rd"}},{"Str":{"pattern":"th"}},{"Alternative":{"atoms":[12,13,14,15]}},{"Entity":{"atom":5}},{"Str":{"pattern":"Meeting"}},{"Named":{"name":"type_with_stage","atom":18}},{"Entity":{"atom":5}},{"Str":{"pattern":"("}},{"Entity":{"atom":31}},{"Entity":{"atom":26}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[24,25]}},{"Entity":{"atom":8}},{"Repetition":{"atom":27,"min":2,"max":null,"tag":"Repetition"}},{"Entity":{"atom":8}},{"Lookahead":{"atom":29,"positive":false}},{"Sequence":{"atoms":[23,28,30]}},{"Named":{"name":"date","atom":22}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[20,21,32,33]}},{"Repetition":{"atom":34,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[1,4,10,11,17,19,35]}},{"Entity":{"atom":101}},{"Entity":{"atom":86}},{"Entity":{"atom":3}},{"Entity":{"atom":5}},{"Entity":{"atom":51}},{"Entity":{"atom":47}},{"Str":{"pattern":"GUM-"}},{"Entity":{"atom":8}},{"Repetition":{"atom":44,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"gum_number","atom":45}},{"Sequence":{"atoms":[43,46]}},{"Entity":{"atom":50}},{"Entity":{"atom":9}},{"Named":{"name":"number","atom":49}},{"Alternative":{"atoms":[42,48]}},{"Entity":{"atom":72}},{"Entity":{"atom":54}},{"Str":{"pattern":":"}},{"Entity":{"atom":67}},{"Entity":{"atom":31}},{"Entity":{"atom":58}},{"Str":{"pattern":"-"}},{"Entity":{"atom":61}},{"Entity":{"atom":8}},{"Repetition":{"atom":60,"min":2,"max":null,"tag":"Repetition"}},{"Entity":{"atom":58}},{"Entity":{"atom":65}},{"Entity":{"atom":8}},{"Repetition":{"atom":64,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[56,57,59,62,63]}},{"Named":{"name":"date","atom":66}},{"Entity":{"atom":70}},{"Entity":{"atom":31}},{"Named":{"name":"date","atom":69}},{"Alternative":{"atoms":[55,68]}},{"Sequence":{"atoms":[53,71]}},{"Repetition":{"atom":52,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":84}},{"Str":{"pattern":"("}},{"Str":{"pattern":"E/F"}},{"Str":{"pattern":"F/E"}},{"Alternative":{"atoms":[76,77]}},{"Entity":{"atom":80}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Alternative":{"atoms":[78,79]}},{"Named":{"name":"languages","atom":81}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[75,82,83]}},{"Repetition":{"atom":74,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[39,40,41,73,85]}},{"Named":{"name":"base","atom":38}},{"Entity":{"atom":89}},{"Str":{"pattern":"/"}},{"Str":{"pattern":"Amd"}},{"Named":{"name":"type_with_stage","atom":90}},{"Entity":{"atom":5}},{"Entity":{"atom":9}},{"Named":{"name":"number","atom":93}},{"Entity":{"atom":54}},{"Entity":{"atom":67}},{"Entity":{"atom":70}},{"Alternative":{"atoms":[96,97]}},{"Sequence":{"atoms":[95,98]}},{"Repetition":{"atom":99,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[87,88,91,92,94,100]}},{"Entity":{"atom":108}},{"Entity":{"atom":86}},{"Named":{"name":"base","atom":103}},{"Entity":{"atom":5}},{"Str":{"pattern":"Corrigendum"}},{"Named":{"name":"type_with_stage","atom":106}},{"Sequence":{"atoms":[104,105,107]}},{"Entity":{"atom":124}},{"Entity":{"atom":86}},{"Named":{"name":"base","atom":110}},{"Entity":{"atom":89}},{"Str":{"pattern":"Cor"}},{"Named":{"name":"type_with_stage","atom":113}},{"Entity":{"atom":5}},{"Entity":{"atom":9}},{"Named":{"name":"number","atom":116}},{"Entity":{"atom":54}},{"Entity":{"atom":67}},{"Entity":{"atom":70}},{"Alternative":{"atoms":[119,120]}},{"Sequence":{"atoms":[118,121]}},{"Repetition":{"atom":122,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[111,112,114,115,117,123]}},{"Entity":{"atom":86}},{"Entity":{"atom":139}},{"Entity":{"atom":3}},{"Entity":{"atom":5}},{"Entity":{"atom":138}},{"Str":{"pattern":"GUM"}},{"Str":{"pattern":"VIM"}},{"Entity":{"atom":58}},{"Entity":{"atom":9}},{"Sequence":{"atoms":[132,133]}},{"Repetition":{"atom":134,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[131,135]}},{"Alternative":{"atoms":[130,136]}},{"Named":{"name":"number","atom":137}},{"Sequence":{"atoms":[127,128,129]}},{"Alternative":{"atoms":[0,37,102,109,125,126]}}],"root":140},"bindings":[]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["rule identifier: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 1 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 1 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 1 and 5 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 1 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 2 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 2 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 2 and 5 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 2 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 3 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 3 and 5 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 3 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 4 and 5 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 4 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 5 and 6 are order-dependent (shared first bytes; ordered choice is decisive)"]},"default_entry":"identifier","render":{},"derive":{},"tests":[{"entry":"identifier","kind":"accept","input":"JCGM 200:2008(F)","expect":{}},{"entry":"identifier","kind":"accept","input":"JCGM 100:2008","expect":{}},{"entry":"identifier","kind":"accept","input":"JCGM 1st Meeting (2004)","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}}],"docs":{},"source":"# Port of lib/pubid/jcgm/parser.rb: JCGM base, meeting, amendment and\n# corrigendum identifiers, and the named GUM/VIM guides.\n\ngrammar PubidJcgm version \"0.1.0\" {\n century = \"19\" / \"20\"\n year_digits = century 2*digit !digit\n month_digits = 2*digit\n day_digits = 2*digit\n digits = 1*digit\n publisher = \"JCGM\" as publisher\n ordinal_suffix = \"st\" / \"nd\" / \"rd\" / \"th\"\n gum_number = \"GUM-\" (1*digit) as gum_number\n standard_number = digits as number\n number_portion = gum_number / standard_number\n full_date = (year_digits dash month_digits dash day_digits) as date\n year_only = year_digits as date\n date_portion = colon (full_date / year_only)\n language_portion = \"(\" ((\"E/F\" / \"F/E\") / upper) as languages \")\"\n base = publisher space number_portion [date_portion] [language_portion]\n meeting_identifier = publisher space digits as number ordinal_suffix space \"Meeting\" as type_with_stage [(space \"(\" year_digits as date \")\")]\n amendment_identifier = (base as base) slash \"Amd\" as type_with_stage space digits as number [(colon (full_date / year_only))]\n corrigendum_identifier = (base as base) space \"Corrigendum\" as type_with_stage\n numbered_corrigendum_identifier = (base as base) slash \"Cor\" as type_with_stage space digits as number [(colon (full_date / year_only))]\n named_number = (\"GUM\" / (\"VIM\" [(dash digits)])) as number\n named_guide_identifier = publisher space named_number\n identifier = meeting_identifier / amendment_identifier / corrigendum_identifier / numbered_corrigendum_identifier / base / named_guide_identifier\n upper = %x41-5A\n digit = %x30-39\n slash = \"/\"\n dash = \"-\"\n colon = \":\"\n space = \" \"\n entry identifier: identifier\n\n test {\n accept \"JCGM 200:2008(F)\"\n accept \"JCGM 100:2008\"\n accept \"JCGM 1st Meeting (2004)\"\n reject \"nonsense\"\n }\n}\n","source_hash":"c4a5cc929b1d59a996d52826fb4199659b3eaee6c645bc5064ce46804be96b59","compiler_version":"1.3.93","checksum":"sha256:b1816fde60067cea27889e934e7bc6064a1618c5f61fdcfb574318bbbb75d496"}
1
+ {"bindings":null,"version":"0.1.0","grammar":"PubidJcgm","shape":"parsanol-tree/v2","binding_version":1,"entries":{"identifier":{"root":"identifier","grammar":{"atoms":[{"Entity":{"atom":36}},{"Entity":{"atom":3}},{"Str":{"pattern":"JCGM"}},{"Named":{"name":"publisher","atom":2}},{"Entity":{"atom":5}},{"Str":{"pattern":" "}},{"Entity":{"atom":9}},{"Entity":{"atom":8}},{"Re":{"pattern":"[\\x30-\\x39]"}},{"Repetition":{"atom":7,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"number","atom":6}},{"Entity":{"atom":16}},{"Str":{"pattern":"st"}},{"Str":{"pattern":"nd"}},{"Str":{"pattern":"rd"}},{"Str":{"pattern":"th"}},{"Alternative":{"atoms":[12,13,14,15]}},{"Entity":{"atom":5}},{"Str":{"pattern":"Meeting"}},{"Named":{"name":"type_with_stage","atom":18}},{"Entity":{"atom":5}},{"Str":{"pattern":"("}},{"Entity":{"atom":31}},{"Entity":{"atom":26}},{"Str":{"pattern":"19"}},{"Str":{"pattern":"20"}},{"Alternative":{"atoms":[24,25]}},{"Entity":{"atom":8}},{"Repetition":{"atom":27,"min":2,"max":null,"tag":"Repetition"}},{"Entity":{"atom":8}},{"Lookahead":{"atom":29,"positive":false}},{"Sequence":{"atoms":[23,28,30]}},{"Named":{"name":"date","atom":22}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[20,21,32,33]}},{"Repetition":{"atom":34,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[1,4,10,11,17,19,35]}},{"Entity":{"atom":106}},{"Entity":{"atom":86}},{"Entity":{"atom":3}},{"Entity":{"atom":5}},{"Entity":{"atom":51}},{"Entity":{"atom":47}},{"Str":{"pattern":"GUM-"}},{"Entity":{"atom":8}},{"Repetition":{"atom":44,"min":1,"max":null,"tag":"Repetition"}},{"Named":{"name":"gum_number","atom":45}},{"Sequence":{"atoms":[43,46]}},{"Entity":{"atom":50}},{"Entity":{"atom":9}},{"Named":{"name":"number","atom":49}},{"Alternative":{"atoms":[42,48]}},{"Entity":{"atom":72}},{"Entity":{"atom":54}},{"Str":{"pattern":":"}},{"Entity":{"atom":67}},{"Entity":{"atom":31}},{"Entity":{"atom":58}},{"Str":{"pattern":"-"}},{"Entity":{"atom":61}},{"Entity":{"atom":8}},{"Repetition":{"atom":60,"min":2,"max":null,"tag":"Repetition"}},{"Entity":{"atom":58}},{"Entity":{"atom":65}},{"Entity":{"atom":8}},{"Repetition":{"atom":64,"min":2,"max":null,"tag":"Repetition"}},{"Sequence":{"atoms":[56,57,59,62,63]}},{"Named":{"name":"date","atom":66}},{"Entity":{"atom":70}},{"Entity":{"atom":31}},{"Named":{"name":"date","atom":69}},{"Alternative":{"atoms":[55,68]}},{"Sequence":{"atoms":[53,71]}},{"Repetition":{"atom":52,"min":0,"max":1,"tag":"Maybe"}},{"Entity":{"atom":84}},{"Str":{"pattern":"("}},{"Str":{"pattern":"E/F"}},{"Str":{"pattern":"F/E"}},{"Alternative":{"atoms":[76,77]}},{"Entity":{"atom":80}},{"Re":{"pattern":"[\\x41-\\x5a]"}},{"Alternative":{"atoms":[78,79]}},{"Named":{"name":"languages","atom":81}},{"Str":{"pattern":")"}},{"Sequence":{"atoms":[75,82,83]}},{"Repetition":{"atom":74,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[39,40,41,73,85]}},{"Named":{"name":"base","atom":38}},{"Entity":{"atom":89}},{"Str":{"pattern":"/"}},{"Str":{"pattern":"Amd"}},{"Named":{"name":"type_with_stage","atom":90}},{"Entity":{"atom":5}},{"Str":{"pattern":"."}},{"Entity":{"atom":5}},{"Repetition":{"atom":94,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[93,95]}},{"Alternative":{"atoms":[92,96]}},{"Entity":{"atom":9}},{"Named":{"name":"number","atom":98}},{"Entity":{"atom":54}},{"Entity":{"atom":67}},{"Entity":{"atom":70}},{"Alternative":{"atoms":[101,102]}},{"Sequence":{"atoms":[100,103]}},{"Repetition":{"atom":104,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[87,88,91,97,99,105]}},{"Entity":{"atom":113}},{"Entity":{"atom":86}},{"Named":{"name":"base","atom":108}},{"Entity":{"atom":5}},{"Str":{"pattern":"Corrigendum"}},{"Named":{"name":"type_with_stage","atom":111}},{"Sequence":{"atoms":[109,110,112]}},{"Entity":{"atom":134}},{"Entity":{"atom":86}},{"Named":{"name":"base","atom":115}},{"Entity":{"atom":89}},{"Str":{"pattern":"Cor"}},{"Named":{"name":"type_with_stage","atom":118}},{"Entity":{"atom":5}},{"Str":{"pattern":"."}},{"Entity":{"atom":5}},{"Repetition":{"atom":122,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[121,123]}},{"Alternative":{"atoms":[120,124]}},{"Entity":{"atom":9}},{"Named":{"name":"number","atom":126}},{"Entity":{"atom":54}},{"Entity":{"atom":67}},{"Entity":{"atom":70}},{"Alternative":{"atoms":[129,130]}},{"Sequence":{"atoms":[128,131]}},{"Repetition":{"atom":132,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[116,117,119,125,127,133]}},{"Entity":{"atom":86}},{"Entity":{"atom":149}},{"Entity":{"atom":3}},{"Entity":{"atom":5}},{"Entity":{"atom":148}},{"Str":{"pattern":"GUM"}},{"Str":{"pattern":"VIM"}},{"Entity":{"atom":58}},{"Entity":{"atom":9}},{"Sequence":{"atoms":[142,143]}},{"Repetition":{"atom":144,"min":0,"max":1,"tag":"Maybe"}},{"Sequence":{"atoms":[141,145]}},{"Alternative":{"atoms":[140,146]}},{"Named":{"name":"number","atom":147}},{"Sequence":{"atoms":[137,138,139]}},{"Alternative":{"atoms":[0,37,107,114,135,136]}}],"root":150},"bindings":[]}},"preprocess":{},"tables":{},"lint":{"order_warnings":["rule identifier: branches 1 and 2 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 1 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 1 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 1 and 5 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 1 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 2 and 3 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 2 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 2 and 5 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 2 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 3 and 4 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 3 and 5 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 3 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 4 and 5 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 4 and 6 are order-dependent (shared first bytes; ordered choice is decisive)","rule identifier: branches 5 and 6 are order-dependent (shared first bytes; ordered choice is decisive)"]},"default_entry":"identifier","render":{},"derive":{},"tests":[{"entry":"identifier","kind":"accept","input":"JCGM 200:2008(F)","expect":{}},{"entry":"identifier","kind":"accept","input":"JCGM 100:2008","expect":{}},{"entry":"identifier","kind":"accept","input":"JCGM 1st Meeting (2004)","expect":{}},{"entry":"identifier","kind":"accept","input":"JCGM 100:2008/Amd.1:2026","expect":{}},{"entry":"identifier","kind":"accept","input":"JCGM 101:2008/Cor.1:2009","expect":{}},{"entry":"identifier","kind":"reject","input":"nonsense","expect":{}}],"docs":{},"source":"# Port of lib/pubid/jcgm/parser.rb: JCGM base, meeting, amendment and\n# corrigendum identifiers, and the named GUM/VIM guides.\n\ngrammar PubidJcgm version \"0.1.0\" {\n century = \"19\" / \"20\"\n year_digits = century 2*digit !digit\n month_digits = 2*digit\n day_digits = 2*digit\n digits = 1*digit\n publisher = \"JCGM\" as publisher\n ordinal_suffix = \"st\" / \"nd\" / \"rd\" / \"th\"\n gum_number = \"GUM-\" (1*digit) as gum_number\n standard_number = digits as number\n number_portion = gum_number / standard_number\n full_date = (year_digits dash month_digits dash day_digits) as date\n year_only = year_digits as date\n date_portion = colon (full_date / year_only)\n language_portion = \"(\" ((\"E/F\" / \"F/E\") / upper) as languages \")\"\n base = publisher space number_portion [date_portion] [language_portion]\n meeting_identifier = publisher space digits as number ordinal_suffix space \"Meeting\" as type_with_stage [(space \"(\" year_digits as date \")\")]\n amendment_identifier = (base as base) slash \"Amd\" as type_with_stage (space / (\".\" [space])) digits as number [(colon (full_date / year_only))]\n corrigendum_identifier = (base as base) space \"Corrigendum\" as type_with_stage\n numbered_corrigendum_identifier = (base as base) slash \"Cor\" as type_with_stage (space / (\".\" [space])) digits as number [(colon (full_date / year_only))]\n named_number = (\"GUM\" / (\"VIM\" [(dash digits)])) as number\n named_guide_identifier = publisher space named_number\n identifier = meeting_identifier / amendment_identifier / corrigendum_identifier / numbered_corrigendum_identifier / base / named_guide_identifier\n upper = %x41-5A\n digit = %x30-39\n slash = \"/\"\n dash = \"-\"\n colon = \":\"\n space = \" \"\n entry identifier: identifier\n\n test {\n accept \"JCGM 200:2008(F)\"\n accept \"JCGM 100:2008\"\n accept \"JCGM 1st Meeting (2004)\"\n accept \"JCGM 100:2008/Amd.1:2026\"\n accept \"JCGM 101:2008/Cor.1:2009\"\n reject \"nonsense\"\n }\n}\n","source_hash":"d380467460b56523c7a36149a52e36904cd94a76406359f763847cfd4b177e8b","compiler_version":"1.3.93","checksum":"sha256:bf00524a2d18b2ab81764b53960b410509b53e924ced7ec438ab2494fcd0fc7e"}
@@ -29,3 +29,5 @@ These notes were part of the root `CLAUDE.md`. Read them before you change `lib/
29
29
  **Known residue, deliberately unchanged:** after the map moved, `Pubid::Iec::Identifier.from_hash({"number" => "60038"})` — a hand-written hash with **no `_type`**, on the bare base class — silently drops the key instead of building an `Identifier` that holds it. That object is already degenerate (`to_s` on it raises `NoMethodError` for `publisher_portion`) and nothing in this repo or in `relaton-iec` constructs one. (hand-off: iec-number-string.)
30
30
 
31
31
  **Two parent-slot gaps recorded by the cross-flavor `base` survey** (`lib/pubid/bsi/CLAUDE.md` has the measured version). `Identifiers::TestReportForm#cispr_identifier` is a polymorphic `Identifier` parent slot with no `#root` override, so `root` returns the form itself. And `Identifiers::ConsolidatedIdentifier#base_document` returns `identifiers&.first` **without** the recursive `&.base_document` that its BSI, CSA and CEN counterparts use — a one-level peel where the others peel fully, so a consolidated edition of a supplement does not reduce to the bare standard. Neither is chased here; both are pinned by nothing, which is why they are written down.
32
+
33
+ **Stage codes — the SSOT and what is settled.** The authoritative source for IEC stage abbreviations is committed in this repo: **`data/iec/stage-codes.md`** — the 52-row IEC/ISO stage-code correspondence table transcribed from IEC's official *IEC/ISO Harmonized APIs* documents (v0.0.8 2023-06, api-portal.iec.ch; v0.0.7 2021-05, iec-apiportal.apigee.io — the successors that replace the IEC Data Feeds), the legacy Data-Feeds field examples, and the IS-lifecycle ladder from `StageCodes_IS_v2.pdf` (2023-06). `www.iec.ch` itself is fully WAF-gated to non-browsers and the Wayback Machine holds no snapshots of its PDFs, so the extract — not the unreachable original — is the reference. **`Pubid::Iec.locate_stage` reads `TYPED_STAGES` only** (`lib/pubid/iec.rb`); `PROJECT_STAGES` (AFDIS, TCDV, PRVC…) are not queryable through it. Every one of metanorma-iec's 55 `STAGE_CODES` — the list quoted in pubid#237 — resolves, locked by `spec/pubid/iec/typed_stages_spec.rb` in the issue's own order; aliases are always **appended, never prepended**, so `abbr.first` (what renders) is unchanged. Notable rulings from that pass: **ADIS** is the legacy Data-Feeds spelling of AFDIS ("Approved for FDIS circulation", ISO_LEVEL 40.99) and is an alias on `cdv` (the typed stage claiming 40.99); **CDISH/CDPAS** are the 50.20 rows of the DISH/DPAS deliverables, not "committee draft ISH/PAS" (30.xx); bare **PUB**, and the codes **AWG/CANV**, are defined by no IEC source and are deliberately unregistered (pubid#572, closed — the extract's "absent from every source" section is the standing evidence). **Reconciliation items recorded in the extract, deliberately not applied:** DELPUB sits at 99.60 there while the `is` ladder does not claim 99.60; CAN carries two codes (30.98 IS/TR/TS, 50.98 PAS/ISH/COR); the rows CDISH-table-siblings DECDISH/MERGED/SPLIT/PCC/PRVN/DEL/RPUB/TPUB and the per-deliverable translation/preparation families are not registered as typed-stage abbreviations.
@@ -21,7 +21,9 @@ module Pubid
21
21
  code: :np,
22
22
  stage_code: :np,
23
23
  type_code: :is,
24
- abbr: ["NP"],
24
+ # DEL is 10.98 (IS/TR/TS "Deleted/abandoned") and PRVN 10.60
25
+ # ("Preparation of RVN") — data/iec/stage-codes.md
26
+ abbr: ["NP", "DEL", "PRVN"],
25
27
  name: "New Proposal",
26
28
  harmonized_stages: %w[10.00 10.60 10.92 10.98],
27
29
  ),
@@ -42,7 +44,7 @@ module Pubid
42
44
  code: :anw,
43
45
  stage_code: :anw,
44
46
  type_code: :is,
45
- abbr: ["ANW"],
47
+ abbr: ["ANW", "AWI"],
46
48
  name: "Approved New Work Item",
47
49
  harmonized_stages: %w[10.99 20.00],
48
50
  ),
@@ -50,7 +52,8 @@ module Pubid
50
52
  code: :wd,
51
53
  stage_code: :wd,
52
54
  type_code: :is,
53
- abbr: ["WD"],
55
+ # DEL is 20.98 for the ISH deliverable ("Deleted/abandoned")
56
+ abbr: ["WD", "DEL"],
54
57
  name: "Working Draft",
55
58
  harmonized_stages: %w[20.20 20.60 20.98 20.99],
56
59
  ),
@@ -60,15 +63,19 @@ module Pubid
60
63
  type_code: :is,
61
64
  abbr: %w[CD 2CD 3CD 4CD 5CD 6CD 7CD 8CD 9CD
62
65
  ACD A2CD A3CD A4CD A5CD A6CD A7CD A8CD A9CD
63
- CDM],
66
+ CDM PCC SPLIT MERGED],
64
67
  name: "Committee Draft",
65
- harmonized_stages: %w[30.00 30.20 30.60 30.92 30.98 30.99],
68
+ harmonized_stages: %w[30.00 30.20 30.60 30.92 30.96 30.97 30.98
69
+ 30.99],
66
70
  ),
67
71
  Pubid::Components::TypedStage.new(
68
72
  code: :cdv,
69
73
  stage_code: :cdv,
70
74
  type_code: :is,
71
- abbr: %w[CDV ACDV CCDV CDVM NCDV],
75
+ # ADIS is the legacy Data Feeds spelling of AFDIS ("Approved for
76
+ # FDIS circulation", harmonized 40.99) — see
77
+ # data/iec/stage-codes.md
78
+ abbr: %w[CDV ACDV CCDV CDVM NCDV DIS ADIS],
72
79
  name: "Committee Draft for Vote",
73
80
  harmonized_stages: %w[40.00 40.20 40.60 40.92 40.98 40.99],
74
81
  ),
@@ -85,10 +92,10 @@ module Pubid
85
92
  stage_code: :published,
86
93
  type_code: :is,
87
94
  abbr: ["", "BPUB", "PPUB", "APUB", "WPUB", "DELPUB",
88
- "RR", "CAN"],
95
+ "RR", "CAN", "RPUB", "TPUB", "DECPUB"],
89
96
  name: "International Standard",
90
97
  harmonized_stages: %w[60.00 60.60 90.20 90.60 90.92 90.93 90.99
91
- 95.20 95.60 95.92 95.99],
98
+ 95.20 95.60 95.92 95.99 99.60],
92
99
  ),
93
100
  ].freeze
94
101
 
@@ -54,7 +54,9 @@ module Pubid
54
54
  code: :dish,
55
55
  stage_code: :draft,
56
56
  type_code: :ish,
57
- abbr: ["DISH"],
57
+ # CDISH is the 50.20 row of IEC's stage-code table
58
+ # ("Draft circulated as DISH") — see data/iec/stage-codes.md
59
+ abbr: ["DISH", "CDISH"],
58
60
  name: "Draft Interpretation Sheet",
59
61
  harmonized_stages: %w[50.00 50.20 50.60 50.92 50.98 50.99],
60
62
  ),
@@ -53,7 +53,9 @@ module Pubid
53
53
  code: :dpas,
54
54
  stage_code: :draft,
55
55
  type_code: :pas,
56
- abbr: ["DPAS"],
56
+ # CDPAS is the 50.20 row of IEC's stage-code table
57
+ # ("Draft circulated as DPAS") — see data/iec/stage-codes.md
58
+ abbr: ["DPAS", "CDPAS"],
57
59
  name: "Draft Publicly Available Specification",
58
60
  harmonized_stages: %w[50.00 50.20 50.60 50.92 50.98 50.99],
59
61
  ),
data/lib/pubid/iec.rb CHANGED
@@ -51,13 +51,51 @@ module Pubid
51
51
  end
52
52
  end
53
53
 
54
+ # The per-deliverable PROJECT_STAGES (AFDIS, TCDV, PRVC, DECDISH, …)
55
+ # as TypedStage objects, so they are queryable like typed stages.
56
+ # Deliberately NOT merged into all_typed_stages: the URN parser's
57
+ # stage reverse-map matches on harmonized_stages.first and would
58
+ # start resolving project codes (stage-IS-40.99 etc.) that resolve
59
+ # to "" today — a parse-behavior change outside a lookup-only round.
60
+ def self.all_project_stages
61
+ @all_project_stages ||= identifier_types.flat_map do |klass|
62
+ next [] unless klass.const_defined?(:PROJECT_STAGES)
63
+
64
+ type_key = klass.type[:key]
65
+ klass.const_get(:PROJECT_STAGES).map do |key, stage|
66
+ Pubid::Components::TypedStage.new(
67
+ code: key.to_sym,
68
+ stage_code: key.to_sym,
69
+ type_code: type_key,
70
+ abbr: [stage[:abbr]].flatten,
71
+ name: stage[:name],
72
+ harmonized_stages: stage[:harmonized_stages],
73
+ )
74
+ end
75
+ end
76
+ end
77
+
54
78
  def self.locate_type(code)
55
79
  identifier_types.find { |t| t.type[:key].to_s == code.to_s }
56
80
  end
57
81
 
58
82
  def self.locate_stage(abbr)
59
83
  abbr_str = abbr.to_s.upcase
60
- all_typed_stages.find { |s| s.abbr.any? { |a| a.to_s.upcase == abbr_str } }
84
+ all_typed_stages.find { |s| s.abbr.any? { |a| a.to_s.upcase == abbr_str } } ||
85
+ all_project_stages.find { |s| s.abbr.any? { |a| a.to_s.upcase == abbr_str } }
86
+ end
87
+
88
+ # Harmonized code -> typed stage ("99.60" -> is, "30.96" -> cd). The
89
+ # IS family wins a shared code — the same deterministic rule as the
90
+ # ISO lookup (pubid#570), because registry order is load-order
91
+ # dependent. Project stages are not candidates: their codes overlap
92
+ # the typed ladders by design (AFDIS 40.99 is also cdv's 40.99).
93
+ def self.locate_stage_by_harmonized_code(code)
94
+ code_str = code.to_s
95
+ candidates = all_typed_stages.select do |s|
96
+ s.harmonized_stages&.include?(code_str)
97
+ end
98
+ candidates.find { |s| s.type_code.to_s == "is" } || candidates.first
61
99
  end
62
100
  end
63
101
  end
@@ -4,4 +4,6 @@ JCGM meetings and URN parsing.
4
4
 
5
5
  These notes were part of the root `CLAUDE.md`. Read them before you change `lib/pubid/jcgm/` or `spec/pubid/jcgm/`. The root file keeps the cross-flavor contract that every flavor obeys.
6
6
 
7
+ - **Dotted supplement spellings are INPUT-ONLY, and that is deliberate house practice**: BIPM's publications page prints `JCGM 100:2008/Amd.1:2026` (Amendment 1 to the GUM, June 2026), and that dotted form is **old syntax** — the same practice as ISO, we **normalize to the new style `Amd 1`** on render. The grammar's supplement separator is `(space / ("." [space]))` for both `Amd` and `Cor`, so `Amd 1`, `Amd.1` and `Amd. 1` all parse while rendering stays `Amd 1`; the fixtures record the dotted rows as normalizing pairs. **The runtime is the baked artifact, not `parser.rb`** — the rule was compiled into `data/parg/jcgm.json` with the pinned parsanol 1.3.93 (the exe resolves the latest installed compiler otherwise; force `gem "parsanol", "1.3.93"` and verify `compiler_version` after baking), and the grammar's `test` section bakes `accept "JCGM 100:2008/Amd.1:2026"` + `accept "JCGM 101:2008/Cor.1:2009"` so the artifact self-checks both forms. The full GUM series as BIPM publishes it parses and round-trips: `JCGM GUM-1:2023`, `GUM-5:2026`, `GUM-6:2020` (as `GumGuide`, `urn:jcgm:gum.6:2020`) — the ask of metanorma/mn-samples-jcgm#42. Locked by `spec/pubid/jcgm/identifier_spec.rb`'s dotted-spellings examples.
8
+
7
9
  - **JCGM meeting year is optional (`JCGM 11st Meeting`)**: a meeting's trailing ` (YYYY)` group is a *separable* component, not part of its identity — JCGM numbers its meetings in one sequence, so the ordinal alone names the event. `rule(:meeting_identifier)` wraps the **whole** group in `.maybe` (not the year inside it, so a dangling `(`, an empty `()` or a truncated `(200)` still fails), mirroring `date_portion.maybe` in `rule(:base)`; `render_meeting` appends the group only when `date` is present. No builder or serialization change was needed: `Builder::Base#assign_attributes` iterates the parsed hash, so an absent `:date` key is simply never assigned, and `SingleIdentifier#emit_kv` already drops a nil value — a dateless meeting is `{_type, number}` and a dated one is byte-identical to before, so **no index migration follows**. **The load-bearing third surface is `UrnParser#parse_meeting_urn`**: `UrnGenerator#generate_meeting_urn` had *always* guarded the date and emitted the short `urn:jcgm:meeting:11`, but the parser rebuilt the ungrammatical `"JCGM 11st Meeting ()"` from it — so the dateless form must be made optional on the URN read-back too, or the round-trip the relaton index depends on breaks. **The whole `UrnParser` was converted to build identifiers directly from the URN segments** (the BIPM pattern; 5 of 38 flavors construct directly, the rest use the shared `flavor_parse` helper, which stays for them). Rendering a string and re-parsing it flattens the segments and silently loses whatever the string form cannot carry, which is what three **pre-existing** defects were: `urn:jcgm:gum.6:2020` **raised** (`"JCGM gum.6:2020"` is not a grammar form); the language segment was dropped (`urn:jcgm:100:2008:en` → `JCGM 100:2008`); and `number, year = parts` discarded every segment past the second, so a supplement decayed into **the standard it amends** — `urn:jcgm:200:2008:corrigendum` → `JCGM 200:2008`, a different document, with no error. The supplement marker (`corrigendum`/`amendment`, written by the generator as the `type_code`) splits the segments: everything before it is the base document, everything after is the supplement's own number and date. Four things the string round-trip used to supply for free are now explicit, and each is load-bearing: (1) **`typed_stage` comes from `Jcgm.locate_stage`, the same registry lookup `Builder#locate_typed_stage` uses — NOT from the attribute default** — because `SingleIdentifier.published_typed_stage` additionally sets `original_abbr`, which would make a URN-reconstructed id **unequal** to a parsed one; the marker is mapped to the abbreviation the registry indexes (`amendment` → `Amd`, since `locate_stage` matches `abbr`, not `type_code`), and the class then comes from `Jcgm.locate_type(typed_stage.type_code)` so one lookup fixes both. **Guide and GumGuide deliberately take the default instead**, because the grammar emits no type token for them and `Builder#build` fills theirs from `published_typed_stage` too — matching the builder means matching *which* of the two paths it took. (2) The meeting number is normalized with **`to_i`**, matching `Identifiers::Meeting.ordinal`, so `011` still reads back as `11` and a missing or non-numeric segment as `0`. (3) The year is **validated against the grammar's `19xx|20xx`** and rejected with `Pubid::UrnParser::Errors::ParseError` (the easc/gost convention) — with no re-parse, nothing else would catch a malformed segment. (4) A language code is mapped back through the inverse of `Builder::Base::LANG_CHAR_MAP` to restore `original_code`, which is what the renderer prints (`en,fr` → `(E/F)`). **KNOWN GAP, generator-side and deliberately unchanged**: a full date renders as its year alone (`JCGM GUM-1:2022-11-28` → `urn:jcgm:gum.1:2022`), so a URN cannot restore month and day; widening it would change already-published URNs. 30 of the 32 corpus ids now round-trip through the URN byte-exactly, and those 2 are the truncated-date pair. (That `original_abbr` asymmetry is **since fixed** — see `lib/pubid/iec/CLAUDE.md` — so the default now agrees too and the lookup mirrors the builder for clarity rather than necessity.) **Deliberately not a typed error**: the earlier reading of this defect was to raise on a nil date; an undated meeting reference is unambiguous and legitimate, and raising would leave relaton's `Bib::ItemData#to_most_recent_reference` with nothing to return. **Do not add the English teens exception to `Identifiers::Meeting.ordinal`** — the real records print `11st`/`12nd`/`13rd`, and three published documents round-trip through that naive rule. Locked by `spec/pubid/jcgm/meeting_partial_spec.rb` plus a bare-form row in `urn_parser_spec.rb`. **Still open, separate call**: `Meeting.new(number: "11").to_s` raises `NoMethodError` (`number` is a `Components::Code`; a bare String is accepted at construction and only explodes at render), and `Meeting#ordinal` renders `"0th"` for a nil `number`. (hand-off: jcgm-meeting-render-nil-date.)
@@ -33,10 +33,17 @@ module Pubid
33
33
  rule(:numbered_corrigendum_identifier) do
34
34
  base.as(:base) >>
35
35
  str("/") >> str("Cor").as(:type_with_stage) >>
36
- space >> digits.as(:number) >>
36
+ supplement_separator >> digits.as(:number) >>
37
37
  corrigendum_date.maybe
38
38
  end
39
39
 
40
+ # "Amd 1" and the dotted "Amd.1:2026" BIPM prints on its
41
+ # publications page (bipm.org, JCGM 100:2008/Amd.1:2026) are both
42
+ # accepted on input; rendering stays "Amd 1".
43
+ rule(:supplement_separator) do
44
+ space | (str(".") >> space.maybe)
45
+ end
46
+
40
47
  # Dateless "named" guides: "JCGM GUM", "JCGM VIM-3" (and future "VIM-N").
41
48
  # Last in the alternation — only reached when base fully fails
42
49
  # (these have no date, so number_portion/date_portion never match).
@@ -83,7 +90,7 @@ module Pubid
83
90
  rule(:amendment_identifier) do
84
91
  base.as(:base) >>
85
92
  str("/") >> amendment_type >>
86
- space >> digits.as(:number) >>
93
+ supplement_separator >> digits.as(:number) >>
87
94
  amendment_date.maybe
88
95
  end
89
96
 
data/lib/pubid/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pubid
4
- VERSION = "2.0.0.pre.alpha.47"
4
+ VERSION = "2.0.0.pre.alpha.49"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pubid
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.pre.alpha.47
4
+ version: 2.0.0.pre.alpha.49
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-10-21 00:00:00.000000000 Z
11
+ date: 2026-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lutaml-model
@@ -68,6 +68,7 @@ files:
68
68
  - conformance/pending.yaml
69
69
  - data/bipm/update_codes.yaml
70
70
  - data/ccsds/update_codes.yaml
71
+ - data/iec/stage-codes.md
71
72
  - data/iec/update_codes.yaml
72
73
  - data/ieee/update_codes.yaml
73
74
  - data/iso/update_codes.yaml