pubid 2.0.20 → 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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.adoc +73 -31
  3. data/data/iec/legacy-notation.md +298 -0
  4. data/data/iho/s4-reserved-specs.yaml +15 -0
  5. data/data/iso/historic-schemes.md +368 -0
  6. data/data/parg/bipm.json +1 -1
  7. data/data/parg/bipm.parg +46 -11
  8. data/data/parg/iec.json +1 -1
  9. data/data/parg/iho.json +1 -1
  10. data/data/parg/iho.parg +128 -11
  11. data/data/parg/iso.json +1 -1
  12. data/data/parg/iso.parg +142 -2
  13. data/data/parg/plateau.json +1 -1
  14. data/data/parg/tables/iec_typed_stages.yaml +3 -0
  15. data/data/parg/tables/typed_stages_ballots.yaml +6 -0
  16. data/data/parg/tables/typed_stages_supplements.yaml +1 -0
  17. data/lib/pubid/bipm/CLAUDE.md +6 -1
  18. data/lib/pubid/bipm/builder.rb +20 -1
  19. data/lib/pubid/bipm/renderer.rb +14 -2
  20. data/lib/pubid/bipm/urn_generator.rb +80 -14
  21. data/lib/pubid/bipm/urn_parser.rb +70 -6
  22. data/lib/pubid/ecma/CLAUDE.md +12 -0
  23. data/lib/pubid/ecma/identifier.rb +1 -2
  24. data/lib/pubid/ecma/parser.rb +49 -0
  25. data/lib/pubid/iec/CLAUDE.md +9 -1
  26. data/lib/pubid/iec/builder.rb +47 -0
  27. data/lib/pubid/iec/identifier.rb +19 -70
  28. data/lib/pubid/iec/identifiers/amendment.rb +34 -0
  29. data/lib/pubid/iec/identifiers/corrigendum.rb +6 -1
  30. data/lib/pubid/iec/identifiers/fragment_identifier.rb +3 -3
  31. data/lib/pubid/iec/single_identifier.rb +69 -4
  32. data/lib/pubid/iec/supplement_identifier.rb +89 -83
  33. data/lib/pubid/iho/CLAUDE.md +54 -1
  34. data/lib/pubid/iho/builder.rb +105 -2
  35. data/lib/pubid/iho/identifiers/base.rb +52 -1
  36. data/lib/pubid/iho/pubid_urn_schema.rb +12 -4
  37. data/lib/pubid/iho/renderer.rb +39 -2
  38. data/lib/pubid/iho/reserved_specs.rb +54 -0
  39. data/lib/pubid/iho/urn_generator.rb +6 -0
  40. data/lib/pubid/iho/urn_parser.rb +50 -9
  41. data/lib/pubid/iho.rb +1 -0
  42. data/lib/pubid/iso/CLAUDE.md +49 -0
  43. data/lib/pubid/iso/builder.rb +77 -0
  44. data/lib/pubid/iso/identifiers/amendment.rb +1 -1
  45. data/lib/pubid/iso/single_identifier.rb +7 -1
  46. data/lib/pubid/nist/CLAUDE.md +49 -0
  47. data/lib/pubid/nist/builder.rb +5 -0
  48. data/lib/pubid/nist/identifiers/base.rb +42 -1
  49. data/lib/pubid/nist/identifiers/federal_information_processing_standards.rb +44 -5
  50. data/lib/pubid/nist/preprocessor.rb +18 -0
  51. data/lib/pubid/nist/urn_parser.rb +1 -1
  52. data/lib/pubid/plateau/CLAUDE.md +35 -0
  53. data/lib/pubid/plateau/builder.rb +4 -0
  54. data/lib/pubid/plateau/identifiers/handbook.rb +10 -2
  55. data/lib/pubid/plateau/pubid_urn_schema.rb +1 -0
  56. data/lib/pubid/plateau/renderer.rb +6 -1
  57. data/lib/pubid/sae/CLAUDE.md +11 -2
  58. data/lib/pubid/version.rb +1 -1
  59. metadata +7 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5bf271d5120b52861eb84e3c8cf375c0bdcebf61ecf3c5666af4a888590ccbc7
4
- data.tar.gz: 2516fdc850d72aab4de4a589a9187d840d685cc2bc101f1a501e088a43d6287e
3
+ metadata.gz: 48dbbe488a6339c779e17067ea3ff034db07d1c2ae636d802cc43d4f96a3506e
4
+ data.tar.gz: 2b557f9660b432e698af587c79c8c8a40f4dd66dcf52475f3b5d6cc1b16ef08a
5
5
  SHA512:
6
- metadata.gz: 7127733e72ab23c53b55b62bc17258bd936055fef0a3840d61ef81d66dc98d193a9e30cedab3d1178e50b51a26021a8b13c91141b1a9c49e4c5b67448b346987
7
- data.tar.gz: b19d01682a51d7b7864643b093e3379fe273fa34370c1f151809e0f84d82f965501dd14a6e95d0956ee9f2206d9e9757eb24c89eec166c8fa7395ed2c7bd6bd6
6
+ metadata.gz: f2f9c0e44fd4c976a403586366360ea6d4df2f2e3f51a3a584f06e676545b38cbac9e796e5caf611f67218706c3c54f20bf43fdb0e23c953253a580a96209d98
7
+ data.tar.gz: d98b1a76dd898d51afca3e70972949d9a5fd741deef5e69d73c2200dbff1eb69fd645f6b3806052dd390a362cd87a6497cf8f07de591e6f7592eedc2aa2867f6
data/README.adoc CHANGED
@@ -7,10 +7,13 @@ image:https://img.shields.io/github/commits-since/metanorma/pubid/latest.svg["Co
7
7
 
8
8
  == Quick Start
9
9
 
10
- [source,ruby]
10
+ [source,shell]
11
11
  ----
12
12
  gem install pubid
13
+ ----
13
14
 
15
+ [source,ruby]
16
+ ----
14
17
  require 'pubid/iso'
15
18
 
16
19
  # Parse an identifier
@@ -18,13 +21,13 @@ id = Pubid::Iso.parse("ISO 9001:2015/Amd 1:2020")
18
21
 
19
22
  # Access components
20
23
  id.publisher.body # => "ISO"
21
- id.number.number # => "9001"
24
+ id.root.number # => "9001"
22
25
 
23
26
  # Render back to string (round-trip)
24
27
  id.to_s # => "ISO 9001:2015/Amd 1:2020"
25
28
 
26
29
  # Generate URN
27
- id.to_urn # => "urn:iso:std:iso:9001:amd:2020:v1"
30
+ id.to_urn # => "urn:iso:std:iso:9001:amd:1:2020:v1"
28
31
  ----
29
32
 
30
33
  == Overview
@@ -78,12 +81,15 @@ README.
78
81
  |link:docs/flavor-guides/iec.md[IEC: TRF format grammars]
79
82
  |IECEE and IECEx Test Report Form patterns — the `&` part join, the
80
83
  `F_II`-style sub-versions, the `_EMC`/`_PS`/`_SOF` test-type suffixes, and the
81
- IECEx `_`/`v` separator. Ported from the archived pubid-iec README.
84
+ IECEx `_`/`v` separator — plus the general IEC forms (supplement faces,
85
+ CSV/CMV/RLV wrappers, standalone supplements). Ported from the archived
86
+ pubid-iec README.
82
87
 
83
88
  |link:docs/flavor-guides/plateau.md[PLATEAU: the Handbook #11 edition split]
84
89
  |Why an edition-1–3 "Handbook #11" reference with a part suffix parses as
85
- Handbook #06 / #04 (the rule encoded in `data/plateau/update_codes.yaml`).
86
- Ported from the archived pubid-plateau README.
90
+ Handbook #06 / #04 (the rule encoded in `data/plateau/update_codes.yaml`),
91
+ and the Handbook volume label (本編 / 本編+付録). Ported from the
92
+ archived pubid-plateau README.
87
93
 
88
94
  |link:docs/RENDERING_GUIDE.md[Advanced Rendering Styles Guide]
89
95
  |ISO short/long supplement abbreviation preservation and IEC `AMD1` vs
@@ -192,23 +198,22 @@ PubID V2 implements RFC 5141-bis compliant URN generation for ISO and IEC identi
192
198
 
193
199
  [source,ruby]
194
200
  ----
195
- require 'pubid/iso'
196
- require 'pubid/iec'
201
+ require 'pubid'
197
202
 
198
203
  # ISO: Parse and generate URN
199
204
  id = Pubid::Iso.parse("ISO/IEC 13818-1:2015/Amd 3:2016")
200
205
  id.to_urn
201
- # => "urn:iso:std:iso-iec:13818:-1:amd:2016:v3"
206
+ # => "urn:iso:std:iso-iec:13818:-1:amd:3:2016:v1"
202
207
 
203
208
  # IEC: Parse and generate URN
204
209
  id = Pubid::Iec.parse("IEC 60068-2-2:1974/Amd 1:1993")
205
210
  id.to_urn
206
- # => "urn:iec:std:iec:60068-2-2:1974:amd:1993:v1"
211
+ # => "urn:iec:std:iec:60068-2-2:1974:::::amd:1:1993"
207
212
 
208
213
  # Round-trip: parse URN back to identifier
209
- id = Pubid::Iso.parse_urn("urn:iso:std:iso:9001:amd:2020:v1")
210
- id.to_s # => "ISO 9001/Amd 1:2020"
211
- id.to_urn # => "urn:iso:std:iso:9001:amd:2020:v1"
214
+ id = Pubid::Iso.parse_urn("urn:iso:std:iso:9001:amd:1:2020:v1")
215
+ id.to_s # => "ISO 9001/AMD 1:2020"
216
+ id.to_urn # => "urn:iso:std:iso:9001:amd:1:2020:v1"
212
217
  ----
213
218
 
214
219
  === Features
@@ -228,9 +233,9 @@ id.to_urn # => "urn:iso:std:iso:9001:amd:2020:v1"
228
233
  `#to_urn` follows the registered grammar of the organization, which can
229
234
  drop values (RFC 5141 has no year). `#to_pubid_urn` is pubid's own URN: it
230
235
  carries every value, always round-trips through `Pubid.parse_urn`, and is
231
- safe to use as an HTTP path. ISO, IEEE, ANSI, ITU, NIST, ASTM, API, ASHRAE,
232
- ASME, JCGM, OIML, ETSI, GOST and PLATEAU support it now; the other flavors follow
233
- (link:docs/URN-SCHEMES.adoc[URN schemes]).
236
+ safe to use as an HTTP path. Every flavor supports it (the per-flavor
237
+ schemas live in `lib/pubid/<flavor>/pubid_urn_schema.rb`;
238
+ link:docs/URN-SCHEMES.adoc[URN schemes] documents each).
234
239
 
235
240
  [source,ruby]
236
241
  ----
@@ -410,6 +415,9 @@ Pubid::Ecma.parse("ECMA-418") === Pubid::Ecma.parse("ECMA-418-1")
410
415
  # => false (ECMA-418 has no part)
411
416
  Pubid::Ecma.parse("ECMA-418-1") === Pubid::Ecma.parse("ECMA-418-1 ed1")
412
417
  # => true
418
+ Pubid::Ecma.parse("ECMA-262 Edition 5.1") === Pubid::Ecma.parse("ECMA-262 ed5.1")
419
+ # => true (the human edition faces — "Edition 5.1", "ed 5.1", a bare
420
+ # trailing edition — parse in as aliases of the canonical " ed<N>" face)
413
421
  ----
414
422
 
415
423
  A reference asks for every part of a document with `all_parts`:
@@ -480,15 +488,18 @@ backend, without parsing.
480
488
  ----
481
489
  require 'pubid'
482
490
 
491
+ Pubid.eager_load_flavors! # flavor modules register lazily; load them all
492
+
483
493
  Pubid::Iso.prefixes
484
- # => ["ISO", "ISO/IEC", "IEC/ISO", "ISO/IEC/IEEE"]
494
+ # => ["ISO", "ISO/IEC", "IEC/ISO", "ISO/IEC/IEEE", "ISO/IEEE"]
485
495
 
486
496
  Pubid::Bsi.prefixes
487
- # => ["BS", "BSI", "DD", "PD", "PAS", "NA", "TS", "HB", ... , "BS EN ISO"]
497
+ # => ["BS", "BSI", "DD", "PD", "PAS", "NA", "TS", "HB", "PP", "BIP", "EP",
498
+ # "BS EN", "BS ISO", "BS IEC", "BS EN ISO", "BS EN IEC"]
488
499
 
489
500
  # Look up a single flavor by name
490
501
  Pubid.prefixes(:iec)
491
- # => ["IEC", "CISPR", "IECEE", "IECEx", "IECQ", "ISO/IEC", "IEC/ISO", "ISO/IEC/IEEE"]
502
+ # => ["IEC", "CISPR", "IECEE", "IECEx", "IECQ", "ISO/IEC", "IEC/ISO", "ISO/IEC/IEEE", "IEC/IEEE"]
492
503
 
493
504
  # Reverse index: prefix token => owning flavor(s)
494
505
  Pubid.prefix_flavors["ISO/IEC"] # => [:iec, :iso]
@@ -1012,7 +1023,7 @@ require 'pubid'
1012
1023
  id = Pubid::Nist.parse("NIST SP 800-53r5")
1013
1024
 
1014
1025
  # Access components
1015
- id.series # => "SP"
1026
+ id.series.to_s # => "SP"
1016
1027
  id.number # => "800-53"
1017
1028
  id.revision # => "r5"
1018
1029
 
@@ -1021,7 +1032,7 @@ id.to_s # => "NIST SP 800-53r5"
1021
1032
 
1022
1033
  # Parse historical NBS patterns
1023
1034
  id = Pubid::Nist.parse("NBS LCIRC 1019r1963")
1024
- id.to_s # => "NBS LCIRC 1019r1963"
1035
+ id.to_s # => "NBS LC 1019r1963"
1025
1036
 
1026
1037
  # CSM volume-number format
1027
1038
  id = Pubid::Nist.parse("NBS CSM v6n1")
@@ -1029,7 +1040,22 @@ id.to_s # => "NBS CSM v6n1"
1029
1040
 
1030
1041
  # Supplement with revision
1031
1042
  id = Pubid::Nist.parse("NBS CIRC 154supprev")
1032
- id.to_s # => "NBS CIRC 154supprev"
1043
+ id.to_s # => "NBS CIRC 154sup"
1044
+
1045
+ # FIPS PUB printed form: the official FIPS face is "FIPS PUB <number>"
1046
+ # ("PUB" is part of the Federal Information Processing Standards
1047
+ # PUBlication designation); every printed spelling parses to the same
1048
+ # identity (same hash, URN and MR string as the canonical parse)
1049
+ id = Pubid::Nist.parse("FIPS PUB 205")
1050
+ id.to_s # => "FIPS PUB 205"
1051
+ id = Pubid::Nist.parse("NIST FIPS PUB 205")
1052
+ id.to_s # => "FIPS PUB 205"
1053
+
1054
+ # Hyphenated numbers and the revision forms ride the alias too
1055
+ id = Pubid::Nist.parse("FIPS PUB 180-4")
1056
+ id.to_s # => "FIPS PUB 180-4"
1057
+ id = Pubid::Nist.parse("FIPS PUB 140-3 rev1")
1058
+ id.to_s # => "FIPS PUB 140-3r1"
1033
1059
  ----
1034
1060
 
1035
1061
  **Language:**
@@ -1039,20 +1065,34 @@ id.to_s # => "NBS CIRC 154supprev"
1039
1065
 
1040
1066
  ==== IEC
1041
1067
 
1042
- The IEC parser handles complex patterns including adopted standards and dual-published identifiers:
1068
+ The IEC parser handles the supplement faces, the value-added wrappers, the
1069
+ project-committee working documents, and the standalone supplements:
1043
1070
 
1044
1071
  [source,ruby]
1045
1072
  ----
1046
1073
  require 'pubid'
1047
1074
 
1048
- # Parse standard IEC identifier
1049
- id = Pubid::Iec.parse("IEC 62014-5 IEEE Std 1734-2011")
1050
- id.class # => Pubid::Iec::Identifiers::DualPublished
1051
- id.to_s # => "IEC 62014-5 and IEEE Std 1734-2011" # normalized
1075
+ # Supplements render the compact official face (AMD1 vs the long "Amd 1")
1076
+ id = Pubid::Iec.parse("IEC 60050-351:2013/AMD1:2016")
1077
+ id.to_s # => "IEC 60050-351:2013/AMD1:2016"
1078
+
1079
+ # Value-added editions are VapIdentifier wrappers (+AMD… CSV)
1080
+ id = Pubid::Iec.parse("IEC 60950-1:2005+AMD1:2009+AMD2:2013 CSV")
1081
+ id.class # => Pubid::Iec::Identifiers::VapIdentifier
1082
+ id.to_s # => "IEC 60950-1:2005+AMD1:2009+AMD2:2013 CSV"
1052
1083
 
1053
- # Space-separated dual identifiers are auto-detected
1054
- id = Pubid::Iec.parse("ANSI C37.61-1973 and IEEE Std 321-1973")
1055
- id.to_s # => "ANSI C37.61-1973 and IEEE Std 321-1973"
1084
+ # Working documents
1085
+ id = Pubid::Iec.parse("1/2457/FDIS")
1086
+ id.class # => Pubid::Iec::Identifiers::WorkingDocument
1087
+ id.to_s # => "1/2457/FDIS"
1088
+
1089
+ # A standalone supplement (a supplement face without its base) parses on
1090
+ # its own, is == its from_hash(to_hash) round trip, and its root is the
1091
+ # bare base number
1092
+ id = Pubid::Iec.parse("IEC/FDAM 60038-1")
1093
+ id.to_s # => "IEC FDAM 60038-1"
1094
+ Pubid.from_hash(id.to_hash) == id # => true
1095
+ id.root.to_s # => "IEC60038"
1056
1096
  ----
1057
1097
 
1058
1098
  ===== Pattern 4: Relationship Identifiers ✨
@@ -2206,12 +2246,14 @@ require 'pubid/nist'
2206
2246
  id = Pubid::Nist.parse("NIST SP 800-53r5")
2207
2247
 
2208
2248
  # Access components
2209
- id.series # => "SP"
2249
+ id.series.to_s # => "SP"
2210
2250
  id.number # => "800-53"
2211
2251
  id.revision # => "r5"
2212
2252
 
2213
2253
  # Render to string
2214
2254
  id.to_s # => "NIST SP 800-53r5"
2255
+ ----
2256
+
2215
2257
  ==== OIML
2216
2258
 
2217
2259
  OIML supports 9 identifier types with edition and supplement support:
@@ -0,0 +1,298 @@
1
+ # IEC legacy notation — old faces, modern counterparts, and the DAM family
2
+
3
+ **Status: RULED and LANDED (owner ruling 2026-11-12, "Yes, do this now").**
4
+ Sections 1–6 are the research record the ruling was made on; section 7
5
+ records the disposition as implemented. Companion to
6
+ `data/iec/stage-codes.md` (same precedent: provenance, sources, tables,
7
+ reconciliation with the pubid registry).
8
+
9
+ The owner's refined ruling holds that the base-less publisher-slash input
10
+ `IEC/FDAM 60038-1` is not garbage to reject: it is the **old face** of
11
+ `IEC 60038-1/FDAM 1`, the way old ISO printed parts with a slash and years
12
+ with a dash before the modern `-part` / `:year` convention settled. This
13
+ document collects the evidence for that mapping, the exact behaviour of the
14
+ current model, the gaps, and the disposition options.
15
+
16
+ ## Sources (all local; the IEC site is WAF-gated and yields no identifier-format examples)
17
+
18
+ | Source | What it evidences | Freshest state used |
19
+ |---|---|---|
20
+ | `pubid-iec` **1.15.21** installed gem (the v1 reference implementation; parslet grammar `lib/pubid/iec/parser.rb`) | the historical grammar's treatment of every face probed below | probed live 2026-11-12 |
21
+ | `relaton-data-iec` (33k records; local clone, main @ `04a8dc449` "update documents") | the real stored docidentifiers and the serialized shapes the consumer reads | 2026-11-12 |
22
+ | `relaton-data-iso` (local clone) | the modern stored docidentifiers of the joint (ISO/IEC) documents behind the owner-cited forms | 2026-11-12 |
23
+ | pubid `main` @ `be6a43c2` (2.0.21) — `grammars/iec.parg` edition-2 artifact, builder, `SupplementIdentifier#ensure_base` | the current decode of every face probed below | probed live 2026-11-12 |
24
+ | `spec/fixtures/iec/**` (full 12,534 inputs, pass/ and fail/ mined raw) | what the corpus pins | 2026-11-12 |
25
+ | `IEC-ISO Harmonized APIs` v0.0.8 (2023-06) / v0.0.7 (2021-05) PDFs | stage codes only (see `stage-codes.md`); **no identifier-format examples** | 2026-10-22 extract |
26
+
27
+ Historical background (not locally citable — needs the owner's citation if
28
+ this doc ships): IEC moved onto the 60000–69999 namespace in 1997 under the
29
+ ISO/IEC agreement; old `IEC 68` became `IEC 60068` (number += 60000, parts
30
+ unchanged).
31
+
32
+ ## 1. Old IEC numbering (the 60000-namespace move)
33
+
34
+ - **Fixtures:** `spec/fixtures/iec/**` carries **zero** old-number inputs
35
+ (`IEC 68`, `IEC 65`, `IEC 68-1` … appear nowhere in full/, pass/ or fail/).
36
+ The only sub-1000 numbers in the fixtures are joint ISO/IEC documents
37
+ (`ISO/IEC 9594-*`, `ISO/IEC 9075-*`, `ISO/IEC 9899`, `ISO/IEC 8825-*`),
38
+ which are ISO-side numbers and were never IEC-old numbers.
39
+ - **v1 gem:** old numbers parse and render round-trip: `IEC 68:1980` →
40
+ `IEC 68:1980`, `IEC 68-1:1988` → number `68`, part `1`.
41
+ - **pubid 2 (current):** identical — `IEC 68:1980` → number `68`,
42
+ `IEC 68-1:1988` → number `68` part `1`. Old numbers still parse; there is
43
+ no 60000-offset inference anywhere in either implementation.
44
+ - **relaton-data-iec:** the crawler stores the **modern** number even for
45
+ pre-1997 editions — `data/iec-60068-1-1960.yaml` is stored as
46
+ `IEC 60068-1:1960` (1960 edition, modern number). Exactly one short-number
47
+ file exists (`iec-112-2021.yaml`, a genuinely modern number). **No record
48
+ carries an old number; the old→new mapping is never encoded in the corpus** —
49
+ the crawler resolved designations to the modern namespace at crawl time.
50
+ - Parts: old `IEC 68-1` style == modern `IEC 60068-1` style; the part
51
+ separator was already `-` on both sides of 1997. No `IEC 68/1` slash-part
52
+ face exists in any local source.
53
+
54
+ ## 2. The old supplement faces and the DAM family
55
+
56
+ ### 2.1 The JTC1/ISO-style prefix face (the DAM family)
57
+
58
+ The letters **DAM / PDAM / FDAM / FPDAM** (Draft / Proposed Draft / Final
59
+ Draft / Final Proposed Draft Amendment) are JTC1 ballot faces: the type
60
+ token (with its amendment number) is printed **before** the base
61
+ designation.
62
+
63
+ Owner's rule under verification: **DAM/PDAM/FDAM/FPDAM without a number
64
+ means Amendment 1.**
65
+
66
+ Evidence:
67
+
68
+ - **relaton-data-iec, exhaustive scan:** across all 32,835 records the
69
+ letters PDAM/FPDAM/FDAM appear in **exactly one file**
70
+ (`iso-iec-21000-14-2007.yaml`) — a **joint** record, and only inside an
71
+ abstract that cites sibling drafts (`ISO/IEC 21000-4:2006/FPDAM 1`,
72
+ `ISO/IEC 21000-5:2004/FDAM 2`) — never in a docidentifier. Word-bounded
73
+ bare `DAM`: **zero occurrences**. **IEC-only records never use the DAM
74
+ letters.** The DAM family is JTC1/ISO ballot vocabulary that rides only on
75
+ joint (and pure-ISO) work; IEC's own catalogue never printed it.
76
+ - **"No number means Amendment 1":** consistent with the joint corpus —
77
+ `ISO/IEC PDAM 12207` names the ballot that became
78
+ `ISO/IEC 12207:1995/Amd 1:2002` (the first amendment; stored docidentifier
79
+ in relaton-data-iso). Every one of the 7,366 published supplement records
80
+ in relaton-data-iec (4,923 AMD + 2,306 COR + 137 ISH) carries its **own
81
+ number** (`number: '1'` …), so the published corpus never contradicts the
82
+ rule; the numberless face exists only at ballot time.
83
+ - **v1 gem:** the prefix face was never decoded — `IEC/FDAM 1 60038-1:2004`
84
+ raised, and the joint forms "parsed" only by the catch-all number rule
85
+ swallowing the tokens whole (`ISO/IEC FDAM 1 14496-12:2004` → number
86
+ `"FDAM 1 14496-12"`, type `:is`) — a mangle, not a decode.
87
+ - **pubid 2 (current):** the prefix face raises in both the iec and iso
88
+ flavors (see the coverage table).
89
+
90
+ ### 2.2 IEC's own historical amendment faces
91
+
92
+ The stored docidentifiers of real IEC amendments are uniformly the modern
93
+ on-base slash form, with the base designation whole and the supplement
94
+ numbered: `IEC 60038:1983/AMD1:1994`, `IEC 60038:2009/AMD1:2021`,
95
+ `CISPR 1:1961/AMD1:1967`, chained `ISO/IEC 9075-1:1999/Amd 1:2001/Cor 1:2003`
96
+ (relaton-data-iso), consolidated `+AMD1:1994+AMD2:1997` (file
97
+ `iec-60038-1983+amd1-1994+amd2-1997-csv.yaml`), titles "Amendment 1".
98
+ **No IEC-only record carries a numberless supplement** (checked across all
99
+ 7,366 supplement rows).
100
+
101
+ ### 2.3 The publisher-slash face `IEC/FDAM 60038-1`
102
+
103
+ - **v1 gem (1.15.21):** the face **did not parse at all** —
104
+ `IEC/FDAM 60038-1`, `IEC/DCOR 60038-1`, `IEC/DISH 60038-1`,
105
+ `IEC/CD 60038`, `IEC/FDIS 60038` all raised `ParseError`. v1's
106
+ `std_document_body` accepted `( "/" | space ) >> type` for **types**
107
+ (TS/TR/PAS/GUIDE: `IEC/TS 80000-1` parsed, `IEC CD 60038` parsed), but
108
+ FDAM/DCOR/DISH/CD are **stage** abbreviations, not types, so the slash
109
+ forms never reached a rule. The space-stage forms were mangled in v1
110
+ (`IEC FDAM 60038-1` → number `"FDAM 60038-1"`, type `:is`;
111
+ `IEC DISH 60038-1` → `/DISH60038`).
112
+ - **pubid 2 (current):** the `standalone_supplement` grammar rule accepts
113
+ `publisher / TYPE number` for **any** table token and `#ensure_base`
114
+ resolves it. See §3 for the resolution — which is the load-bearing finding.
115
+ - **relaton-data-iec:** **zero** docidentifiers of this shape (the pattern
116
+ `IEC/FDAM`-family or any `ORG/STAGE number` face: 0 of 32,835). The face
117
+ exists in no real record.
118
+
119
+ ### 2.4 The old dotted on-base spelling `/Amd.1`
120
+
121
+ `IEC 60068-1:1988/Amd.1:1992` raises in **both** v1 and pubid 2. The dotted
122
+ spelling (real on ISO covers, and inside IEC abstracts citing joint work,
123
+ e.g. `ISO/IEC 21000-5:2004/Amd.1:2007`) was never accepted by the IEC
124
+ flavor. ISO flavor accepts it; IEC does not. Recorded as a gap, not chased
125
+ here.
126
+
127
+ ## 3. How the current model resolves the old face (the load-bearing answer)
128
+
129
+ Probed on `main` @ `be6a43c2` (2.0.21):
130
+
131
+ | Input | base in to_hash | supplement own fields | to_s |
132
+ |---|---|---|---|
133
+ | `IEC/FDAM 60038-1` | `{"number" => "60038"}` | `{"stage"=>"fdamd", "number"=>"1"}` | `IEC FDAM 60038-1` |
134
+ | `IEC FDAM 60038-1` | `{"number" => "60038"}` | `{"stage"=>"fdamd", "number"=>"1"}` | `IEC FDAM 60038-1` |
135
+ | `IEC 60038-1/FDAM 1` (modern) | `{"number" => "60038", "part" => "1"}` | `{"stage"=>"fdamd", "number"=>"1"}` | `IEC 60038-1/FDAM1` |
136
+ | `IEC 60038-1/FDAM1:2021` | `{"number" => "60038", "part" => "1"}` | `stage fdamd, number 1, year 2021` | `IEC 60038-1/FDAM1:2021` |
137
+ | `IEC 60038:2009/AMD1:2014` | `{"number" => "60038", "year" => "2009"}` | `number 1, year 2014` | unchanged |
138
+
139
+ **The current model resolves the old form to "base 60038 with FDAM number
140
+ 1": the bound hash captures `supp_number_with_part = "60038-1"`, the
141
+ builder splits it into number `60038` + part `1` on the supplement, and
142
+ `SupplementIdentifier#ensure_base` moves `@number` onto the synthetic base
143
+ while `@part` becomes the supplement's own number.** The base loses its
144
+ part; the "1" migrates into the supplement slot.
145
+
146
+ **Under the owner's reading this is wrong.** The old face names **FDAM of
147
+ 60038-1**: the whole `60038-1` is the base designation, the type token is
148
+ unnumbered (→ Amendment 1 per the DAM rule), and the modern counterpart is
149
+ `IEC 60038-1/FDAM 1` — where the printed "1" after FDAM is the supplement
150
+ number the old face left implicit. The two faces must decode to the same
151
+ identifier, and today they do not (the space form of the old face and the
152
+ modern on-base form disagree on where the `1` lives — `{"number" => "60038"}`
153
+ vs `{"number" => "60038", "part" => "1"}`).
154
+
155
+ Note this correction touches the **space form too** (`IEC FDAM 60038-1`
156
+ would re-resolve to base 60038-1), and with it the #693-era equality work
157
+ and the six fixtures in `full/iec-pubid.txt` (rows 2195–2200) plus the
158
+ three normalization pairs the testsuite mirror carries (`FDAM/DCOR/DISH
159
+ 60038-1`). The render would move from `IEC FDAM 60038-1` to the modern
160
+ on-base face (`IEC 60038-1/FDAM…` spelling per the ruling) or keep the old
161
+ render as a facet — an owner call (see §5).
162
+
163
+ ## 4. Coverage table — the owner-cited forms (I own the ISO/IEC joint rows)
164
+
165
+ "Decode" = parses today under the current grammar, either flavor
166
+ (`Pubid::Iec.parse` / `Pubid::Iso.parse`; `ISO/IEC` routes to both via
167
+ `JOINT_PREFIXES`). Modern target = the stored docidentifier of the same
168
+ document in the joint corpus, where one exists.
169
+
170
+ | Input (historic face) | Decode today? | Historic identity (owner scheme) | Modern counterpart (evidence) |
171
+ |---|---|---|---|
172
+ | `ISO/IEC FDAM 1 14496-12:2004` | **no** (raises, both flavors) | FDAM **1** of base `14496-12:2004` | `ISO/IEC 14496-12:2005/Amd 1:2007` (corpus; the ballot predates the final edition year) |
173
+ | `ISO/IEC FDAM 19776-2:2005/Am1:2006` | **no** | FDAM (unnumbered → **1**) of base `19776-2:2005`, amendment already stamped `/Am1` on the base | `ISO/IEC 19776-2:2005/Amd 1:2008` (corpus) |
174
+ | `ISO/IEC FPDAM 10373-6/Amd 9:2001` | **no** | FPDAM (unnumbered → **9**, stated on-base) of base `10373-6` | `ISO/IEC 10373-6:2001/Amd N` family (corpus stores amd-1…7 of the 2001 edition) |
175
+ | `ISO/IEC FPDAM 9075/1` | **no** | FPDAM (unnumbered → **1**) of base `9075/1` — old slash-part spelling → base `9075-1` | `ISO/IEC 9075-1:1999/Amd 1:2001` (corpus) |
176
+ | `ISO/IEC 19772:2009/DAM 1:2019(E)` | **yes** (both flavors; on-base DAM) | DAM **1** of base `19772:2009` | none — the amendment died in ballot (corpus has only `Cor 1:2014`, then the 2020 edition) |
177
+ | `ISO/IEC PDAM 2 10646:2012` | **no** | PDAM **2** of base `10646:2012` | `ISO/IEC 10646:2012/Amd 1:2013` (corpus; Amd 2 followed) |
178
+ | `ISO/IEC PDAM TR 9573-11/Amd.1` | **no** | PDAM (unnumbered → **1**) of base `TR 9573-11`, amendment stamped on-base | TR amendment family (corpus: `iso-iec-tr-9573-11-*`) |
179
+ | `ISO/IEC PDAM 9541-1/Amd.4` | **no** | PDAM (unnumbered → **4**, stated on-base) of base `9541-1` | `ISO/IEC 9541-1:1991/Amd 4:2009` (corpus) |
180
+ | `ISO/IEC PDAM 12207` | **no** | PDAM (unnumbered → **1**) of base `12207` | `ISO/IEC 12207:1995/Amd 1:2002` (corpus) |
181
+
182
+ Pure-ISO rows of the owner's list (`ISO/FDAM 19119:2005/Amd.1`,
183
+ `ISO/PDAM 19110:2005/Amd.1`, `ISO 15877-5:2009 FDAM 1`, `ISO/PDAM 19973-2`)
184
+ belong to the ISO-side study; the DAM-letter evidence in §2.1 covers them
185
+ symmetrically (zero IEC-only usage).
186
+
187
+ Cross-cutting: the **on-base** DAM form already decodes (`19772` row) via
188
+ the same grammar branch as `/AMD1` — only the **prefix** (type-before-base)
189
+ faces and the old slash-part spelling are gaps.
190
+
191
+ ## 5. Gap list (current grammar/model vs the historic scheme)
192
+
193
+ 1. **Prefix supplement faces raise**: `[P|F]?DAM [N] BASE` — no rule in
194
+ `grammars/iec.parg` (joint or IEC-only; both flavors raise). Historic
195
+ face per the owner's scheme; zero published corpus occurrences
196
+ (ballot-time only), so the blast radius is inputs typed by humans, not
197
+ stored records.
198
+ 2. **The old publisher-slash face mis-resolves**: `IEC/FDAM 60038-1` /
199
+ `IEC FDAM 60038-1` put the base's part into the supplement's number
200
+ (§3). The modern and old faces disagree on the same document's identity.
201
+ 3. **Old slash-part spelling** (`9075/1`): never handled by either
202
+ implementation; the owner's ISO analogy places it in the same
203
+ historic-notation family.
204
+ 4. **Dotted on-base `/Amd.1`**: raises in the IEC flavor (both
205
+ implementations); ISO accepts it. Abstracts of joint records cite the
206
+ dotted spelling.
207
+ 5. **No old-number ↔ 60000-mapping knowledge** in either implementation:
208
+ old numbers parse as opaque numbers (`IEC 68` stays `68`); the corpus
209
+ never needs the mapping (crawler resolved at crawl time).
210
+
211
+ ## 6. Disposition options (two house precedents) and recommendation
212
+
213
+ **Option A — ISO `original_abbr` spelling-facet style** (root CLAUDE.md,
214
+ "Spelling is not identity"): parse the historic face, store the modern
215
+ identity, keep the input's spelling as an `equality_ignored` facet so
216
+ `to_s` can reproduce it. Fits when both spellings live in the wild and
217
+ round-tripping the input spelling matters. Cost: a facet on
218
+ `TypedStage`/supplement components plus render plumbing in both flavors.
219
+
220
+ **Option B — IHO legacy-decode-to-modern style** (the IHO edition ruling):
221
+ decode the legacy face straight to the modern canonical identity, no
222
+ spelling kept. Fits when the legacy face is a ballot/era artefact that the
223
+ modern catalogue never stores. Cost: grammar decode + the identity
224
+ correction of §3; render emits the modern face.
225
+
226
+ **Option C — drop**: keep raising on the prefix faces and leave the
227
+ publisher-slash face as #693 left it. Contradicts the refined ruling.
228
+
229
+ **Recommendation: B, with one owner call.** The DAM-family faces and the
230
+ old slash-part spelling are ballot-era artefacts with **zero** published
231
+ corpus occurrences (measured above), so a spelling facet (A) buys nothing
232
+ for matching; decode-to-modern gives one identity per document. The decode
233
+ rule: `[P|F]?DAM [N] BASE` → supplement (P|F)DAM, number `N` defaulting to
234
+ **1** (the owner's rule), base designation kept **whole** (parts and
235
+ slash-parts resolved per the modern separator convention). The owner call:
236
+ whether the already-accepted faces (`IEC/FDAM 60038-1`, the on-base
237
+ `/AMD1`, the dotted `/Amd.1`) also move to the corrected base-whole
238
+ resolution — §3 shows the space form shares the defect, so the correction
239
+ cannot be scoped to the slash form alone. This doc records the evidence
240
+ for that decision; no implementation is proposed until the owner rules.
241
+
242
+ ## 7. Disposition as implemented (the owner's ruling)
243
+
244
+ **Option B — IHO-style decode-to-modern — on ALL the already-accepted
245
+ faces too** (the §6 owner call): `IEC/FDAM 60038-1` and `IEC FDAM 60038-1`
246
+ both decode to the modern identity; the space form shares the defect, so
247
+ the correction is not scoped to the slash form alone.
248
+
249
+ The decode rule, as landed:
250
+
251
+ - **Base kept whole.** The designation after the stage token is the BASE's:
252
+ number, part, subpart and the date (old notation prints the base's year;
253
+ a draft supplement carries no publication date of its own). The base is a
254
+ concrete InternationalStandard, so the legacy face hash-matches — and
255
+ equals — the modern on-base parse: `parse("IEC/DCOR 60038-1") ==
256
+ parse("IEC 60038-1/DCOR 1")`.
257
+ - **Unnumbered = 1.** The supplement's number is the captured N, defaulting
258
+ to 1 (the DAM-family no-number rule, extended to the DCOR/DISH faces the
259
+ fixtures carry).
260
+ - **Draft-ness preserved.** Each face keeps its own stage: FDAM → `fdamd`
261
+ (50.xx), DAM → `damd` (40.xx), DCOR → `dcor`, DISH → `dish`, CDISH →
262
+ `cd`, FDCOR → `fdcor`; the joint ladder's PDAM (30.xx, CD level) and
263
+ FPDAM (50.xx, FDIS level) registered with the circulation slots 30.20 /
264
+ 50.20 leading their harmonized lists (CDAM and FDAM own the
265
+ (type, leading-code) URN keys 30.00 / 50.00).
266
+ - **Slash-part.** `FPDAM 9075/1` — the `/1` is the base's part (base
267
+ 9075 part 1), consistent with the ISO-side reading of the old
268
+ slash-part spelling.
269
+ - **Modern canonical face emitted.** `IEC/FDAM 60038-1` → `IEC 60038-1/FDAM1`;
270
+ the legacy face is decoded, not preserved as a spelling facet.
271
+ - **Stays untouched:** the modern on-base forms — `IEC 60038:1983/AMD1:1994`,
272
+ `IEC 60038-1/FDAM 1`, `ISO/IEC 19772:2009/DAM 1:2019(E)` — and the true
273
+ standalone self-base (`IEC/AMD 1:2017` → `IEC Amd 1-1:2017`).
274
+
275
+ Split of labour: the DAM-family prefix faces needed a new grammar rule
276
+ (`legacy_supplement`, pubid-grammar) — they raised before; the DCOR/DISH
277
+ (and CDISH/FDCOR/CDCor) faces already parsed through the standalone and
278
+ typed branches and were corrected in the builder
279
+ (`SupplementIdentifier#ensure_base`: the part-to-number move deleted, the
280
+ designation moved whole onto a concrete base, the supplement numbered 1).
281
+ A bare number after a DAM-family token is a base designation under the
282
+ JTC1 reading (`IEC FDAM 60038` → `IEC 60038/FDAM1`); a bare ordinal after
283
+ a non-DAM token stays the supplement's own number.
284
+
285
+ Replay gate (all 26,838 full-fixture inputs, main baseline vs the branch):
286
+ byte-identical everywhere except the six §3 rows (rows 2195–2200 of
287
+ `full/iec-pubid.txt`), which re-resolve; 0 inputs stopped parsing; the
288
+ prefix faces go raise→parse (pass 12,337 → 12,359 of 12,491; fail steady
289
+ at 132). The testsuite mirror normalization pairs
290
+ (FDAM/DCOR/DISH `60038-1`) flip to the corrected identities in the same
291
+ round.
292
+
293
+ Still raising, deliberately (gap 4 of §5, not chased here): the dotted
294
+ on-base `/Amd.1` spelling, and the stamped prefix faces whose base
295
+ designation carries an amendment stamp (`FPDAM 10373-6/Amd 9:2001`,
296
+ `PDAM 9541-1/Amd.4`, `FDAM 19776-2:2005/Am1:2006`, `PDAM TR
297
+ 9573-11/Amd.1`) — they need the dotted/ISO stamp spellings the IEC flavor
298
+ does not carry; the ISO-side study owns that family.
@@ -0,0 +1,15 @@
1
+ # IHO S-4 reserved specification numbers ("NOT CURRENTLY USED" in the
2
+ # publication's contents), keyed by the S-4 edition whose contents list
3
+ # them, then by the citation's Part letter. A reserved number parses and
4
+ # resolves AS RESERVED, never invalid (issue #691): the identifier is
5
+ # well-formed, the number just names no specification in that edition.
6
+ #
7
+ # Provenance:
8
+ # - Ed 4.10.0: the owner's list in pubid/pubid#691.
9
+ # - Ed 4.9.0 (March 2021): verified against the publication's Part B
10
+ # contents (reference-docs/iho/S-4_Ed4.9.0_March_2021.pdf) — the same
11
+ # four numbers print "NOT CURRENTLY USED".
12
+ "4.10.0":
13
+ B: ["170", "230", "305", "323"]
14
+ "4.9.0":
15
+ B: ["170", "230", "305", "323"]