svg_conform 0.2.4 → 0.2.7

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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/rake.yml +4 -1
  4. data/Gemfile +6 -3
  5. data/Gemfile.lock +211 -0
  6. data/TODO.issue-100/00-overview.md +135 -0
  7. data/TODO.issue-100/01-build-lens-core-v1.md +92 -0
  8. data/TODO.issue-100/02-adopt-lens-core-in-svg.md +123 -0
  9. data/TODO.issue-100/03-ship-svg-1-1-full-static.md +138 -0
  10. data/TODO.issue-100/04-ship-svg-2-secure-static.md +131 -0
  11. data/TODO.issue-100/05-freeze-candidate-and-handoff.md +108 -0
  12. data/TODO.issue-100/90-progress.md +65 -0
  13. data/config/profiles/metanorma.yml +4 -4
  14. data/config/profiles/svg_1_1_full_static.yml +69 -0
  15. data/config/profiles/svg_2_secure_static.yml +55 -0
  16. data/config/requirements/processing_features_static.yml +29 -0
  17. data/data/svg_1_1/manifest.yml +7 -0
  18. data/data/svg_1_1/svg11-flat-20110816.dtd +5664 -0
  19. data/docs/api_reference.adoc +19 -1
  20. data/docs/cli_guide.adoc +35 -45
  21. data/docs/diagnostics.adoc +73 -0
  22. data/docs/lens_core_handoff.adoc +84 -0
  23. data/docs/profiles.adoc +94 -1
  24. data/lib/svg_conform/cli.rb +3 -4
  25. data/lib/svg_conform/commands/check.rb +11 -60
  26. data/lib/svg_conform/conformance_report.rb +79 -1
  27. data/lib/svg_conform/diagnostics/lens_core_adapter.rb +200 -0
  28. data/lib/svg_conform/document.rb +12 -1
  29. data/lib/svg_conform/external_checkers/svgcheck/parser.rb +1 -3
  30. data/lib/svg_conform/node_helpers.rb +1 -1
  31. data/lib/svg_conform/profile.rb +11 -2
  32. data/lib/svg_conform/quality_metrics/complexity_calculator.rb +3 -3
  33. data/lib/svg_conform/quality_metrics/configuration.rb +4 -2
  34. data/lib/svg_conform/references/base_reference.rb +153 -0
  35. data/lib/svg_conform/references/reference_manifest.rb +1 -2
  36. data/lib/svg_conform/report_comparator.rb +4 -4
  37. data/lib/svg_conform/requirements/base_requirement.rb +6 -0
  38. data/lib/svg_conform/requirements/external_references_requirement.rb +234 -0
  39. data/lib/svg_conform/requirements/link_validation_requirement.rb +2 -0
  40. data/lib/svg_conform/requirements/processing_features_requirement.rb +175 -0
  41. data/lib/svg_conform/requirements/svg_1_1_document_requirement.rb +213 -0
  42. data/lib/svg_conform/requirements.rb +6 -0
  43. data/lib/svg_conform/sax_document.rb +8 -4
  44. data/lib/svg_conform/sax_validation_handler.rb +22 -1
  45. data/lib/svg_conform/validation/css2_core_grammar.rb +359 -0
  46. data/lib/svg_conform/validation/css_references.rb +122 -0
  47. data/lib/svg_conform/validation/error_tracker.rb +40 -9
  48. data/lib/svg_conform/validation/svg_1_1_document_conformance.rb +154 -0
  49. data/lib/svg_conform/validation_context.rb +25 -5
  50. data/lib/svg_conform/validation_result.rb +91 -16
  51. data/lib/svg_conform/version.rb +1 -1
  52. data/lib/svg_conform/yaml_io.rb +20 -0
  53. data/lib/svg_conform.rb +25 -0
  54. data/spec/fixtures/diagnostics/external-reference-info.svg +6 -0
  55. data/spec/fixtures/plantuml/README.adoc +11 -0
  56. data/spec/fixtures/plantuml/unit-type.svg +1 -0
  57. data/spec/fixtures/profiles/svg_1_1_full_static/invalid/animation.svg +4 -0
  58. data/spec/fixtures/profiles/svg_1_1_full_static/invalid/css-grammar.svg +4 -0
  59. data/spec/fixtures/profiles/svg_1_1_full_static/invalid/dtd-attribute.svg +4 -0
  60. data/spec/fixtures/profiles/svg_1_1_full_static/invalid/dtd-content-model.svg +4 -0
  61. data/spec/fixtures/profiles/svg_1_1_full_static/invalid/interaction.svg +4 -0
  62. data/spec/fixtures/profiles/svg_1_1_full_static/invalid/malformed-xml.svg +4 -0
  63. data/spec/fixtures/profiles/svg_1_1_full_static/invalid/script.svg +5 -0
  64. data/spec/fixtures/profiles/svg_1_1_full_static/invalid/wrong-namespace.svg +4 -0
  65. data/spec/fixtures/profiles/svg_1_1_full_static/invalid/wrong-version.svg +4 -0
  66. data/spec/fixtures/profiles/svg_1_1_full_static/invalid/xml-stylesheet.svg +5 -0
  67. data/spec/fixtures/profiles/svg_1_1_full_static/valid/css.svg +5 -0
  68. data/spec/fixtures/profiles/svg_1_1_full_static/valid/foreign-metadata.svg +6 -0
  69. data/spec/fixtures/profiles/svg_1_1_full_static/valid/minimal-full.svg +5 -0
  70. data/spec/fixtures/profiles/svg_1_1_full_static/valid/xml-stylesheet.svg +5 -0
  71. data/spec/fixtures/profiles/svg_2_secure_static/invalid/animation.svg +4 -0
  72. data/spec/fixtures/profiles/svg_2_secure_static/invalid/css-import.svg +4 -0
  73. data/spec/fixtures/profiles/svg_2_secure_static/invalid/css-url.svg +4 -0
  74. data/spec/fixtures/profiles/svg_2_secure_static/invalid/data-bad-png.svg +4 -0
  75. data/spec/fixtures/profiles/svg_2_secure_static/invalid/data-empty-png.svg +4 -0
  76. data/spec/fixtures/profiles/svg_2_secure_static/invalid/data-malformed-base64.svg +4 -0
  77. data/spec/fixtures/profiles/svg_2_secure_static/invalid/data-malformed-percent.svg +4 -0
  78. data/spec/fixtures/profiles/svg_2_secure_static/invalid/data-unsupported-type.svg +4 -0
  79. data/spec/fixtures/profiles/svg_2_secure_static/invalid/file-like-fragment.svg +4 -0
  80. data/spec/fixtures/profiles/svg_2_secure_static/invalid/http.svg +4 -0
  81. data/spec/fixtures/profiles/svg_2_secure_static/invalid/https.svg +4 -0
  82. data/spec/fixtures/profiles/svg_2_secure_static/invalid/interaction.svg +4 -0
  83. data/spec/fixtures/profiles/svg_2_secure_static/invalid/protocol-relative.svg +4 -0
  84. data/spec/fixtures/profiles/svg_2_secure_static/invalid/relative-file-fragment.svg +4 -0
  85. data/spec/fixtures/profiles/svg_2_secure_static/invalid/relative-file.svg +4 -0
  86. data/spec/fixtures/profiles/svg_2_secure_static/invalid/script.svg +4 -0
  87. data/spec/fixtures/profiles/svg_2_secure_static/invalid/svg-data-excessive-nesting.svg +4 -0
  88. data/spec/fixtures/profiles/svg_2_secure_static/invalid/svg-data-malformed-xml.svg +4 -0
  89. data/spec/fixtures/profiles/svg_2_secure_static/invalid/svg-data-nested-external.svg +4 -0
  90. data/spec/fixtures/profiles/svg_2_secure_static/invalid/urn.svg +4 -0
  91. data/spec/fixtures/profiles/svg_2_secure_static/invalid/xml-stylesheet.svg +5 -0
  92. data/spec/fixtures/profiles/svg_2_secure_static/valid/jpeg-base64.svg +4 -0
  93. data/spec/fixtures/profiles/svg_2_secure_static/valid/jpeg-percent.svg +4 -0
  94. data/spec/fixtures/profiles/svg_2_secure_static/valid/png-base64.svg +4 -0
  95. data/spec/fixtures/profiles/svg_2_secure_static/valid/png-percent.svg +4 -0
  96. data/spec/fixtures/profiles/svg_2_secure_static/valid/same-document-fragment.svg +4 -0
  97. data/spec/fixtures/profiles/svg_2_secure_static/valid/svg-base64.svg +4 -0
  98. data/spec/fixtures/profiles/svg_2_secure_static/valid/svg-percent.svg +4 -0
  99. data/spec/fixtures/quality_metrics/aliases.yml +7 -0
  100. data/spec/svg_conform/commands/check_command_spec.rb +22 -2
  101. data/spec/svg_conform/commands/profiles_command_spec.rb +18 -0
  102. data/spec/svg_conform/conformance_report_spec.rb +90 -0
  103. data/spec/svg_conform/diagnostics/lens_core_adapter_spec.rb +136 -0
  104. data/spec/svg_conform/gemspec_spec.rb +34 -0
  105. data/spec/svg_conform/profiles/metanorma_profile_spec.rb +48 -0
  106. data/spec/svg_conform/profiles/svg_1_1_full_static_profile_spec.rb +146 -0
  107. data/spec/svg_conform/profiles/svg_2_secure_static_profile_spec.rb +150 -0
  108. data/spec/svg_conform/profiles_plantuml_spec.rb +81 -0
  109. data/spec/svg_conform/quality_metrics/configuration_spec.rb +82 -0
  110. data/spec/svg_conform/references/data_uri_reference_spec.rb +97 -0
  111. data/spec/svg_conform/references/integration_spec.rb +30 -0
  112. data/spec/svg_conform/references/reference_classifier_spec.rb +17 -0
  113. data/spec/svg_conform/requirements/external_references_requirement_spec.rb +109 -0
  114. data/spec/svg_conform/requirements/link_validation_requirement_spec.rb +16 -0
  115. data/spec/svg_conform/requirements/processing_features_requirement_spec.rb +75 -0
  116. data/spec/svg_conform/requirements/svg_1_1_document_requirement_spec.rb +141 -0
  117. data/spec/svg_conform/requirements/text_as_path_requirement_spec.rb +9 -5
  118. data/spec/svg_conform/validation/css2_core_grammar_spec.rb +116 -0
  119. data/spec/svg_conform/validation/svg_1_1_document_conformance_spec.rb +144 -0
  120. data/spec/svg_conform/validation_context_source_plumbing_spec.rb +122 -0
  121. data/spec/svg_conform/validation_context_spec.rb +39 -0
  122. data/spec/svg_conform/validation_result_spec.rb +123 -0
  123. data/spec/svg_conform/yaml_io_spec.rb +41 -0
  124. data/svg_conform.gemspec +4 -2
  125. metadata +120 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f1d7de294c1bcf83f963e80f3505faa12d65d90a45ec86decb82ee828e79b1e2
4
- data.tar.gz: 932524562cb5d1b9a78387725ab659dd21c5f2cf964ebbbec291202f5d87c876
3
+ metadata.gz: c570e54c0025259102584f034e789bcd18b4487d2954b858f8941c1095a7034a
4
+ data.tar.gz: 96aae95705ee4769216dd82a1b694d471f0dd348ae15228282f925006f66e558
5
5
  SHA512:
6
- metadata.gz: 85956fa210da5c6e1282baaeddc02c44450413a9d5b5dfebdfd73d9f7534ae83ce5b0858790db2e1f1d77498dac944ba21f1f868d97fbc202cea0eb017980c36
7
- data.tar.gz: 4bcddffa2c56f0ccafaf708e99bea046787c949adb0a07d58581667e421c68abcd49e8014b58e2f225078d5ea83a5d12920c8ddedc4cd6a3daedb23920a6a502
6
+ metadata.gz: 6314a0c0a4bd5f2cdf3a04e95852173382ccb33c9f4a2683cc38809a1a0afed53ea1febb809a12803e417769f82fed9a538ea7ee3e110f50b37bb7814ab0b541
7
+ data.tar.gz: 9f3f51c1f050447a6d84506e59fc52d3efaec2616deea94eb6e596c8255969a843bd69a8feebecb318308211937a81b85d079aa43a9dc9db9dd0edd045536a52
data/.gitattributes ADDED
@@ -0,0 +1 @@
1
+ data/svg_1_1/* -text
@@ -14,5 +14,8 @@ on:
14
14
  jobs:
15
15
  rake:
16
16
  uses: metanorma/ci/.github/workflows/generic-rake.yml@main
17
+ with:
18
+ # tests-passed dispatch is refused on pull_request runs; releases come from push
19
+ cascade: ${{ github.event_name != 'pull_request' }}
17
20
  secrets:
18
- pat_token: ${{ secrets.GITHUB_TOKEN }}
21
+ pat_token: ${{ secrets.CLARICLE_CI_PAT_TOKEN }}
data/Gemfile CHANGED
@@ -6,9 +6,12 @@ source "https://rubygems.org"
6
6
  gemspec
7
7
 
8
8
  gem "canon", "~>0.1.10"
9
- gem "lutaml-model", github: "lutaml/lutaml-model", branch: "main"
10
- gem "moxml", github: "lutaml/moxml", branch: "main"
11
- gem "leptris", ">= 1.9.365.1"
9
+ gem "leptris", ">= 1.9.399.0"
10
+ gem "lutaml-model", "~> 0.8.155" # rubygems line; drop the git-main float
11
+ gem "moxml", "~> 0.6.0"
12
+ # lens_core is optional at runtime (#121); development and CI use the released v1 gem.
13
+ gem "lens_core", "~> 0.1"
14
+ gem "nokogiri", "~> 1.16"
12
15
  gem "openssl", "~> 3.0"
13
16
  gem "rake"
14
17
  gem "rspec"
data/Gemfile.lock ADDED
@@ -0,0 +1,211 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ svg_conform (0.2.6)
5
+ lutaml-model (~> 0.8.155)
6
+ moxml (~> 0.6.0)
7
+ nokogiri (~> 1.16)
8
+ rainbow
9
+ table_tennis
10
+ thor
11
+ yeptris (~> 0.6.36)
12
+
13
+ GEM
14
+ remote: https://rubygems.org/
15
+ specs:
16
+ ast (2.4.3)
17
+ base64 (0.3.0)
18
+ bigdecimal (4.1.3)
19
+ canon (0.1.23)
20
+ diff-lcs
21
+ json
22
+ moxml
23
+ nokogiri
24
+ paint
25
+ table_tennis
26
+ thor
27
+ unicode-name
28
+ concurrent-ruby (1.3.8)
29
+ csv (3.3.7)
30
+ diff-lcs (1.6.2)
31
+ ffi (1.17.4-aarch64-linux-gnu)
32
+ ffi (1.17.4-aarch64-linux-musl)
33
+ ffi (1.17.4-arm-linux-gnu)
34
+ ffi (1.17.4-arm-linux-musl)
35
+ ffi (1.17.4-arm64-darwin)
36
+ ffi (1.17.4-x86_64-darwin)
37
+ ffi (1.17.4-x86_64-linux-gnu)
38
+ ffi (1.17.4-x86_64-linux-musl)
39
+ json (3.1.0)
40
+ language_server-protocol (3.17.0.6)
41
+ lens_core (0.1.0)
42
+ leptris (1.9.416.0-aarch64-linux)
43
+ ffi (~> 1.15)
44
+ leptris (1.9.416.0-aarch64-linux-musl)
45
+ ffi (~> 1.15)
46
+ leptris (1.9.416.0-arm-linux)
47
+ ffi (~> 1.15)
48
+ leptris (1.9.416.0-arm-linux-musl)
49
+ ffi (~> 1.15)
50
+ leptris (1.9.416.0-arm64-darwin)
51
+ ffi (~> 1.15)
52
+ leptris (1.9.416.0-x86_64-darwin)
53
+ ffi (~> 1.15)
54
+ leptris (1.9.416.0-x86_64-linux)
55
+ ffi (~> 1.15)
56
+ leptris (1.9.416.0-x86_64-linux-musl)
57
+ ffi (~> 1.15)
58
+ lint_roller (1.1.0)
59
+ liquid (5.14.0)
60
+ bigdecimal
61
+ strscan (>= 3.1.1)
62
+ lutaml-model (0.8.174)
63
+ base64 (>= 0.1.1)
64
+ bigdecimal
65
+ canon
66
+ concurrent-ruby
67
+ liquid (>= 4.0, < 6.0)
68
+ moxml (~> 0.6.1)
69
+ ostruct
70
+ rubyzip (~> 3.7)
71
+ thor (~> 1.4)
72
+ memo_wise (1.13.0)
73
+ moxml (0.6.4)
74
+ nokogiri (1.19.7-aarch64-linux-gnu)
75
+ racc (~> 1.4)
76
+ nokogiri (1.19.7-aarch64-linux-musl)
77
+ racc (~> 1.4)
78
+ nokogiri (1.19.7-arm-linux-gnu)
79
+ racc (~> 1.4)
80
+ nokogiri (1.19.7-arm-linux-musl)
81
+ racc (~> 1.4)
82
+ nokogiri (1.19.7-arm64-darwin)
83
+ racc (~> 1.4)
84
+ nokogiri (1.19.7-x86_64-darwin)
85
+ racc (~> 1.4)
86
+ nokogiri (1.19.7-x86_64-linux-gnu)
87
+ racc (~> 1.4)
88
+ nokogiri (1.19.7-x86_64-linux-musl)
89
+ racc (~> 1.4)
90
+ openssl (3.3.3)
91
+ ostruct (0.6.3)
92
+ paint (2.3.0)
93
+ parallel (2.3.0)
94
+ parser (3.3.13.0)
95
+ ast (~> 2.4.1)
96
+ racc
97
+ prism (2.1.0-aarch64-linux-gnu)
98
+ prism (2.1.0-aarch64-linux-musl)
99
+ prism (2.1.0-arm-linux-gnu)
100
+ prism (2.1.0-arm-linux-musl)
101
+ prism (2.1.0-arm64-darwin)
102
+ prism (2.1.0-x86_64-darwin)
103
+ prism (2.1.0-x86_64-linux-gnu)
104
+ prism (2.1.0-x86_64-linux-musl)
105
+ racc (1.8.1)
106
+ rainbow (3.1.1)
107
+ rake (13.4.2)
108
+ regexp_parser (2.13.1)
109
+ rspec (3.13.2)
110
+ rspec-core (~> 3.13.0)
111
+ rspec-expectations (~> 3.13.0)
112
+ rspec-mocks (~> 3.13.0)
113
+ rspec-core (3.13.7)
114
+ rspec-support (~> 3.13.0)
115
+ rspec-expectations (3.13.6)
116
+ diff-lcs (>= 1.2.0, < 2.0)
117
+ rspec-support (~> 3.13.0)
118
+ rspec-mocks (3.13.9)
119
+ diff-lcs (>= 1.2.0, < 2.0)
120
+ rspec-support (~> 3.13.0)
121
+ rspec-support (3.13.8)
122
+ rubocop (1.92.2)
123
+ json (>= 2.3)
124
+ language_server-protocol (~> 3.17.0.2)
125
+ lint_roller (~> 1.1.0)
126
+ parallel (>= 1.10)
127
+ parser (>= 3.3.0.2)
128
+ rainbow (>= 2.2.2, < 4.0)
129
+ regexp_parser (>= 2.11.3, < 3.0)
130
+ rubocop-ast (>= 1.49.0, < 2.0)
131
+ ruby-progressbar (~> 1.7)
132
+ unicode-display_width (>= 2.4.0, < 4.0)
133
+ rubocop-ast (1.51.0)
134
+ parser (>= 3.3.7.2)
135
+ prism (>= 1.7)
136
+ rubocop-performance (1.27.0)
137
+ lint_roller (~> 1.1)
138
+ rubocop (>= 1.89.0, < 2.0)
139
+ rubocop-ast (>= 1.47.1, < 2.0)
140
+ rubocop-rake (0.7.1)
141
+ lint_roller (~> 1.1)
142
+ rubocop (>= 1.72.1)
143
+ rubocop-rspec (3.10.2)
144
+ lint_roller (~> 1.1)
145
+ regexp_parser (>= 2.0)
146
+ rubocop (~> 1.86, >= 1.86.2)
147
+ ruby-progressbar (1.13.0)
148
+ rubyzip (3.7.0)
149
+ strscan (3.1.8)
150
+ table_tennis (1.0.0)
151
+ csv (~> 3.3)
152
+ ffi (~> 1.17)
153
+ memo_wise (~> 1.13)
154
+ paint (~> 2.3)
155
+ unicode-display_width (~> 3.2)
156
+ thor (1.5.0)
157
+ unicode-display_width (3.3.0)
158
+ unicode-emoji (~> 4.3)
159
+ unicode-emoji (4.3.0)
160
+ unicode-name (1.15.1)
161
+ unicode-types (~> 1.12)
162
+ unicode-types (1.12.0)
163
+ yeptris (0.6.61.1-aarch64-linux)
164
+ ffi (~> 1.15)
165
+ yeptris (0.6.61.1-aarch64-linux-musl)
166
+ ffi (~> 1.15)
167
+ yeptris (0.6.61.1-arm-linux-eabihf)
168
+ ffi (~> 1.15)
169
+ yeptris (0.6.61.1-arm-linux-musleabihf)
170
+ ffi (~> 1.15)
171
+ yeptris (0.6.61.1-arm64-darwin)
172
+ ffi (~> 1.15)
173
+ yeptris (0.6.61.1-x86_64-darwin)
174
+ ffi (~> 1.15)
175
+ yeptris (0.6.61.1-x86_64-linux)
176
+ ffi (~> 1.15)
177
+ yeptris (0.6.61.1-x86_64-linux-musl)
178
+ ffi (~> 1.15)
179
+
180
+ PLATFORMS
181
+ aarch64-linux
182
+ aarch64-linux-gnu
183
+ aarch64-linux-musl
184
+ arm-linux
185
+ arm-linux-eabihf
186
+ arm-linux-gnu
187
+ arm-linux-musl
188
+ arm-linux-musleabihf
189
+ arm64-darwin
190
+ x86_64-darwin
191
+ x86_64-linux-gnu
192
+ x86_64-linux-musl
193
+
194
+ DEPENDENCIES
195
+ canon (~> 0.1.10)
196
+ lens_core (~> 0.1)
197
+ leptris (>= 1.9.399.0)
198
+ lutaml-model (~> 0.8.155)
199
+ moxml (~> 0.6.0)
200
+ nokogiri (~> 1.16)
201
+ openssl (~> 3.0)
202
+ rake
203
+ rspec
204
+ rubocop
205
+ rubocop-performance
206
+ rubocop-rake
207
+ rubocop-rspec
208
+ svg_conform!
209
+
210
+ BUNDLED WITH
211
+ 2.7.2
@@ -0,0 +1,135 @@
1
+ # Issue 100 — Spec-version profiles and shared lens diagnostics
2
+
3
+ ## Outcome
4
+
5
+ Ship a versioned, zero-runtime-dependency `LensCore` diagnostics contract, make
6
+ `svg_conform` emit that contract without breaking its transitional interfaces,
7
+ and add two opt-in, corpus-backed profiles:
8
+
9
+ - SVG 1.1 Full in static processing mode.
10
+ - SVG 2 in secure static processing mode.
11
+
12
+ The work ends with a pinned LensCore candidate and an observable adoption
13
+ handoff. Publishing a LensCore gem and migrating other lens repositories are
14
+ follow-on work, not part of this issue.
15
+
16
+ ## Current state
17
+
18
+ - `svg_conform` defines diagnostics in several places. `ValidationContext` and
19
+ `ErrorTracker` collect `infos`, but `ValidationResult`,
20
+ `ConformanceReport`, and the `check` command project only errors and warnings.
21
+ - `ValidationResult#to_h` and `#to_text`, Lutaml-backed
22
+ `ConformanceReport`, CLI JSON/YAML/table output, and the svgcheck compatibility
23
+ path do not share one canonical report object.
24
+ - Profiles are Lutaml models loaded from `config/profiles/*.yml`; requirements
25
+ are registered independently in `lib/svg_conform/profile.rb`,
26
+ `lib/svg_conform/profile_compiler.rb`, and
27
+ `lib/svg_conform/requirements.rb`.
28
+ - Validation supports DOM and SAX requirement hooks, with SAX as the normal
29
+ validation path. The current profiles describe project policies, not complete
30
+ SVG 1.1 Full or SVG 2 secure-static conformance.
31
+ - `SvgConform::References::ReferenceClassifier.classify` already returns typed
32
+ internal-fragment, data-URI, URN, external-URL, and relative-path references.
33
+ `LinkValidationRequirement` consumes that API. Data URIs currently expose
34
+ their media type but are not decoded or syntax-checked.
35
+ - The repository has no committed `Gemfile.lock` and no LensCore dependency.
36
+
37
+ ## Target design
38
+
39
+ ### One diagnostics contract
40
+
41
+ The new `claricle/lens_core` repository owns schema version `1`, Ruby value
42
+ objects, JSON/YAML parsers, and JSON/YAML/human renderers. A report carries its
43
+ schema version, producer, profile identity, validity, summary counts, and an
44
+ ordered diagnostics collection. A diagnostic has a stable severity, extensible
45
+ kind, stable requirement ID, message, optional citations/remediation/context,
46
+ and an optional typed location that can represent XML coordinates or binary
47
+ offsets/records.
48
+
49
+ `svg_conform` constructs that report once. Existing hashes, text/table output,
50
+ `ConformanceReport`, and svgcheck-compatible output become projections or
51
+ adapters over it for one transition. The adapter preserves all four existing
52
+ channels: errors, validity errors, warnings, and infos.
53
+
54
+ ### Profiles as explicit requirement matrices
55
+
56
+ Each profile has stable requirement IDs, normative citations, expected failure
57
+ behavior, DOM/SAX coverage, focused fixtures, and documentation. SVG 1.1 Full
58
+ uses the pinned 2011 Recommendation and its flat DTD, plus the Recommendation's
59
+ Appendix G.2 handling and referenced CSS/XML-stylesheet rules. SVG 2 uses the
60
+ pinned Candidate Recommendation and enforces secure-static processing,
61
+ including its external-resource boundary.
62
+
63
+ The profiles are additive and opt-in. Existing profile behavior and the default
64
+ profile do not change.
65
+
66
+ ## Work sequence
67
+
68
+ | Card | Work item | Depends on | Effort |
69
+ | --- | --- | --- | --- |
70
+ | 01 | Build LensCore diagnostics v1 | — | 4–7 person-days |
71
+ | 02 | Adopt LensCore in svg_conform | 01 | 3–5 person-days |
72
+ | 03 | Ship SVG 1.1 Full/static | 02 | 8–12 person-days |
73
+ | 04 | Ship SVG 2 secure-static | 02, 03 shared processing/source plumbing | 4–6 person-days |
74
+ | 05 | Freeze the candidate and adoption handoff | 01–04 | 2–4 person-days |
75
+
76
+ Overall estimate: **21–34 person-days**. The range includes implementation,
77
+ focused tests, fixtures, documentation, and integration fixes. It excludes gem
78
+ publication, work in other lens repositories, and external review latency.
79
+ Confidence is medium: the largest uncertainty is whether the existing XML stack
80
+ can perform offline SVG 1.1 DTD validation without another runtime dependency;
81
+ Card 03 resolves that with a bounded spike before implementation proceeds.
82
+
83
+ ## Acceptance map
84
+
85
+ | Issue acceptance criterion | Planned evidence |
86
+ | --- | --- |
87
+ | SVG 1.1 and SVG 2 profiles ship, are documented, and each has a corpus | Cards 03 and 04 profile specs, focused fixture directories, CLI checks, and profile documentation |
88
+ | The diagnostics model is extracted, versioned, and shaped for reuse by other lenses | Cards 01 and 02 schema/model/renderer specs plus XML and binary location examples |
89
+ | A diagnostics JSON round-trip corpus is pinned | Card 05 byte-stable parse/render goldens tied to exact LensCore and svg_conform commits |
90
+ | Other lenses can adopt the shape without SVG code | Card 05 host-only check and handoff manifest; actual EMF/PNG/PostScript migrations remain separate issues |
91
+
92
+ ## Scope boundaries
93
+
94
+ Included:
95
+
96
+ - LensCore schema/model/parser/renderer v1 and its zero-runtime-dependency gem.
97
+ - The svg_conform adapter and transitional aliases.
98
+ - SVG 1.1 Full/static and SVG 2 secure-static profiles.
99
+ - A pinned JSON corpus and cross-repository adoption handoff.
100
+
101
+ Excluded:
102
+
103
+ - EMF, PNG, or PostScript implementation, fixtures, dependency changes, and
104
+ releases.
105
+ - Publishing LensCore or replacing its temporary Git pin with a released gem.
106
+ - SVG 1.1 Tiny/Basic profiles. They are a future sibling profile of
107
+ `svg_1_1_full_static`, to be added once the SVG 1.1 Mobile Profiles
108
+ specification is available as reference material; the repository's available
109
+ Tiny reference is for SVG Tiny 1.2.
110
+ - Complete SVG 2 document/rendering conformance beyond secure-static processing.
111
+ - Profile inheritance or a redesign of existing profile discovery.
112
+ - Full-suite, full-corpus, or fuzz testing for this issue.
113
+
114
+ ## Normative sources
115
+
116
+ - SVG 1.1 Second Edition Recommendation:
117
+ <https://www.w3.org/TR/2011/REC-SVG11-20110816/>
118
+ - SVG 1.1 conformance criteria:
119
+ <https://www.w3.org/TR/2011/REC-SVG11-20110816/conform.html#ConformingSVGDocuments>
120
+ - SVG 1.1 flat DTD:
121
+ <https://www.w3.org/Graphics/SVG/1.1/DTD/svg11-flat-20110816.dtd>
122
+ - CSS 2 core grammar referenced by SVG 1.1:
123
+ <https://www.w3.org/TR/2008/REC-CSS2-20080411/grammar.html>
124
+ - Associating Style Sheets with XML 1.0:
125
+ <https://www.w3.org/TR/1999/REC-xml-stylesheet-19990629/>
126
+ - SVG 2 Candidate Recommendation snapshot:
127
+ <https://www.w3.org/TR/2026/CR-SVG2-20261006/>
128
+ - SVG 2 secure-static processing mode:
129
+ <https://www.w3.org/TR/2026/CR-SVG2-20261006/conform.html#secure-static-mode>
130
+
131
+ ## Verification policy
132
+
133
+ Each card names only focused specs, fixture checks, lint, and build commands.
134
+ No card requires a full test suite, a corpus sweep, or fuzzing. Commands are run
135
+ in the repository named by the card.
@@ -0,0 +1,92 @@
1
+ # 01 — Build LensCore diagnostics v1
2
+
3
+ Effort: **4–7 person-days**
4
+
5
+ Repository: `claricle/lens_core` (new)
6
+
7
+ ## Purpose
8
+
9
+ Create the independent, zero-runtime-dependency gem that owns the shared
10
+ diagnostics wire format and all of its renderers. It must be useful without
11
+ loading svg_conform or any document-format implementation.
12
+
13
+ ## Contract
14
+
15
+ Schema version `1` defines these public concepts:
16
+
17
+ - `LensCore::Report`: `schema_version`, producer name/version, profile ID and
18
+ citations, validity, summary counts, ordered diagnostics, and an optional
19
+ caller-supplied timestamp.
20
+ - `LensCore::Diagnostic`: severity (`error`, `warning`, or `info`), extensible
21
+ kind, stable requirement ID, message, citations, optional remediation,
22
+ JSON-safe context, and optional location.
23
+ - `LensCore::Citation`: required absolute `uri` and optional human label.
24
+ - `LensCore::Location`: an explicit location type plus applicable fields. XML
25
+ locations can carry line, column, and path; binary locations can carry byte
26
+ offset, byte length, record index, and record type. Absent coordinates are
27
+ omitted rather than populated with invented zeroes.
28
+
29
+ The schema rejects an unsupported major schema version but permits new
30
+ diagnostic `kind` values and unknown additive object members so compatible v1
31
+ producers can evolve. Parsers return stable LensCore error classes for malformed
32
+ JSON/YAML, unsupported versions, schema violations, unsafe YAML tags, and
33
+ non-UTF-8 text.
34
+
35
+ Rendering rules are deterministic: diagnostic input order is preserved,
36
+ standard object keys follow schema order, extension keys are lexical, summary
37
+ counts are derived from diagnostics, and timestamps appear only when supplied.
38
+ JSON parsing followed by JSON rendering is byte-stable for canonical inputs.
39
+
40
+ ## Files and implementation
41
+
42
+ - Add `lens_core.gemspec`, `Gemfile`, `Rakefile`, and `lib/lens_core.rb`. The
43
+ gemspec has no runtime dependencies; implementation uses Ruby's JSON, YAML,
44
+ and time facilities.
45
+ - Add value objects in:
46
+ - `lib/lens_core/report.rb`
47
+ - `lib/lens_core/diagnostic.rb`
48
+ - `lib/lens_core/citation.rb`
49
+ - `lib/lens_core/location.rb`
50
+ - Add `lib/lens_core/parsers/json.rb` and `yaml.rb`, and matching renderers in
51
+ `lib/lens_core/renderers/json.rb`, `yaml.rb`, and `human.rb`. Keep validation
52
+ and canonicalization shared rather than reimplemented in each format.
53
+ - Add the normative machine-readable schema at
54
+ `schemas/lens-diagnostics-v1.json` and document field semantics, compatibility
55
+ rules, omission/null behavior, ordering, UTF-8 handling, and failure classes
56
+ in `docs/diagnostics-v1.adoc`.
57
+ - Add `spec/lens_core/report_spec.rb`, `spec/lens_core/parsers_spec.rb`,
58
+ `spec/lens_core/renderers_spec.rb`, and `spec/lens_core/schema_spec.rb`, with
59
+ small fixtures under `spec/fixtures/v1/`. Include XML-location and
60
+ binary-location reports to prove the contract is not SVG-shaped; these are
61
+ format-neutral examples, not claims that another lens has adopted the gem.
62
+
63
+ Keep the Ruby model and JSON Schema at independently readable version constants,
64
+ so a host can report both the gem version and schema version. Do not add SVG
65
+ names, SVG requirement IDs, or dependencies on a lens repository.
66
+
67
+ ## Done when
68
+
69
+ - The gem builds with zero runtime dependencies and can be required in a clean
70
+ Ruby process.
71
+ - Schema-valid JSON and safe YAML parse to the same report model; JSON, YAML,
72
+ and human renderers all include errors, warnings, and infos.
73
+ - Canonical JSON fixtures round-trip byte-for-byte, while malformed, unsafe,
74
+ non-UTF-8, and unsupported-version fixtures fail with documented LensCore
75
+ errors.
76
+ - XML and binary fixture locations survive parse/render without losing present
77
+ coordinates or manufacturing absent ones.
78
+ - The schema, Ruby model, and documentation agree on required fields,
79
+ compatibility behavior, ordering, timestamp handling, and omission rules.
80
+ - All changed Ruby passes the repository linter and the gem artifact builds.
81
+
82
+ ```sh
83
+ bundle install
84
+ bundle exec rspec spec/lens_core/report_spec.rb spec/lens_core/parsers_spec.rb spec/lens_core/renderers_spec.rb spec/lens_core/schema_spec.rb
85
+ bundle exec rubocop
86
+ bundle exec rake build
87
+ ```
88
+
89
+ ## Status
90
+
91
+ State: done
92
+ PR: https://github.com/claricle/lens_core/pull/1
@@ -0,0 +1,123 @@
1
+ # 02 — Adopt LensCore in svg_conform
2
+
3
+ Effort: **3–5 person-days**
4
+
5
+ Repository: `claricle/svg_conform`
6
+
7
+ Depends on: Card 01
8
+
9
+ ## Purpose
10
+
11
+ Make one `LensCore::Report` the canonical representation of an svg_conform
12
+ validation result while preserving the current public and svgcheck-compatible
13
+ surfaces for one transition.
14
+
15
+ ## Dependency and load boundary
16
+
17
+ - Add LensCore to `Gemfile` with the HTTPS Git repository and the exact Card 01
18
+ candidate commit. Generate and commit `Gemfile.lock`; assert that its LensCore
19
+ revision equals the handoff commit.
20
+ - Do not add an unreleased Git dependency or Bundler-only options to
21
+ `svg_conform.gemspec`. Replacing the temporary Git pin with a released gem
22
+ requirement belongs to the later release work.
23
+ - Load LensCore from `lib/svg_conform.rb` and expose the adapter without making
24
+ LensCore depend on svg_conform.
25
+
26
+ ## Canonical mapping
27
+
28
+ Add `SvgConform::Diagnostics::LensCoreAdapter` at
29
+ `lib/svg_conform/diagnostics/lens_core_adapter.rb` with one entry point:
30
+
31
+ ```ruby
32
+ SvgConform::Diagnostics::LensCoreAdapter.call(
33
+ validation_result,
34
+ filename: nil,
35
+ timestamp: nil
36
+ )
37
+ ```
38
+
39
+ The adapter maps:
40
+
41
+ | svg_conform source | LensCore severity | LensCore kind |
42
+ | --- | --- | --- |
43
+ | `errors` | `error` | the issue's violation type, falling back to `validation` |
44
+ | `validity_errors` | `error` | `validity` |
45
+ | `warnings` | `warning` | the issue's violation type, falling back to `validation` |
46
+ | `infos` | `info` | the issue's violation type, falling back to `notice` |
47
+
48
+ It preserves the source collection order within those channels, maps explicit
49
+ requirement IDs without generating new IDs from messages, includes available
50
+ line/column/element-path data, carries remediation fields already exposed by
51
+ `ValidationIssue`, and converts issue data to JSON-safe primitives. An
52
+ unsupported object in context fails at the adapter boundary with a stable error
53
+ rather than leaking Ruby object inspection into JSON.
54
+
55
+ Add profile-level `specification_uri`/`specification_label` to `Profile` in
56
+ `lib/svg_conform/profile.rb`, and requirement-level
57
+ `citation_uri`/`citation_label` to `BaseRequirement` in
58
+ `lib/svg_conform/requirements/base_requirement.rb`; map both pairs in their
59
+ Lutaml YAML definitions. The adapter turns them into LensCore citations.
60
+ Existing profiles may omit them; the two new profiles may not.
61
+
62
+ ## Public and transitional surfaces
63
+
64
+ - Extend `ValidationResult` with `infos`, `info_count`, and
65
+ `to_lens_core_report(timestamp: nil)`. Include infos and validity errors in
66
+ counts/projections without changing `valid?`: only errors and validity errors
67
+ make a result invalid.
68
+ - Make `ValidationResult#to_h`, `#to_json`, and text output project the canonical
69
+ report. Keep their current call signatures and retain file/reference/fix data
70
+ as documented compatibility extensions.
71
+ - Back `ConformanceReport.from_svg_conform_result` with the same report and keep
72
+ its current class, factory signature, and YAML shape as a deprecated legacy
73
+ projection. Preserve the `use_svgcheck_mapping:` adapter and
74
+ `from_svgcheck_result`; svgcheck filtering happens after canonical report
75
+ construction and does not mutate the canonical report.
76
+ - Route `lib/svg_conform/commands/check.rb` JSON, YAML, and table output through
77
+ LensCore renderers. The accepted CLI value `table` remains an alias for the
78
+ human renderer for compatibility.
79
+ - Extend `ValidationContext#add_notice`,
80
+ `ValidationContext#add_external_reference_notice`, and
81
+ `Validation::ErrorTracker#add_notice` to accept an optional explicit
82
+ requirement ID while preserving existing calls. Store it on the existing
83
+ `Errors::ValidationIssue` used by the info channel. Requirements pass their
84
+ own stable ID so infos no longer fall back to `unknown`.
85
+
86
+ Document the v1 API and transition in the proposed `docs/diagnostics.adoc`, and
87
+ update the existing `docs/api_reference.adoc` and `docs/cli_guide.adoc`. Add
88
+ focused specs at `spec/svg_conform/diagnostics/lens_core_adapter_spec.rb`,
89
+ `spec/svg_conform/validation_result_spec.rb`,
90
+ `spec/svg_conform/conformance_report_spec.rb`, and the existing command/context
91
+ spec paths used below. Add
92
+ `spec/fixtures/diagnostics/external-reference-info.svg` so the library and CLI
93
+ assertions use a small SVG that emits an ordinary informational external
94
+ reference notice without depending on later profile fixtures.
95
+
96
+ ## Done when
97
+
98
+ - `Gemfile.lock` pins the exact LensCore candidate commit, while the gemspec has
99
+ no unreleased LensCore dependency.
100
+ - One validation result containing an error, validity error, warning, and info
101
+ produces one schema-valid report with correct validity and per-channel
102
+ summary counts.
103
+ - The library's JSON/YAML/human paths and the ordinary `check` command render
104
+ from that report and preserve the informational diagnostic.
105
+ - Existing `ValidationResult`, `ConformanceReport`, table-output, and svgcheck
106
+ factory call signatures remain callable and are covered by compatibility
107
+ specs.
108
+ - Profile and requirement citations survive YAML loading and appear in the
109
+ canonical report; JSON-unsafe issue context fails deterministically.
110
+ - All changed Ruby passes the repository linter and the gem builds.
111
+
112
+ ```sh
113
+ bundle install
114
+ bundle exec rspec spec/svg_conform/diagnostics/lens_core_adapter_spec.rb spec/svg_conform/validation_result_spec.rb spec/svg_conform/conformance_report_spec.rb spec/svg_conform/validation_context_spec.rb spec/svg_conform/commands/check_command_spec.rb
115
+ bundle exec svg_conform check spec/fixtures/diagnostics/external-reference-info.svg --profile svg_1_2_rfc --format json
116
+ bundle exec rubocop
117
+ bundle exec rake build
118
+ ```
119
+
120
+ ## Status
121
+
122
+ State: done
123
+ PR: https://github.com/claricle/svg_conform/pull/103