rails-mmd 0.1.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 (45) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +10 -0
  3. data/LICENSE +21 -0
  4. data/README.md +82 -0
  5. data/SECURITY.md +40 -0
  6. data/docs/active-record-association-support.md +37 -0
  7. data/docs/assets/branding/rails-mmd-social-preview.jpg +0 -0
  8. data/docs/usage.md +126 -0
  9. data/exe/rails-mmd +6 -0
  10. data/fixtures/schemas/diagnostics/valid/catalog.json +39 -0
  11. data/lib/rails_mmd/artifact_ref_sanitizer.rb +59 -0
  12. data/lib/rails_mmd/attribute_types.rb +29 -0
  13. data/lib/rails_mmd/canonical_json.rb +19 -0
  14. data/lib/rails_mmd/cli.rb +86 -0
  15. data/lib/rails_mmd/config.rb +249 -0
  16. data/lib/rails_mmd/constant_resolver.rb +33 -0
  17. data/lib/rails_mmd/diagnostic_factory.rb +122 -0
  18. data/lib/rails_mmd/domain_resolver.rb +97 -0
  19. data/lib/rails_mmd/exit_policy.rb +27 -0
  20. data/lib/rails_mmd/generate_command.rb +116 -0
  21. data/lib/rails_mmd/generation_pipeline.rb +109 -0
  22. data/lib/rails_mmd/ir_builder.rb +122 -0
  23. data/lib/rails_mmd/mermaid_serializer.rb +168 -0
  24. data/lib/rails_mmd/metadata_shapes.rb +55 -0
  25. data/lib/rails_mmd/model_inventory.rb +144 -0
  26. data/lib/rails_mmd/ordering.rb +25 -0
  27. data/lib/rails_mmd/output_directory.rb +192 -0
  28. data/lib/rails_mmd/publisher.rb +166 -0
  29. data/lib/rails_mmd/rails_loader.rb +96 -0
  30. data/lib/rails_mmd/redactor.rb +143 -0
  31. data/lib/rails_mmd/relationship_builder.rb +982 -0
  32. data/lib/rails_mmd/render_plan_builder.rb +192 -0
  33. data/lib/rails_mmd/safe_token_assigner.rb +130 -0
  34. data/lib/rails_mmd/schema_probe.rb +376 -0
  35. data/lib/rails_mmd/schema_validator.rb +136 -0
  36. data/lib/rails_mmd/subject_ids.rb +17 -0
  37. data/lib/rails_mmd/tooling_tasks.rb +47 -0
  38. data/lib/rails_mmd/verification_runner.rb +143 -0
  39. data/lib/rails_mmd/version.rb +5 -0
  40. data/lib/rails_mmd.rb +15 -0
  41. data/schemas/config.schema.json +54 -0
  42. data/schemas/diagnostics.schema.json +418 -0
  43. data/schemas/ir.schema.json +84 -0
  44. data/schemas/render_plan.schema.json +136 -0
  45. metadata +135 -0
@@ -0,0 +1,418 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://audiostakes.example/schemas/rails-mmd/diagnostics.schema.json",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": ["schema_version", "scope", "domain_id", "diagnostics", "digest_sha256"],
7
+ "allOf": [
8
+ {
9
+ "if": { "properties": { "scope": { "const": "global" } }, "required": ["scope"] },
10
+ "then": { "properties": { "domain_id": { "type": "null" } } }
11
+ },
12
+ {
13
+ "if": { "properties": { "scope": { "const": "domain" } }, "required": ["scope"] },
14
+ "then": { "properties": { "domain_id": { "$ref": "#/$defs/domain_id" } } }
15
+ }
16
+ ],
17
+ "properties": {
18
+ "schema_version": { "const": 1 },
19
+ "scope": { "enum": ["global", "domain"] },
20
+ "domain_id": {
21
+ "anyOf": [
22
+ { "type": "null" },
23
+ { "$ref": "#/$defs/domain_id" }
24
+ ]
25
+ },
26
+ "diagnostics": {
27
+ "type": "array",
28
+ "items": { "$ref": "#/$defs/diagnostic" }
29
+ },
30
+ "digest_sha256": { "$ref": "#/$defs/digest" }
31
+ },
32
+ "$defs": {
33
+ "digest": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
34
+ "domain_id": {
35
+ "type": "string",
36
+ "allOf": [
37
+ { "pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$" },
38
+ { "not": { "pattern": "\\s" } }
39
+ ]
40
+ },
41
+ "ruby_constant": {
42
+ "type": "string",
43
+ "pattern": "^[A-Z][A-Za-z0-9_]*(?:::[A-Z][A-Za-z0-9_]*)*$"
44
+ },
45
+ "safe_token": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]*$" },
46
+ "structured_id": { "type": "string", "pattern": "^[a-z][a-z0-9_]*$" },
47
+ "relative_path": {
48
+ "type": "string",
49
+ "minLength": 1,
50
+ "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))(?!.*//)[A-Za-z0-9._/-]+$",
51
+ "not": { "pattern": "^[A-Za-z]:[\\\\/]" }
52
+ },
53
+ "sanitized_string": {
54
+ "type": "string",
55
+ "allOf": [
56
+ { "not": { "pattern": "(^|\\s)/(?:Users|tmp|private|var)(?:/|\\s|$)" } },
57
+ { "not": { "pattern": "[A-Za-z]:[\\\\/]" } },
58
+ { "not": { "pattern": "\\b(?:generated_at|process_id|random_seed|raw_exception_backtrace|token|secret|api[_-]?key|password)\\b\\s*[:=]" } }
59
+ ]
60
+ },
61
+ "diagnostic_code": {
62
+ "enum": [
63
+ "CONFIG_NOT_FOUND",
64
+ "CONFIG_SCHEMA_INVALID",
65
+ "CONFIG_DOMAIN_NOT_FOUND",
66
+ "OUTPUT_DIRECTORY_INVALID",
67
+ "DOMAIN_MODEL_NOT_FOUND",
68
+ "DOMAIN_MODEL_NOT_RENDERABLE",
69
+ "DOMAIN_EMPTY",
70
+ "RAILS_LOAD_FAILED",
71
+ "RAILS_EAGER_LOAD_FAILED",
72
+ "MULTI_DB_UNSUPPORTED",
73
+ "MODEL_TABLE_MISSING",
74
+ "MODEL_PRIMARY_KEY_UNSUPPORTED",
75
+ "ASSOCIATION_TARGET_UNRESOLVED",
76
+ "ASSOCIATION_THROUGH_UNRESOLVED",
77
+ "ASSOCIATION_SOURCE_UNRESOLVED",
78
+ "ASSOCIATION_TARGET_NOT_RENDERABLE_OMITTED",
79
+ "ASSOCIATION_POLYMORPHIC_OMITTED",
80
+ "ASSOCIATION_POLYMORPHIC_TARGETS_UNRESOLVED",
81
+ "ASSOCIATION_JOIN_TABLE_INVALID",
82
+ "ASSOCIATION_SCOPED_OMITTED",
83
+ "ASSOCIATION_COMPOSITE_KEY_OMITTED",
84
+ "ASSOCIATION_KEY_COLUMN_MISSING",
85
+ "ASSOCIATION_NON_PRIMARY_KEY_OMITTED",
86
+ "ASSOCIATION_NAME_UNSUPPORTED_OMITTED",
87
+ "ASSOCIATION_MACRO_OMITTED",
88
+ "DOMAIN_RELATIONSHIP_OMITTED",
89
+ "DB_METADATA_DEGRADED",
90
+ "SAFE_TOKEN_COLLISION",
91
+ "MERMAID_SERIALIZATION_FAILED",
92
+ "OUTPUT_WRITE_FAILED",
93
+ "INTERNAL_ERROR"
94
+ ]
95
+ },
96
+ "diagnostic": {
97
+ "type": "object",
98
+ "additionalProperties": false,
99
+ "required": [
100
+ "diagnostic_id",
101
+ "code",
102
+ "severity",
103
+ "phase",
104
+ "scope",
105
+ "subject_id",
106
+ "message",
107
+ "metadata",
108
+ "artifact_refs",
109
+ "remediation"
110
+ ],
111
+ "allOf": [
112
+ { "$ref": "#/$defs/metadata_for_config_not_found" },
113
+ { "$ref": "#/$defs/metadata_for_config_schema_invalid" },
114
+ { "$ref": "#/$defs/metadata_for_config_domain_not_found" },
115
+ { "$ref": "#/$defs/metadata_for_output_directory_invalid" },
116
+ { "$ref": "#/$defs/metadata_for_domain_model_not_found" },
117
+ { "$ref": "#/$defs/metadata_for_domain_model_not_renderable" },
118
+ { "$ref": "#/$defs/metadata_for_domain_empty" },
119
+ { "$ref": "#/$defs/metadata_for_rails_load_failed" },
120
+ { "$ref": "#/$defs/metadata_for_rails_eager_load_failed" },
121
+ { "$ref": "#/$defs/metadata_for_multi_db_unsupported" },
122
+ { "$ref": "#/$defs/metadata_for_model_table_missing" },
123
+ { "$ref": "#/$defs/metadata_for_model_primary_key_unsupported" },
124
+ { "$ref": "#/$defs/metadata_for_association_target_unresolved" },
125
+ { "$ref": "#/$defs/metadata_for_association_through_unresolved" },
126
+ { "$ref": "#/$defs/metadata_for_association_source_unresolved" },
127
+ { "$ref": "#/$defs/metadata_for_association_target_not_renderable_omitted" },
128
+ { "$ref": "#/$defs/metadata_for_association_polymorphic_omitted" },
129
+ { "$ref": "#/$defs/metadata_for_association_polymorphic_targets_unresolved" },
130
+ { "$ref": "#/$defs/metadata_for_association_join_table_invalid" },
131
+ { "$ref": "#/$defs/metadata_for_association_scoped_omitted" },
132
+ { "$ref": "#/$defs/metadata_for_association_composite_key_omitted" },
133
+ { "$ref": "#/$defs/metadata_for_association_key_column_missing" },
134
+ { "$ref": "#/$defs/metadata_for_association_non_primary_key_omitted" },
135
+ { "$ref": "#/$defs/metadata_for_association_name_unsupported_omitted" },
136
+ { "$ref": "#/$defs/metadata_for_association_macro_omitted" },
137
+ { "$ref": "#/$defs/metadata_for_domain_relationship_omitted" },
138
+ { "$ref": "#/$defs/metadata_for_db_metadata_degraded" },
139
+ { "$ref": "#/$defs/metadata_for_safe_token_collision" },
140
+ { "$ref": "#/$defs/metadata_for_mermaid_serialization_failed" },
141
+ { "$ref": "#/$defs/metadata_for_output_write_failed" },
142
+ { "$ref": "#/$defs/metadata_for_internal_error" }
143
+ ],
144
+ "properties": {
145
+ "diagnostic_id": { "type": "string", "minLength": 1 },
146
+ "code": { "$ref": "#/$defs/diagnostic_code" },
147
+ "severity": { "enum": ["fatal", "error", "warning"] },
148
+ "phase": {
149
+ "enum": [
150
+ "config",
151
+ "output_setup",
152
+ "domain_resolution",
153
+ "rails_load",
154
+ "schema_probe",
155
+ "relationship_build",
156
+ "tokenization",
157
+ "serialization",
158
+ "publish",
159
+ "internal"
160
+ ]
161
+ },
162
+ "scope": { "enum": ["invocation", "domain", "model", "relationship", "artifact"] },
163
+ "subject_id": {
164
+ "anyOf": [
165
+ { "type": "null" },
166
+ { "$ref": "#/$defs/sanitized_string" }
167
+ ]
168
+ },
169
+ "message": { "$ref": "#/$defs/sanitized_string" },
170
+ "metadata": { "type": "object" },
171
+ "artifact_refs": {
172
+ "type": "array",
173
+ "items": { "$ref": "#/$defs/artifact_ref" }
174
+ },
175
+ "remediation": {
176
+ "anyOf": [
177
+ { "type": "null" },
178
+ { "$ref": "#/$defs/remediation" }
179
+ ]
180
+ }
181
+ }
182
+ },
183
+ "artifact_ref": {
184
+ "type": "object",
185
+ "additionalProperties": false,
186
+ "required": ["artifact_kind", "domain_id"],
187
+ "properties": {
188
+ "artifact_kind": { "enum": ["diagnostics", "er", "class", "render_plan", "stderr"] },
189
+ "domain_id": {
190
+ "anyOf": [
191
+ { "type": "null" },
192
+ { "$ref": "#/$defs/domain_id" }
193
+ ]
194
+ },
195
+ "path": { "$ref": "#/$defs/relative_path" }
196
+ }
197
+ },
198
+ "remediation": {
199
+ "type": "object",
200
+ "additionalProperties": false,
201
+ "required": ["summary"],
202
+ "properties": {
203
+ "summary": { "$ref": "#/$defs/sanitized_string" },
204
+ "steps": {
205
+ "type": "array",
206
+ "items": { "$ref": "#/$defs/sanitized_string" }
207
+ }
208
+ }
209
+ },
210
+ "metadata_shape_config_not_found": {
211
+ "type": "object",
212
+ "additionalProperties": false,
213
+ "required": ["config_path"],
214
+ "properties": { "config_path": { "$ref": "#/$defs/relative_path" } }
215
+ },
216
+ "metadata_shape_config_schema_invalid": {
217
+ "type": "object",
218
+ "additionalProperties": false,
219
+ "required": ["config_path", "field_path"],
220
+ "properties": {
221
+ "config_path": { "$ref": "#/$defs/relative_path" },
222
+ "field_path": { "$ref": "#/$defs/sanitized_string" }
223
+ }
224
+ },
225
+ "metadata_shape_config_domain_not_found": {
226
+ "type": "object",
227
+ "additionalProperties": false,
228
+ "required": ["domain_id"],
229
+ "properties": { "domain_id": { "$ref": "#/$defs/domain_id" } }
230
+ },
231
+ "metadata_shape_output_directory_invalid": {
232
+ "type": "object",
233
+ "additionalProperties": false,
234
+ "required": ["field_path", "reason"],
235
+ "properties": {
236
+ "field_path": { "$ref": "#/$defs/sanitized_string" },
237
+ "reason": { "$ref": "#/$defs/sanitized_string" }
238
+ }
239
+ },
240
+ "metadata_shape_domain_model_not_found": {
241
+ "type": "object",
242
+ "additionalProperties": false,
243
+ "required": ["domain_id", "ruby_constant"],
244
+ "properties": {
245
+ "domain_id": { "$ref": "#/$defs/domain_id" },
246
+ "ruby_constant": { "$ref": "#/$defs/ruby_constant" }
247
+ }
248
+ },
249
+ "metadata_shape_domain_model_not_renderable": {
250
+ "type": "object",
251
+ "additionalProperties": false,
252
+ "required": ["domain_id", "ruby_constant", "renderability_reason"],
253
+ "properties": {
254
+ "domain_id": { "$ref": "#/$defs/domain_id" },
255
+ "ruby_constant": { "$ref": "#/$defs/ruby_constant" },
256
+ "renderability_reason": { "$ref": "#/$defs/sanitized_string" }
257
+ }
258
+ },
259
+ "metadata_shape_domain_empty": {
260
+ "type": "object",
261
+ "additionalProperties": false,
262
+ "required": ["domain_id"],
263
+ "properties": { "domain_id": { "$ref": "#/$defs/domain_id" } }
264
+ },
265
+ "metadata_shape_exception": {
266
+ "type": "object",
267
+ "additionalProperties": false,
268
+ "required": ["exception_class", "exception_summary", "backtrace"],
269
+ "properties": {
270
+ "exception_class": { "$ref": "#/$defs/ruby_constant" },
271
+ "exception_summary": { "$ref": "#/$defs/sanitized_string" },
272
+ "backtrace": { "type": "null" }
273
+ }
274
+ },
275
+ "metadata_shape_multi_db_unsupported": {
276
+ "type": "object",
277
+ "additionalProperties": false,
278
+ "required": ["domain_id", "connection_context_ids"],
279
+ "properties": {
280
+ "domain_id": { "$ref": "#/$defs/domain_id" },
281
+ "connection_context_ids": {
282
+ "type": "array",
283
+ "items": { "$ref": "#/$defs/sanitized_string" }
284
+ }
285
+ }
286
+ },
287
+ "metadata_shape_model_table_missing": {
288
+ "type": "object",
289
+ "additionalProperties": false,
290
+ "required": ["domain_id", "ruby_constant", "table_name"],
291
+ "properties": {
292
+ "domain_id": { "$ref": "#/$defs/domain_id" },
293
+ "ruby_constant": { "$ref": "#/$defs/ruby_constant" },
294
+ "table_name": { "$ref": "#/$defs/structured_id" }
295
+ }
296
+ },
297
+ "metadata_shape_association": {
298
+ "type": "object",
299
+ "additionalProperties": false,
300
+ "required": ["domain_id", "owner_constant", "association_name"],
301
+ "properties": {
302
+ "domain_id": { "$ref": "#/$defs/domain_id" },
303
+ "owner_constant": { "$ref": "#/$defs/ruby_constant" },
304
+ "association_name": { "$ref": "#/$defs/sanitized_string" },
305
+ "target_constant": { "$ref": "#/$defs/ruby_constant" }
306
+ }
307
+ },
308
+ "metadata_shape_association_join_table": {
309
+ "type": "object",
310
+ "additionalProperties": false,
311
+ "required": ["domain_id", "owner_constant", "association_name", "join_table", "reason"],
312
+ "properties": {
313
+ "domain_id": { "$ref": "#/$defs/domain_id" },
314
+ "owner_constant": { "$ref": "#/$defs/ruby_constant" },
315
+ "association_name": { "$ref": "#/$defs/sanitized_string" },
316
+ "target_constant": { "$ref": "#/$defs/ruby_constant" },
317
+ "join_table": { "$ref": "#/$defs/structured_id" },
318
+ "reason": {
319
+ "enum": ["unresolved", "primary_key_present", "join_column_missing", "extra_columns", "ambiguous_columns"]
320
+ }
321
+ }
322
+ },
323
+ "metadata_shape_association_macro": {
324
+ "type": "object",
325
+ "additionalProperties": false,
326
+ "required": ["domain_id", "owner_constant", "association_name", "association_macro"],
327
+ "properties": {
328
+ "domain_id": { "$ref": "#/$defs/domain_id" },
329
+ "owner_constant": { "$ref": "#/$defs/ruby_constant" },
330
+ "association_name": { "$ref": "#/$defs/sanitized_string" },
331
+ "association_macro": { "$ref": "#/$defs/structured_id" }
332
+ }
333
+ },
334
+ "metadata_shape_db_metadata_degraded": {
335
+ "type": "object",
336
+ "additionalProperties": false,
337
+ "required": ["domain_id", "ruby_constant", "metadata_kind", "reason"],
338
+ "properties": {
339
+ "domain_id": { "$ref": "#/$defs/domain_id" },
340
+ "ruby_constant": { "$ref": "#/$defs/ruby_constant" },
341
+ "metadata_kind": {
342
+ "enum": ["foreign_key", "foreign_key_nullability", "unique_index", "cardinality"]
343
+ },
344
+ "reason": { "$ref": "#/$defs/sanitized_string" }
345
+ }
346
+ },
347
+ "metadata_shape_safe_token_collision": {
348
+ "type": "object",
349
+ "additionalProperties": false,
350
+ "required": [
351
+ "artifact_kind",
352
+ "domain_id",
353
+ "token_kind",
354
+ "base_safe_token",
355
+ "collision_subject_count",
356
+ "resolved"
357
+ ],
358
+ "properties": {
359
+ "artifact_kind": { "enum": ["er", "class"] },
360
+ "domain_id": { "$ref": "#/$defs/domain_id" },
361
+ "token_kind": { "enum": ["entity", "attribute", "relationship", "diagnostic", "comment"] },
362
+ "base_safe_token": { "$ref": "#/$defs/safe_token" },
363
+ "collision_subject_count": { "type": "integer", "minimum": 2 },
364
+ "resolved": { "type": "boolean" }
365
+ }
366
+ },
367
+ "metadata_shape_mermaid_serialization_failed": {
368
+ "type": "object",
369
+ "additionalProperties": false,
370
+ "required": ["artifact_kind", "domain_id", "reason"],
371
+ "properties": {
372
+ "artifact_kind": { "enum": ["er", "class"] },
373
+ "domain_id": { "$ref": "#/$defs/domain_id" },
374
+ "reason": { "$ref": "#/$defs/sanitized_string" }
375
+ }
376
+ },
377
+ "metadata_shape_output_write_failed": {
378
+ "type": "object",
379
+ "additionalProperties": false,
380
+ "required": ["operation"],
381
+ "properties": {
382
+ "operation": { "enum": ["mkdir", "write", "rename", "remove"] },
383
+ "path": { "$ref": "#/$defs/relative_path" }
384
+ }
385
+ },
386
+ "metadata_for_config_not_found": { "if": { "properties": { "code": { "const": "CONFIG_NOT_FOUND" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_config_not_found" } } } },
387
+ "metadata_for_config_schema_invalid": { "if": { "properties": { "code": { "const": "CONFIG_SCHEMA_INVALID" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_config_schema_invalid" } } } },
388
+ "metadata_for_config_domain_not_found": { "if": { "properties": { "code": { "const": "CONFIG_DOMAIN_NOT_FOUND" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_config_domain_not_found" } } } },
389
+ "metadata_for_output_directory_invalid": { "if": { "properties": { "code": { "const": "OUTPUT_DIRECTORY_INVALID" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_output_directory_invalid" } } } },
390
+ "metadata_for_domain_model_not_found": { "if": { "properties": { "code": { "const": "DOMAIN_MODEL_NOT_FOUND" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_domain_model_not_found" } } } },
391
+ "metadata_for_domain_model_not_renderable": { "if": { "properties": { "code": { "const": "DOMAIN_MODEL_NOT_RENDERABLE" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_domain_model_not_renderable" } } } },
392
+ "metadata_for_domain_empty": { "if": { "properties": { "code": { "const": "DOMAIN_EMPTY" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_domain_empty" } } } },
393
+ "metadata_for_rails_load_failed": { "if": { "properties": { "code": { "const": "RAILS_LOAD_FAILED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_exception" } } } },
394
+ "metadata_for_rails_eager_load_failed": { "if": { "properties": { "code": { "const": "RAILS_EAGER_LOAD_FAILED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_exception" } } } },
395
+ "metadata_for_multi_db_unsupported": { "if": { "properties": { "code": { "const": "MULTI_DB_UNSUPPORTED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_multi_db_unsupported" } } } },
396
+ "metadata_for_model_table_missing": { "if": { "properties": { "code": { "const": "MODEL_TABLE_MISSING" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_model_table_missing" } } } },
397
+ "metadata_for_model_primary_key_unsupported": { "if": { "properties": { "code": { "const": "MODEL_PRIMARY_KEY_UNSUPPORTED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_model_table_missing" } } } },
398
+ "metadata_for_association_target_unresolved": { "if": { "properties": { "code": { "const": "ASSOCIATION_TARGET_UNRESOLVED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_association" } } } },
399
+ "metadata_for_association_through_unresolved": { "if": { "properties": { "code": { "const": "ASSOCIATION_THROUGH_UNRESOLVED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_association" } } } },
400
+ "metadata_for_association_source_unresolved": { "if": { "properties": { "code": { "const": "ASSOCIATION_SOURCE_UNRESOLVED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_association" } } } },
401
+ "metadata_for_association_target_not_renderable_omitted": { "if": { "properties": { "code": { "const": "ASSOCIATION_TARGET_NOT_RENDERABLE_OMITTED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_association" } } } },
402
+ "metadata_for_association_polymorphic_omitted": { "if": { "properties": { "code": { "const": "ASSOCIATION_POLYMORPHIC_OMITTED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_association" } } } },
403
+ "metadata_for_association_polymorphic_targets_unresolved": { "if": { "properties": { "code": { "const": "ASSOCIATION_POLYMORPHIC_TARGETS_UNRESOLVED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_association" } } } },
404
+ "metadata_for_association_join_table_invalid": { "if": { "properties": { "code": { "const": "ASSOCIATION_JOIN_TABLE_INVALID" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_association_join_table" } } } },
405
+ "metadata_for_association_scoped_omitted": { "if": { "properties": { "code": { "const": "ASSOCIATION_SCOPED_OMITTED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_association" } } } },
406
+ "metadata_for_association_composite_key_omitted": { "if": { "properties": { "code": { "const": "ASSOCIATION_COMPOSITE_KEY_OMITTED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_association" } } } },
407
+ "metadata_for_association_key_column_missing": { "if": { "properties": { "code": { "const": "ASSOCIATION_KEY_COLUMN_MISSING" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_association" } } } },
408
+ "metadata_for_association_non_primary_key_omitted": { "if": { "properties": { "code": { "const": "ASSOCIATION_NON_PRIMARY_KEY_OMITTED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_association" } } } },
409
+ "metadata_for_association_name_unsupported_omitted": { "if": { "properties": { "code": { "const": "ASSOCIATION_NAME_UNSUPPORTED_OMITTED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_association" } } } },
410
+ "metadata_for_association_macro_omitted": { "if": { "properties": { "code": { "const": "ASSOCIATION_MACRO_OMITTED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_association_macro" } } } },
411
+ "metadata_for_domain_relationship_omitted": { "if": { "properties": { "code": { "const": "DOMAIN_RELATIONSHIP_OMITTED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_association" } } } },
412
+ "metadata_for_db_metadata_degraded": { "if": { "properties": { "code": { "const": "DB_METADATA_DEGRADED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_db_metadata_degraded" } } } },
413
+ "metadata_for_safe_token_collision": { "if": { "properties": { "code": { "const": "SAFE_TOKEN_COLLISION" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_safe_token_collision" } } } },
414
+ "metadata_for_mermaid_serialization_failed": { "if": { "properties": { "code": { "const": "MERMAID_SERIALIZATION_FAILED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_mermaid_serialization_failed" } } } },
415
+ "metadata_for_output_write_failed": { "if": { "properties": { "code": { "const": "OUTPUT_WRITE_FAILED" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_output_write_failed" } } } },
416
+ "metadata_for_internal_error": { "if": { "properties": { "code": { "const": "INTERNAL_ERROR" } }, "required": ["code"] }, "then": { "properties": { "metadata": { "$ref": "#/$defs/metadata_shape_exception" } } } }
417
+ }
418
+ }
@@ -0,0 +1,84 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://audiostakes.example/schemas/rails-mmd/ir.schema.json",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": ["schema_version", "domain_id", "entities", "relationships", "diagnostic_ids", "digest_sha256"],
7
+ "properties": {
8
+ "schema_version": { "const": 1 },
9
+ "domain_id": { "$ref": "#/$defs/domain_id" },
10
+ "entities": {
11
+ "type": "array",
12
+ "items": { "$ref": "#/$defs/entity" }
13
+ },
14
+ "relationships": {
15
+ "type": "array",
16
+ "items": { "$ref": "#/$defs/relationship" }
17
+ },
18
+ "diagnostic_ids": {
19
+ "type": "array",
20
+ "items": { "type": "string", "minLength": 1 }
21
+ },
22
+ "digest_sha256": { "$ref": "#/$defs/digest" }
23
+ },
24
+ "$defs": {
25
+ "digest": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
26
+ "domain_id": {
27
+ "type": "string",
28
+ "allOf": [
29
+ { "pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$" },
30
+ { "not": { "pattern": "\\s" } }
31
+ ]
32
+ },
33
+ "ruby_constant": {
34
+ "type": "string",
35
+ "pattern": "^[A-Z][A-Za-z0-9_]*(?:::[A-Z][A-Za-z0-9_]*)*$"
36
+ },
37
+ "entity": {
38
+ "type": "object",
39
+ "additionalProperties": false,
40
+ "required": ["entity_id", "ruby_constant", "table_name", "attributes"],
41
+ "properties": {
42
+ "entity_id": { "type": "string", "minLength": 1 },
43
+ "ruby_constant": { "$ref": "#/$defs/ruby_constant" },
44
+ "table_name": { "type": "string", "minLength": 1 },
45
+ "attributes": {
46
+ "type": "array",
47
+ "items": { "$ref": "#/$defs/attribute" }
48
+ }
49
+ }
50
+ },
51
+ "attribute": {
52
+ "type": "object",
53
+ "additionalProperties": false,
54
+ "required": ["attribute_id", "name", "role", "type"],
55
+ "properties": {
56
+ "attribute_id": { "type": "string", "minLength": 1 },
57
+ "name": { "type": "string", "minLength": 1 },
58
+ "role": { "enum": ["primary_key", "foreign_key"] },
59
+ "type": { "type": "string", "minLength": 1 }
60
+ }
61
+ },
62
+ "relationship": {
63
+ "type": "object",
64
+ "additionalProperties": false,
65
+ "required": [
66
+ "relationship_id",
67
+ "owner_entity_id",
68
+ "target_entity_id",
69
+ "association_name",
70
+ "owner_cardinality",
71
+ "target_cardinality"
72
+ ],
73
+ "properties": {
74
+ "relationship_id": { "type": "string", "minLength": 1 },
75
+ "owner_entity_id": { "type": "string", "minLength": 1 },
76
+ "target_entity_id": { "type": "string", "minLength": 1 },
77
+ "association_name": { "type": "string", "pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*[!?=]?$" },
78
+ "owner_cardinality": { "$ref": "#/$defs/cardinality" },
79
+ "target_cardinality": { "$ref": "#/$defs/cardinality" }
80
+ }
81
+ },
82
+ "cardinality": { "enum": ["0..1", "1..1", "0..many", "1..many"] }
83
+ }
84
+ }
@@ -0,0 +1,136 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://audiostakes.example/schemas/rails-mmd/render_plan.schema.json",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": [
7
+ "schema_version",
8
+ "artifact_kind",
9
+ "domain_id",
10
+ "direction",
11
+ "entities",
12
+ "relationships",
13
+ "comments",
14
+ "diagnostic_ids",
15
+ "digest_sha256"
16
+ ],
17
+ "properties": {
18
+ "schema_version": { "const": 1 },
19
+ "artifact_kind": { "enum": ["er", "class"] },
20
+ "domain_id": { "$ref": "#/$defs/domain_id" },
21
+ "direction": { "enum": ["TB", "BT", "LR", "RL"] },
22
+ "entities": {
23
+ "type": "array",
24
+ "items": { "$ref": "#/$defs/entity" }
25
+ },
26
+ "relationships": {
27
+ "type": "array",
28
+ "items": { "$ref": "#/$defs/relationship" }
29
+ },
30
+ "comments": {
31
+ "type": "array",
32
+ "items": { "$ref": "#/$defs/comment" }
33
+ },
34
+ "diagnostic_ids": {
35
+ "type": "array",
36
+ "items": { "type": "string", "minLength": 1 }
37
+ },
38
+ "digest_sha256": { "$ref": "#/$defs/digest" }
39
+ },
40
+ "$defs": {
41
+ "digest": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
42
+ "domain_id": {
43
+ "type": "string",
44
+ "allOf": [
45
+ { "pattern": "^[a-z][a-z0-9]*(?:_[a-z0-9]+)*$" },
46
+ { "not": { "pattern": "\\s" } }
47
+ ]
48
+ },
49
+ "safe_token": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]*$" },
50
+ "sanitized_comment": {
51
+ "type": "string",
52
+ "allOf": [
53
+ { "not": { "pattern": "(^|\\s)/(?:Users|tmp|private|var)(?:/|\\s|$)" } },
54
+ { "not": { "pattern": "[A-Za-z]:[\\\\/]" } },
55
+ { "not": { "pattern": "[\\r\\n]" } },
56
+ { "not": { "pattern": "[A-Za-z0-9_-]*(?:generated_at|process_id|random_seed|raw_exception_backtrace|token|secret|api[_-]?key|password|passwd|credential|database_url|(?:access|auth|refresh)[_-]?token|pid)[A-Za-z0-9_-]*\\s*(?::|=|\\s+)\\s*\\S+" } },
57
+ { "not": { "pattern": "\\[REDACTED_KEY\\]\\s*(?::|=|\\s+)\\s*\\S+" } }
58
+ ]
59
+ },
60
+ "attribute_type": {
61
+ "enum": ["bigint", "boolean", "date", "datetime", "decimal", "float", "integer", "string", "text", "time", "unknown"]
62
+ },
63
+ "entity": {
64
+ "type": "object",
65
+ "additionalProperties": false,
66
+ "required": ["entity_id", "safe_token", "label", "attributes"],
67
+ "properties": {
68
+ "entity_id": { "type": "string", "minLength": 1 },
69
+ "safe_token": { "$ref": "#/$defs/safe_token" },
70
+ "label": { "$ref": "#/$defs/sanitized_comment" },
71
+ "attributes": {
72
+ "type": "array",
73
+ "items": { "$ref": "#/$defs/attribute" }
74
+ }
75
+ }
76
+ },
77
+ "attribute": {
78
+ "type": "object",
79
+ "additionalProperties": false,
80
+ "required": ["attribute_id", "safe_token", "label", "type", "key_marker"],
81
+ "properties": {
82
+ "attribute_id": { "type": "string", "minLength": 1 },
83
+ "safe_token": { "$ref": "#/$defs/safe_token" },
84
+ "label": { "$ref": "#/$defs/sanitized_comment" },
85
+ "type": { "$ref": "#/$defs/attribute_type" },
86
+ "key_marker": {
87
+ "anyOf": [
88
+ { "type": "null" },
89
+ { "enum": ["PK", "FK"] }
90
+ ]
91
+ }
92
+ }
93
+ },
94
+ "relationship": {
95
+ "type": "object",
96
+ "additionalProperties": false,
97
+ "required": [
98
+ "relationship_id",
99
+ "safe_token",
100
+ "owner_safe_token",
101
+ "target_safe_token",
102
+ "label",
103
+ "owner_cardinality",
104
+ "target_cardinality",
105
+ "er_left_marker",
106
+ "er_right_marker",
107
+ "class_owner_multiplicity",
108
+ "class_target_multiplicity"
109
+ ],
110
+ "properties": {
111
+ "relationship_id": { "type": "string", "minLength": 1 },
112
+ "safe_token": { "$ref": "#/$defs/safe_token" },
113
+ "owner_safe_token": { "$ref": "#/$defs/safe_token" },
114
+ "target_safe_token": { "$ref": "#/$defs/safe_token" },
115
+ "label": { "$ref": "#/$defs/sanitized_comment" },
116
+ "owner_cardinality": { "$ref": "#/$defs/cardinality" },
117
+ "target_cardinality": { "$ref": "#/$defs/cardinality" },
118
+ "er_left_marker": { "enum": ["|o", "||", "}o", "}|"] },
119
+ "er_right_marker": { "enum": ["o|", "||", "o{", "|{"] },
120
+ "class_owner_multiplicity": { "enum": ["0..1", "1", "0..*", "1..*"] },
121
+ "class_target_multiplicity": { "enum": ["0..1", "1", "0..*", "1..*"] }
122
+ }
123
+ },
124
+ "comment": {
125
+ "type": "object",
126
+ "additionalProperties": false,
127
+ "required": ["comment_id", "safe_token", "text"],
128
+ "properties": {
129
+ "comment_id": { "type": "string", "minLength": 1 },
130
+ "safe_token": { "$ref": "#/$defs/safe_token" },
131
+ "text": { "$ref": "#/$defs/sanitized_comment" }
132
+ }
133
+ },
134
+ "cardinality": { "enum": ["0..1", "1..1", "0..many", "1..many"] }
135
+ }
136
+ }