sts 0.5.6 → 0.6.0

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rake.yml +3 -0
  3. data/.github/workflows/release.yml +11 -5
  4. data/.rubocop.yml +24 -9
  5. data/.rubocop_todo.yml +92 -4
  6. data/TODO.sts-refactor/00-overview.md +24 -4
  7. data/TODO.sts-refactor/03-namespace-coupling.md +108 -147
  8. data/lib/sts/iso_sts/alt_text.rb +23 -0
  9. data/lib/sts/iso_sts/app.rb +1 -1
  10. data/lib/sts/iso_sts/array.rb +1 -1
  11. data/lib/sts/iso_sts/disp_formula.rb +1 -1
  12. data/lib/sts/iso_sts/doc_number.rb +16 -0
  13. data/lib/sts/iso_sts/doc_type.rb +16 -0
  14. data/lib/sts/iso_sts/document_identification.rb +4 -4
  15. data/lib/sts/iso_sts/fpage.rb +23 -0
  16. data/lib/sts/iso_sts/graphic.rb +2 -2
  17. data/lib/sts/iso_sts/ics.rb +16 -0
  18. data/lib/sts/iso_sts/is_proof.rb +13 -0
  19. data/lib/sts/iso_sts/iso_meta.rb +3 -3
  20. data/lib/sts/iso_sts/issue.rb +23 -0
  21. data/lib/sts/iso_sts/long_desc.rb +23 -0
  22. data/lib/sts/iso_sts/lpage.rb +21 -0
  23. data/lib/sts/iso_sts/mixed_citation.rb +7 -7
  24. data/lib/sts/iso_sts/nat_meta.rb +3 -3
  25. data/lib/sts/iso_sts/originator.rb +16 -0
  26. data/lib/sts/iso_sts/page_range.rb +21 -0
  27. data/lib/sts/iso_sts/part_number.rb +16 -0
  28. data/lib/sts/iso_sts/proj_id.rb +16 -0
  29. data/lib/sts/iso_sts/pub_date.rb +17 -0
  30. data/lib/sts/iso_sts/pub_id.rb +19 -0
  31. data/lib/sts/iso_sts/reg_meta.rb +5 -5
  32. data/lib/sts/iso_sts/release_version.rb +16 -0
  33. data/lib/sts/{tbx_iso_tml/mi.rb → iso_sts/release_version_id.rb} +5 -7
  34. data/lib/sts/iso_sts/sdo.rb +16 -0
  35. data/lib/sts/iso_sts/secretariat.rb +3 -4
  36. data/lib/sts/iso_sts/standard.rb +1 -0
  37. data/lib/sts/iso_sts/standard_identification.rb +8 -8
  38. data/lib/sts/iso_sts/std_ref.rb +7 -7
  39. data/lib/sts/iso_sts/styled_content.rb +0 -2
  40. data/lib/sts/iso_sts/suppl_number.rb +16 -0
  41. data/lib/sts/iso_sts/suppl_type.rb +16 -0
  42. data/lib/sts/iso_sts/suppl_version.rb +16 -0
  43. data/lib/sts/iso_sts/table_wrap_foot.rb +1 -1
  44. data/lib/sts/iso_sts/tex_math.rb +23 -0
  45. data/lib/sts/iso_sts/urn.rb +16 -0
  46. data/lib/sts/iso_sts/version.rb +16 -0
  47. data/lib/sts/iso_sts/volume.rb +23 -0
  48. data/lib/sts/iso_sts/wi_number.rb +19 -0
  49. data/lib/sts/iso_sts/year.rb +21 -0
  50. data/lib/sts/iso_sts.rb +27 -0
  51. data/lib/sts/niso_sts/display_formula.rb +1 -3
  52. data/lib/sts/niso_sts/inline_formula.rb +1 -3
  53. data/lib/sts/niso_sts/standard.rb +1 -0
  54. data/lib/sts/tbx_iso_tml/definition.rb +1 -1
  55. data/lib/sts/tbx_iso_tml/note.rb +1 -1
  56. data/lib/sts/tbx_iso_tml/td.rb +1 -1
  57. data/lib/sts/tbx_iso_tml/term.rb +1 -1
  58. data/lib/sts/tbx_iso_tml/th.rb +1 -1
  59. data/lib/sts/tbx_iso_tml.rb +0 -12
  60. data/lib/sts/version.rb +1 -1
  61. data/lib/sts.rb +1 -1
  62. data/sts.gemspec +2 -2
  63. metadata +32 -18
  64. data/lib/sts/mathml.rb +0 -21
  65. data/lib/sts/tbx_iso_tml/math.rb +0 -49
  66. data/lib/sts/tbx_iso_tml/mfenced.rb +0 -40
  67. data/lib/sts/tbx_iso_tml/mfrac.rb +0 -30
  68. data/lib/sts/tbx_iso_tml/mrow.rb +0 -41
  69. data/lib/sts/tbx_iso_tml/mspace.rb +0 -18
  70. data/lib/sts/tbx_iso_tml/mstyle.rb +0 -29
  71. data/lib/sts/tbx_iso_tml/msub.rb +0 -31
  72. data/lib/sts/tbx_iso_tml/msup.rb +0 -25
  73. data/lib/sts/tbx_iso_tml/munder.rb +0 -19
  74. data/lib/sts/tbx_iso_tml/munderover.rb +0 -23
  75. data/lib/sts/tbx_iso_tml/semantics.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6978d54e78a3b856fabb8c79a810c03b1b7ac57326453dc695b212405ef5bb50
4
- data.tar.gz: 3f9e168d8fe68849e4002c1c1b912e11fc0d2d83b0f9920487e3983f455b63dc
3
+ metadata.gz: 13e2f7f08de69885f2e625b3e46d6d459116fe58692c735a5a003113e7a80a77
4
+ data.tar.gz: eb1de3ce704b4d955a89bce414ceea801ebd6d8c51ecb3c3bc73ec44b88eb6f2
5
5
  SHA512:
6
- metadata.gz: 0cb8492ca499c979631a349444635829b128cecffaf81a8805ac0ea6e4b0608d798d0b260af40470708497f680ae7b0a1eea8d90024e0180bf5a3810b51d98d4
7
- data.tar.gz: 2b1838bdbde38dfc2cef5ea6218e4c48b6e04e99fd169d45c9d6bfe34929239d07b9b62df3bc834921605fa2103a90c29da281225b6e2a52d4c58f7cc45be113
6
+ metadata.gz: 530f4e261194c92e8543cf8eb242e851be5aede2d365ff8ef7bef98f21d0f9a25e4d464632ae108b61bdbaa43d5bb77c583c8c1ec7e14bc355f78016690cc01f
7
+ data.tar.gz: 231aebbc44901782184e018394071dbfc98db1245960b5161158a301b32ab6640a6c1190b1f23a133059848e7dc0e977ac625a47e1ad6198520aa88caff7441b
@@ -8,6 +8,9 @@ on:
8
8
  tags: [ v* ]
9
9
  pull_request:
10
10
 
11
+ permissions:
12
+ contents: write
13
+
11
14
  jobs:
12
15
  rake:
13
16
  uses: metanorma/ci/.github/workflows/generic-rake.yml@main
@@ -2,11 +2,6 @@
2
2
  # See https://github.com/metanorma/cimas
3
3
  name: release
4
4
 
5
- permissions:
6
- contents: write
7
- packages: write
8
- id-token: write
9
-
10
5
  on:
11
6
  workflow_dispatch:
12
7
  inputs:
@@ -24,6 +19,17 @@ jobs:
24
19
  uses: metanorma/ci/.github/workflows/rubygems-release.yml@main
25
20
  with:
26
21
  next_version: ${{ github.event.inputs.next_version }}
22
+ # Explicit release_command needed since metanorma/ci#314 deprecated
23
+ # `bundler_cache` (hardcoded to false) — the reusable's release job no
24
+ # longer runs `bundle install` implicitly via ruby/setup-ruby, so the
25
+ # default `bundle exec rake release` fires against uninstalled gems.
26
+ # Surfaced by kwkwan on metanorma-plugin-lutaml#285. Deeper fix
27
+ # (bundle install inside rubygems-release.yml's release job) tracked
28
+ # separately.
29
+ release_command: |
30
+ bundle install
31
+ bundle exec rake release
27
32
  secrets:
28
33
  rubygems-api-key: ${{ secrets.METANORMA_CI_RUBYGEMS_API_KEY }}
29
34
  pat_token: ${{ secrets.METANORMA_CI_PAT_TOKEN }}
35
+
data/.rubocop.yml CHANGED
@@ -1,16 +1,31 @@
1
+ # Auto-generated by Cimas: Do not edit it manually!
2
+ # See https://github.com/metanorma/cimas
1
3
  inherit_from:
4
+ - https://raw.githubusercontent.com/riboseinc/oss-guides/main/ci/rubocop.yml
5
+ # .rubocop_todo.yml MUST be the last entry. inherit_from is last-wins:
6
+ # if listed before oss-guides, the shared config's stricter Metrics/
7
+ # (MethodLength, BlockLength, etc.) rules override the todo's per-file
8
+ # grandfathering, and the todo becomes inert on those cops. Empirically
9
+ # verified on suma 2026-07-06: with todo listed first, 34 Metrics/* offenses
10
+ # remained; moved last, cleared. Every gem in the metanorma-org fleet
11
+ # already ships a `.rubocop_todo.yml` on its live tree (audited 2026-07-06,
12
+ # 54/54 have it), so this reference is safe to emit unconditionally.
2
13
  - .rubocop_todo.yml
3
- - https://raw.githubusercontent.com/riboseinc/oss-guides/master/ci/rubocop.yml
14
+
15
+ # Rubocop plugins enabled centrally so every metanorma-org gem picks them up
16
+ # on cimas sync — best practice belongs at the shared-template layer, not
17
+ # per-repo. Per ronaldtse feedback on metanorma/ci#332.
18
+ plugins:
19
+ - rubocop-rspec
20
+ - rubocop-performance
21
+ - rubocop-rake
4
22
 
5
23
  # local repo-specific modifications
6
24
  # ...
7
25
 
8
- Metrics/BlockLength:
9
- Max: 35
10
-
11
26
  AllCops:
12
- TargetRubyVersion: 2.6
13
- NewCops: enable
14
- Exclude:
15
- - 'lib/sts/mapper.rb'
16
- - 'vendor/**/*'
27
+ # 3.3 matches the org-wide minimum being pushed via #274 (Raise minimum
28
+ # Ruby version to 3.3 due to EOL of 3.2 on 2026-03-31). Was 3.4 before
29
+ # this commit — 3.4 was above the org's stated minimum and would have
30
+ # applied Rubocop rules that fail-close on gems still targeting 3.3.
31
+ TargetRubyVersion: 3.3
data/.rubocop_todo.yml CHANGED
@@ -1,11 +1,17 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2026-05-12 03:56:52 UTC using RuboCop version 1.86.1.
3
+ # on 2026-07-15 03:29:27 UTC using RuboCop version 1.86.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
+ # Offense count: 4
10
+ # This cop supports safe autocorrection (--autocorrect).
11
+ Layout/EmptyLines:
12
+ Exclude:
13
+ - 'spec/sts_spec.rb'
14
+
9
15
  # Offense count: 14
10
16
  # This cop supports safe autocorrection (--autocorrect).
11
17
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, AllowRBSInlineAnnotation, AllowCopDirectives, AllowedPatterns, SplitStrings.
@@ -21,16 +27,98 @@ Layout/LineLength:
21
27
  - 'spec/round_trip/reference_docs_spec.rb'
22
28
  - 'spec/spec_helper.rb'
23
29
 
30
+ # Offense count: 1
31
+ # This cop supports unsafe autocorrection (--autocorrect-all).
32
+ Lint/RedundantDirGlobSort:
33
+ Exclude:
34
+ - 'spec/spec_helper.rb'
35
+
24
36
  # Offense count: 1
25
37
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
26
38
  Metrics/AbcSize:
27
39
  Exclude:
28
40
  - 'lib/sts/iso_sts/mathml2/math.rb'
29
41
 
42
+ # Offense count: 10
43
+ # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
44
+ # AllowedMethods: refine
45
+ Metrics/BlockLength:
46
+ Max: 35
47
+
30
48
  # Offense count: 1
31
49
  # This cop supports safe autocorrection (--autocorrect).
32
- # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
33
- # SupportedStyles: single_quotes, double_quotes
34
- Style/StringLiterals:
50
+ # Configuration parameters: EnforcedStyle.
51
+ # SupportedStyles: be, be_nil
52
+ RSpec/BeNil:
35
53
  Exclude:
54
+ - 'spec/sts_spec.rb'
55
+
56
+ # Offense count: 9
57
+ # Configuration parameters: IgnoredMetadata.
58
+ RSpec/DescribeClass:
59
+ Exclude:
60
+ - '**/spec/features/**/*'
61
+ - '**/spec/requests/**/*'
62
+ - '**/spec/routing/**/*'
63
+ - '**/spec/system/**/*'
64
+ - '**/spec/views/**/*'
65
+ - 'spec/anti_patterns_spec.rb'
66
+ - 'spec/elements/expanded_models_spec.rb'
67
+ - 'spec/elements/index_toc_spec.rb'
68
+ - 'spec/elements/missing_attributes_spec.rb'
69
+ - 'spec/elements/modified_elements_spec.rb'
36
70
  - 'spec/elements/new_elements_spec.rb'
71
+ - 'spec/elements/reference_models_spec.rb'
72
+ - 'spec/elements/structural_models_spec.rb'
73
+ - 'spec/round_trip/reference_docs_spec.rb'
74
+
75
+ # Offense count: 1
76
+ # This cop supports safe autocorrection (--autocorrect).
77
+ RSpec/EmptyLineAfterFinalLet:
78
+ Exclude:
79
+ - 'spec/sts_spec.rb'
80
+
81
+ # Offense count: 62
82
+ # Configuration parameters: CountAsOne.
83
+ RSpec/ExampleLength:
84
+ Max: 40
85
+
86
+ # Offense count: 5
87
+ RSpec/LeakyLocalVariable:
88
+ Exclude:
89
+ - 'spec/anti_patterns_spec.rb'
90
+ - 'spec/round_trip/reference_docs_spec.rb'
91
+
92
+ # Offense count: 4
93
+ # This cop supports safe autocorrection (--autocorrect).
94
+ RSpec/LetBeforeExamples:
95
+ Exclude:
96
+ - 'spec/sts_spec.rb'
97
+
98
+ # Offense count: 1
99
+ RSpec/MultipleDescribes:
100
+ Exclude:
101
+ - 'spec/profiles/iso_iec_validator_spec.rb'
102
+
103
+ # Offense count: 67
104
+ RSpec/MultipleExpectations:
105
+ Max: 14
106
+
107
+ # Offense count: 4
108
+ # Configuration parameters: AllowedPatterns.
109
+ # AllowedPatterns: ^expect_, ^assert_
110
+ RSpec/NoExpectationExample:
111
+ Exclude:
112
+ - 'spec/iso_sts/iso_sts_spec.rb'
113
+ - 'spec/sts_spec.rb'
114
+
115
+ # Offense count: 2
116
+ # Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata, InflectorPath, EnforcedInflector.
117
+ # SupportedInflectors: default, active_support
118
+ RSpec/SpecFilePathFormat:
119
+ Exclude:
120
+ - '**/spec/routing/**/*'
121
+ - 'spec/sts_element_spec.rb'
122
+ - 'spec/sts_table_spec.rb'
123
+ - 'spec/iso_sts/iso_sts_spec.rb'
124
+ - 'spec/iso_sts/iso_sts_element_spec.rb'
@@ -19,7 +19,7 @@
19
19
  |---|------|----------|----------|--------------|
20
20
  | 01 | `01-mathml-delegation.md` | Anti-Pattern Fix | DONE | — |
21
21
  | 02 | `02-type-resolution.md` | Anti-Pattern Fix | DONE | — |
22
- | 03 | `03-namespace-coupling.md` | Architecture | IN PROGRESS | 01, 02 |
22
+ | 03 | `03-namespace-coupling.md` | Architecture | IN PROGRESS (63→20 refs) | 01, 02 |
23
23
  | 04 | `04-register-versioning.md` | Architecture | HIGH | 01, 02 |
24
24
  | 05 | `05-missing-elements.md` | Feature Gap | MOSTLY DONE | 04 |
25
25
  | 06 | `06-missing-attributes.md` | Feature Gap | DONE | 04 |
@@ -140,7 +140,9 @@ grep -r "method_missing|respond_to_missing|Object.const_get|\.send" lib/
140
140
  ```
141
141
 
142
142
  ### IsoSts Namespace Independence (2026-05-07)
143
- - ARCHITECTURE DECISION: IsoSts and NisoSts must remain fully independent
143
+ - ARCHITECTURE DECISION: IsoSts must be independent **of NisoSts**. This is not
144
+ independence in general — `lib/sts/iso_sts/` still references `TbxIsoTml` and
145
+ MathML types directly, and those shared namespaces are outside this ADR.
144
146
  - ISOSTS is frozen legacy; NISO STS evolves — combining them violates OCP
145
147
  - Created 11 new IsoSts-specific types: Monospace, Sc, Strike, Underline, Uri,
146
148
  NamedContent, StandardRef, MetaDate, ContentLanguage (with autoloads)
@@ -164,9 +166,27 @@ grep -r "method_missing|respond_to_missing|Object.const_get|\.send" lib/
164
166
  - ~~Expand StringName usage (in contrib, element-citation, related-article — NISO STS 1.2)~~ → DONE: added to name-alternatives; already in person-group and mixed-citation
165
167
 
166
168
  ### Architectural Items (High Effort)
167
- - `03-namespace-coupling.md` — IN PROGRESS: 15763 IsoSts→NisoSts cross-references remaining
169
+ - `03-namespace-coupling.md` — IN PROGRESS: 6320 IsoSts→NisoSts references
170
+ remaining (issue #40). Source of truth is `ISOSTS.xsd`, NOT the NisoSts
171
+ models — they disagree on nearly every element.
168
172
  - `04-register-versioning.md` — Version the models via lutaml-model Registers
169
173
  - `11-duplicate-models.md` — 44 overlapping element resolution (depends on 03)
170
174
 
175
+ ### Known bugs — schema conformance (separate from 03)
176
+ The 2026-05-07 "@id added to all models (XSD-verified)" pass verified against
177
+ the **NISO** XSD and applied the result to IsoSts. ISOSTS disagrees:
178
+ - **16 IsoSts models silently drop ISOSTS attributes that exist** — real data
179
+ loss. `sub`/`sup` lose `arrange`+`specific-use`; `ext-link` loses 5 xlink
180
+ attrs; `mixed-citation` loses 6; `graphic`, `copyright-*`, `edition`,
181
+ `title`, `label`, `uri`, `named-content`, `underline`, `meta-date`, `body`
182
+ lose others.
183
+ - **25 IsoSts models carry an `@id` ISOSTS does not define** — harmless (never
184
+ populated on parse, never emitted on serialise), but dead surface.
185
+
171
186
  ## Next Action
172
- Proceed with `03-namespace-coupling.md` — audit and resolve 157+ IsoSts→NisoSts cross-references.
187
+ Two independent tracks:
188
+ 1. Finish `03` — the 20 remaining refs split into 11 deep-root refs (each
189
+ reaching a ~152-class recursive core) and 9 child-bearing refs whose closure
190
+ is unmeasured. Both need sizing before planning.
191
+ 2. File and fix the 16 lossy models above — real data loss, unrelated to
192
+ decoupling.
@@ -3,156 +3,117 @@
3
3
  **Priority**: HIGH
4
4
  **Category**: Architecture
5
5
  **Estimated Effort**: High
6
- **Files Affected**: `lib/sts/iso_sts/content_groups/highlight_elements.rb` (194+ references)
6
+ **Status**: Partially done — 63 → 20 references (GitHub issue #40)
7
7
 
8
8
  ## Problem
9
9
 
10
- `lib/sts/iso_sts/content_groups/highlight_elements.rb` contains 194+ references to `Sts::NisoSts::*` types:
11
-
12
- ```ruby
13
- # Examples from highlight_elements.rb
14
- [:sub, "Sts::NisoSts::Sub", "sub"]
15
- [:sup, "Sts::NisoSts::Sup", "sup"]
16
- [:italic, "Sts::NisoSts::Italic", "italic"]
17
- [:bold, "Sts::NisoSts::Bold", "bold"]
18
- # ... 190 more
19
- ```
20
-
21
- ### Why This Is Wrong
22
-
23
- 1. **Violation of namespace isolation**: IsoSts should not depend on NisoSts implementation
24
- 2. **Shared-namespace ambiguity**: These elements appear in both ISOSTS and NISO STS but with different attribute sets
25
- 3. **Tight coupling**: Changes to NisoSts can break IsoSts
26
- 4. **Schema divergence ignored**: `<p>` in ISOSTS has different attributes than `<p>` in NISO STS Extended
27
-
28
- ### Root Cause
29
-
30
- The schemas ARE different:
31
- - ISOSTS `<p>`: `@content-type, @id, @specific-use, @xml:lang`
32
- - NISO STS Extended `<p>`: `@content-type, @id, @originator, @specific-use, @style-type, @xml:base, @xml:lang`
33
-
34
- ## Solution: Three-Tier Architecture
35
-
36
- ### Tier 1: Shared Base Types
37
-
38
- Create shared types in `Sts::Base` that both namespaces inherit:
39
-
40
- ```ruby
41
- # lib/sts/base/text/emphasis.rb
42
- module Sts
43
- module Base
44
- module Text
45
- class Emphasis < Lutaml::Model::Serializable
46
- # Shared attributes/methods
47
- attribute :id, :string
48
- attribute :base_fontsize, :string
49
- attribute :baseline_shift, :string
50
- attribute :color, :string
51
-
52
- xml do
53
- map_attribute :id, to: :id
54
- # ...
55
- end
56
- end
57
-
58
- class Bold < Emphasis
59
- xml { element "bold" }
60
- end
61
-
62
- class Italic < Emphasis
63
- xml { element "italic" }
64
- end
65
-
66
- class Sub < Emphasis
67
- xml { element "sub" }
68
- end
69
-
70
- class Sup < Emphasis
71
- xml { element "sup" }
72
- end
73
- end
74
- end
75
- end
76
- ```
77
-
78
- ### Tier 2: IsoSts-Specific Extensions
79
-
80
- ```ruby
81
- # lib/sts/iso_sts/text/emphasis.rb
82
- module Sts
83
- module IsoSts
84
- module Text
85
- class Bold < ::Sts::Base::Text::Bold
86
- # Add IsoSts-specific attributes
87
- attribute :content_type, :string
88
-
89
- xml do
90
- # Override/add IsoSts-specific mapping
91
- map_attribute "content-type", to: :content_type
92
- end
93
- end
94
- end
95
- end
96
- end
97
- ```
98
-
99
- ### Tier 3: NisoSts-Specific Extensions
100
-
101
- ```ruby
102
- # lib/sts/niso_sts/text/emphasis.rb
103
- module Sts
104
- module NisoSts
105
- module Text
106
- class Bold < ::Sts::Base::Text::Bold
107
- # Add NisoSts-specific attributes
108
- attribute :originator, :string
109
- attribute :style_type, :string
110
-
111
- xml do
112
- map_attribute "originator", to: :originator
113
- map_attribute "style-type", to: :style_type
114
- end
115
- end
116
- end
117
- end
118
- end
119
- ```
120
-
121
- ## Implementation Strategy
122
-
123
- 1. **Phase 1**: Identify all shared element types (bold, italic, sub, sup, p, etc.)
124
- 2. **Phase 2**: Create base types with MINIMAL shared attributes
125
- 3. **Phase 3**: Refactor IsoSts to extend bases
126
- 4. **Phase 4**: Refactor NisoSts to extend bases
127
- 5. **Phase 5**: Delete `Sts::NisoSts::*` references from IsoSts content groups
128
-
129
- ## Files to Refactor
130
-
131
- Primary targets:
132
- - `lib/sts/iso_sts/content_groups/highlight_elements.rb`
133
- - `lib/sts/iso_sts/content_groups/emphasis_elements.rb` (if exists)
134
- - `lib/sts/niso_sts/content_groups/highlight_elements.rb`
10
+ `lib/sts/iso_sts/` references `Sts::NisoSts::*` types directly. IsoSts must be
11
+ independent of NisoSts (ADR 2026-05-07): ISOSTS is frozen legacy, NISO STS
12
+ evolves, so coupling them violates OCP.
13
+
14
+ PR #31 reduced this from 157 to 63 references. Issue #40 reduced it further,
15
+ from 63 to 20.
16
+
17
+ ## Source of truth: ISOSTS.xsd, NOT the NisoSts models
18
+
19
+ The obvious approach — duplicate each NisoSts class into IsoSts — produces
20
+ **schema-incorrect models**. The NisoSts models disagree with
21
+ `reference-docs/isosts-v1/xsd/ISOSTS.xsd` on nearly every element:
22
+
23
+ | Element | ISOSTS says | NisoSts model |
24
+ |---|---|---|
25
+ | `year` (:948) | `content-type`, `specific-use`, `xml:lang` no `@id` | `@id` only |
26
+ | `doc-type` (:6378) | `type="xs:string"` no attributes at all | `@id` + content |
27
+ | `ics` (:6373) | `type="xs:string"` | `@id` + `ics-desc` child |
28
+ | `fpage` | `content-type`, `seq`, `specific-use`, `xml:lang`; no children | `@id` + bold/italic |
29
+ | `license` (:51) | `license-type`, `specific-use`, `xml:lang` | `@id`, `xlink:href` |
30
+ | `ruby` | **not an ISOSTS element** | exists (NISO only) |
31
+
32
+ `feature_doc.xml` declares `<!DOCTYPE standard SYSTEM ".../ISOSTS.dtd">`, and
33
+ the ISOSTS DTD agrees with ISOSTS.xsd (both derive `bold`, `sub` etc. from the
34
+ JATS 0.4 modules). ISOSTS.xsd is a faithful conversion and is the authority.
35
+
36
+ **Round-tripping does not prove correctness.** A model that invents or drops an
37
+ attribute still parses and serialises symmetrically, so the suite stays green
38
+ while the model is wrong. Assert the exact attribute set per element instead.
39
+
40
+ Attribute lists must be **generated** from the XSD, never hand-read: `version`
41
+ on `tex-math` and `specific-use` on `pub-id` sit after long `xs:enumeration`
42
+ blocks and are invisible to a truncated read.
43
+
44
+ ## Done in issue #40 — 43 refs removed, 27 classes added
45
+
46
+ - **14 `xs:string` elements** (`originator`, `doc-type`, `doc-number`,
47
+ `part-number`, `version`, `suppl-type`, `suppl-number`, `suppl-version`,
48
+ `urn`, `sdo`, `proj-id`, `release-version`, `ics`, `secretariat`) — modelled
49
+ as content-only IsoSts classes with no attributes. 23 refs.
50
+ - **3 `permissions` refs** repointed to the existing `IsoSts::Permissions`.
51
+ - **`ruby` deleted** from `StyledContent` — ISOSTS defines no such element.
52
+ Behaviour change: `<ruby>` in `<styled-content>` no longer round-trips.
53
+ - **13 element classes** modelled from ISOSTS.xsd: `Year`, `PubDate`,
54
+ `ReleaseVersionId`, `IsProof`, `AltText`, `LongDesc`, `TexMath`, `PubId`,
55
+ `Volume`, `Issue`, `Fpage`, `Lpage`, `PageRange`. 16 refs.
56
+ - **`WiNumber` added** — ISOSTS gives `wi-number` an `@id`; it was typed as a
57
+ plain string, silently discarding that `@id`.
58
+
59
+ ### Why classes and not plain `:string`
60
+
61
+ `xs:string` elements look like they need no class. They do: an empty element
62
+ (`<sdo/>`, which `feature_doc.xml` contains) does not survive a `:string`
63
+ round-trip. For a scalar, `render_empty: :empty` recovers it; for a collection
64
+ (`secretariat`, `ics` — both `maxOccurs="unbounded"`) an empty element parses
65
+ to `[]`, destroying the information at parse time before any render option
66
+ applies. Content-only classes round-trip every case.
67
+
68
+ ## Remaining — 20 refs
69
+
70
+ **11 refs to 9 deep roots**: `MetadataStd`, `ElementCitation`, `PersonGroup`,
71
+ `Collab`, `Source`, `DispQuote`, `EditingInstruction`, `TermDisplay`,
72
+ `BoxedText`. Each reaches the same ~152-class mutually-recursive core
73
+ (`Section` → `Paragraph` → `DispQuote` → `Paragraph`). `DispQuote` has 3 direct
74
+ children but pulls all 152. No incremental path — needs its own decision.
75
+
76
+ **9 refs whose ISOSTS content models have real children**, closure not measured:
77
+ `attrib` (:1082), `term-head` (:4308), `article-title`, `license` → `license-p`
78
+ (:51), `publisher` `publisher-loc`, `custom-meta-group` → `custom-meta`.
79
+ `attrib` and `term-head` are **not** trivial leaves in ISOSTS despite what the
80
+ NisoSts models suggest; `license-p` reaches `array`/`alternatives`, so these may
81
+ not be cheap. Measure before planning.
82
+
83
+ ## Rejected: three-tier `Sts::Base` hierarchy
84
+
85
+ An earlier draft of this document proposed shared base types in `Sts::Base` that
86
+ both namespaces inherit. This contradicts the 2026-05-07 ADR, and the schemas
87
+ genuinely diverge — `IsoSts::Fig` has `title`/`alternatives` that
88
+ `NisoSts::Figure` lacks; `IsoSts::Ref` has `nlm-citation`/`citation-alternatives`
89
+ that `NisoSts::Reference` lacks. A shared base would fight the schemas.
90
+
91
+ That draft also targeted `lib/sts/iso_sts/content_groups/highlight_elements.rb`
92
+ and "194+ references" to emphasis types. That file was deleted as dead code; the
93
+ real work was metadata types, not highlight elements.
94
+
95
+ ## Naming trap (for the deferred work)
96
+
97
+ A mechanical `s/NisoSts::/IsoSts::/` is wrong. Nine elements already have
98
+ different class names per namespace, so a blind rename creates `IsoSts::Section`
99
+ alongside the existing `IsoSts::Sec` — two classes for one `sec` element:
100
+
101
+ `Section`/`Sec`, `Figure`/`Fig`, `Reference`/`Ref`, `ReferenceList`/`RefList`,
102
+ `SectionArray`/`Array`, `StdCrossReference`/`StandardCrossReference`,
103
+ `DisplayFormula`/`DispFormula`, `TermSection`/`TermSec`,
104
+ `ReferenceStandard`/`Std`.
105
+
106
+ Remap child references by **XML element name**, not class name.
107
+
108
+ ## Scope boundary
109
+
110
+ Independence from NisoSts is not independence in general: `lib/sts/iso_sts/`
111
+ still references 6 `TbxIsoTml` types directly, and much of the deferred closure
112
+ reaches `TbxIsoTml`/MathML. Those are shared namespaces, outside this ADR.
135
113
 
136
114
  ## Verification
137
115
 
138
- 1. No `Sts::NisoSts` references in `lib/sts/iso_sts/`
139
- 2. All round-trip tests pass
140
- 3. Schema validation against both ISOSTS DTD and NISO STS XSD passes
141
-
142
- ## Dependencies
143
-
144
- - `01-mathml-delegation.md` — uses similar patterns
145
- - `02-type-resolution.md` — must be clean first
146
-
147
- ## TODO Checklist
148
-
149
- - [ ] Audit all IsoSts → NisoSts references
150
- - [ ] Categorize by shared vs. truly namespace-specific
151
- - [ ] Design base type hierarchy
152
- - [ ] Create `lib/sts/base/` directory structure
153
- - [ ] Implement base types for shared elements
154
- - [ ] Refactor IsoSts to use base types
155
- - [ ] Refactor NisoSts to use base types
156
- - [ ] Remove cross-namespace references
157
- - [ ] Verify no circular dependencies
158
- - [ ] Run full test suite
116
+ 1. `grep -rho "Sts::NisoSts::" lib/sts/iso_sts/ | wc -l` → `20`
117
+ 2. Every attribute on an IsoSts model traces to a line in `ISOSTS.xsd`
118
+ 3. Autoload registry 1:1 with the directory (112/112)
119
+ 4. `bundle exec rspec` green; `bundle exec rubocop` clean
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sts
4
+ module IsoSts
5
+ class AltText < Lutaml::Model::Serializable
6
+ attribute :content, :string
7
+ attribute :id, :string
8
+ attribute :content_type, :string
9
+ attribute :specific_use, :string
10
+ attribute :xml_lang, :string
11
+
12
+ xml do
13
+ element "alt-text"
14
+
15
+ map_content to: :content
16
+ map_attribute "id", to: :id
17
+ map_attribute "content-type", to: :content_type
18
+ map_attribute "specific-use", to: :specific_use
19
+ map_attribute "xml:lang", to: :xml_lang
20
+ end
21
+ end
22
+ end
23
+ end
@@ -15,7 +15,7 @@ module Sts
15
15
  attribute :term_sec, ::Sts::IsoSts::TermSec, collection: true
16
16
  attribute :fn_group, ::Sts::IsoSts::FnGroup, collection: true
17
17
  attribute :ref_list, ::Sts::IsoSts::RefList, collection: true
18
- attribute :permissions, ::Sts::NisoSts::Permissions
18
+ attribute :permissions, ::Sts::IsoSts::Permissions
19
19
  attribute :non_normative_note, ::Sts::IsoSts::NonNormativeNote,
20
20
  collection: true
21
21
  attribute :non_normative_example, ::Sts::IsoSts::NonNormativeExample,
@@ -12,7 +12,7 @@ module Sts
12
12
  attribute :label, ::Sts::IsoSts::Label
13
13
  attribute :table, ::Sts::IsoSts::Table
14
14
  attribute :attrib, ::Sts::NisoSts::Attrib, collection: true
15
- attribute :permissions, ::Sts::NisoSts::Permissions, collection: true
15
+ attribute :permissions, ::Sts::IsoSts::Permissions, collection: true
16
16
 
17
17
  xml do
18
18
  element "array"
@@ -17,7 +17,7 @@ module Sts
17
17
  attribute :styled_content, ::Sts::IsoSts::StyledContent
18
18
  attribute :preformat, ::Sts::IsoSts::Preformat
19
19
  attribute :graphic, ::Sts::IsoSts::Graphic, collection: true
20
- attribute :tex_math, ::Sts::NisoSts::TexMath
20
+ attribute :tex_math, ::Sts::IsoSts::TexMath
21
21
 
22
22
  xml do
23
23
  element "disp-formula"
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sts
4
+ module IsoSts
5
+ # ISOSTS declares <doc-number> as type="xs:string" -- no attributes.
6
+ class DocNumber < Lutaml::Model::Serializable
7
+ attribute :content, :string
8
+
9
+ xml do
10
+ element "doc-number"
11
+
12
+ map_content to: :content
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Sts
4
+ module IsoSts
5
+ # ISOSTS declares <doc-type> as type="xs:string" -- no attributes.
6
+ class DocType < Lutaml::Model::Serializable
7
+ attribute :content, :string
8
+
9
+ xml do
10
+ element "doc-type"
11
+
12
+ map_content to: :content
13
+ end
14
+ end
15
+ end
16
+ end
@@ -4,11 +4,11 @@ module Sts
4
4
  module IsoSts
5
5
  class DocumentIdentification < Lutaml::Model::Serializable
6
6
  attribute :id, :string
7
- attribute :sdo, ::Sts::NisoSts::Sdo
8
- attribute :proj_id, ::Sts::NisoSts::ProjId
7
+ attribute :sdo, ::Sts::IsoSts::Sdo
8
+ attribute :proj_id, ::Sts::IsoSts::ProjId
9
9
  attribute :language, ::Sts::IsoSts::Language
10
- attribute :release_version, ::Sts::NisoSts::ReleaseVersion
11
- attribute :urn, ::Sts::NisoSts::Urn
10
+ attribute :release_version, ::Sts::IsoSts::ReleaseVersion
11
+ attribute :urn, ::Sts::IsoSts::Urn
12
12
 
13
13
  xml do
14
14
  element "doc-ident"