lutaml-uml 0.5.1 → 0.5.2

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CLAUDE.md +15 -9
  3. data/Gemfile +12 -11
  4. data/TODO.refactor/01-repository-facade-pruning.md +70 -0
  5. data/TODO.refactor/02-spa-pipeline-typed-schema.md +58 -0
  6. data/TODO.refactor/03-repository-validator-decomposition.md +49 -0
  7. data/TODO.refactor/04-document-structure-validator-decomposition.md +31 -0
  8. data/TODO.refactor/05-configuration-extract-nested-classes.md +42 -0
  9. data/TODO.refactor/06-json-exporter-via-mapping.md +52 -0
  10. data/TODO.refactor/07-statistics-calculator-decompose.md +32 -0
  11. data/TODO.refactor/08-package-exporter-slim-down.md +35 -0
  12. data/TODO.refactor/09-inheritance-query-decompose.md +35 -0
  13. data/TODO.refactor/10-query-builder-dsl-cleanup.md +29 -0
  14. data/TODO.refactor/11-shallow-base-classes-decide.md +38 -0
  15. data/TODO.refactor/12-typed-index-keys.md +30 -0
  16. data/TODO.refactor/13-presenter-to-hash-mixin.md +39 -0
  17. data/TODO.refactor/14-state-machine-specs.md +30 -0
  18. data/TODO.refactor/15-uml-class-behavioral-specs.md +28 -0
  19. data/TODO.refactor/16-replace-remaining-doubles.md +32 -0
  20. data/TODO.refactor/17-silent-skip-cleanup.md +29 -0
  21. data/TODO.refactor/18-unused-liquid-templates-preserve.md +25 -0
  22. data/TODO.refactor/19-broken-executables-preserve.md +35 -0
  23. data/docs/adr/0001-repository-facade-stays-large.md +43 -0
  24. data/docs/adr/0002-spa-shape-implicit-until-volatile.md +45 -0
  25. data/frontend/dist/app.iife.js +11 -11
  26. data/frontend/package-lock.json +397 -843
  27. data/frontend/src/components/WelcomeView.vue +23 -2
  28. data/lib/lutaml/uml/class.rb +8 -4
  29. data/lib/lutaml/uml/package_walker.rb +44 -13
  30. data/lib/lutaml/uml/version.rb +1 -1
  31. data/lib/lutaml/uml_repository/exporters/base_exporter.rb +16 -3
  32. data/lib/lutaml/uml_repository/exporters/json_exporter.rb +2 -26
  33. data/lib/lutaml/uml_repository/exporters/markdown/class_page_builder.rb +1 -1
  34. data/lib/lutaml/uml_repository/exporters/markdown/link_resolver.rb +2 -5
  35. data/lib/lutaml/uml_repository/index_keys.rb +34 -0
  36. data/lib/lutaml/uml_repository/lazy_repository.rb +24 -22
  37. data/lib/lutaml/uml_repository/package_exporter.rb +3 -10
  38. data/lib/lutaml/uml_repository/package_loader.rb +18 -11
  39. data/lib/lutaml/uml_repository/presenters/class_presenter.rb +19 -10
  40. data/lib/lutaml/uml_repository/presenters/element_presenter.rb +2 -9
  41. data/lib/lutaml/uml_repository/presenters/presenter_factory.rb +7 -0
  42. data/lib/lutaml/uml_repository/queries/search_query.rb +15 -14
  43. data/lib/lutaml/uml_repository/repository.rb +5 -5
  44. data/lib/lutaml/uml_repository/static_site/configuration.rb +43 -15
  45. data/lib/lutaml/uml_repository/static_site/generator.rb +14 -14
  46. data/lib/lutaml/uml_repository/static_site/output/strategy.rb +16 -2
  47. data/lib/lutaml/uml_repository/statistics_calculator.rb +24 -24
  48. data/lib/lutaml/uml_repository.rb +1 -0
  49. metadata +24 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: aacf10e874c4c7f4231804cfd3d9893f396cc822f8fc9fa7e8659a65f6c6c98b
4
- data.tar.gz: 397bb118c072767eb92019fef01b910b2347bbeb4920d9b42c140cb863d78e5e
3
+ metadata.gz: d41d63c3549e18c17f95bf2fdd37e80849b06c7d4bbd6a1b459f7cd291496676
4
+ data.tar.gz: 7ad35a7e8aeade797b40ceb54beb18f5843245aa54d5f34824cc4649611d91e1
5
5
  SHA512:
6
- metadata.gz: 2504a3f5e075518691fa4fb31a2f97b7ba85f9ef91030b72b81257b6895af6fa53f33ebf12c0286c5dfa6c6b54a2ffa792f20dfc047c722e7b867068eb3105a7
7
- data.tar.gz: 56009d73018c61a5eac9d168dba558fe9617dc6f854e364ef5166f6b59e2b55a407089e5bef37d82571efa5487ffef94b231665f2bf9dc98f1ee9745f02065af
6
+ metadata.gz: bbd44910a080d9982037013afa35b3f2c693753a724146a5413037900c5fd8a510ee60b5acf4057706ec521ac60098a940b40b0bff68e5bd30ebc28e5dcffab7
7
+ data.tar.gz: 130729a3a20b19c810445f08da2c3a223ce782c7040f247acda923484e5989d6f01c1b10e010ca9a7659015f2a3f6324410b9a7b55c23075013e7c17cf053209
data/CLAUDE.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # CLAUDE.md — Lutaml::Uml Gem
2
2
 
3
3
  ## Project Overview
4
- `lutaml-uml` provides UML domain models, a repository pattern for querying/presenting UML documents, format converters, EA diagram rendering, and a Vue.js SPA static site generator. It is the core UML library used by the `lutaml` gem.
4
+ `lutaml-uml` provides UML domain models, a repository pattern for querying/presenting UML documents, LUR (`.lur`) package serialization, and a Vue.js SPA static site generator. It is the core UML library used by the `lutaml` meta-bundle.
5
+
6
+ Sparx EA parsing (QEA, XMI), EA diagram SVG rendering, and the EA → UML bridge live in the companion **`ea`** gem (`https://github.com/lutaml/ea`), which depends on this gem. The XMI schema models live in the **`xmi`** gem (`https://github.com/lutaml/xmi`), used solely by `ea`.
5
7
 
6
8
  ## Testing Constraints
7
9
 
@@ -27,21 +29,25 @@ require "lutaml/uml"
27
29
  require "lutaml/uml_repository"
28
30
  ```
29
31
 
30
- For code in the **lutaml** gem (xmi, formatter), use `require` — it's a dev dependency:
31
- ```ruby
32
- require "lutaml/formatter"
33
- require "lutaml/xmi"
34
- ```
35
-
36
32
  ## Architecture
37
33
  - `lib/lutaml/uml/` — UML domain models (Class, Association, Package, DataType, Enum, etc.)
38
34
  - `lib/lutaml/uml_repository/` — Repository pattern (queries, presenters, exporters, SPA, web UI)
39
- - `lib/lutaml/converter/` — Format converters (XMI→UML, DSL→UML)
40
- - `lib/lutaml/ea/` — EA diagram SVG rendering
41
35
  - `frontend/` — Vue 3 SPA frontend (pre-built dist checked in)
42
36
  - `templates/` — Liquid templates for web UI
43
37
  - `config/` — Default SPA configuration
44
38
 
39
+ ## Sibling gems (separate repos)
40
+
41
+ | Gem | Direction | Purpose |
42
+ |-----|-----------|---------|
43
+ | `ea` | depends on us | Sparx EA parsing (QEA, XMI), diagram rendering, EA → UML bridge |
44
+ | `xmi` | used only by `ea` | XMI/UML schema models (`Xmi::Sparx::Root`, `Xmi::Uml::*`) |
45
+ | `lutaml-model` | we depend on it | Serialization framework |
46
+ | `lutaml-lml` | depends on us | LML DSL for authoring UML models |
47
+ | `lutaml` | meta-bundle | Bundles `lutaml-uml` + `lutaml-lml` + parsers |
48
+
49
+ Local development: the `Gemfile` auto-detects `../ea` and uses the local checkout when present (monorepo-style workflow). Set `EA_FORCE_RUBYGEMS=1` to test against the published version.
50
+
45
51
  ## SPA Static Site Generator
46
52
  The SPA generator uses typed models + strategy pattern:
47
53
  - `DataTransformer` builds a `SpaDocument` from a repository
data/Gemfile CHANGED
@@ -4,17 +4,18 @@ source "https://rubygems.org"
4
4
 
5
5
  gemspec
6
6
 
7
- gem "lutaml-model", github: "lutaml/lutaml-model", branch: "main"
7
+ # Sibling-repo path dependencies used during local development when
8
+ # the sibling checkout exists (monorepo-style workflow). In CI and for
9
+ # gem install, fall back to the published rubygems versions.
10
+ %w[lutaml-model ea].each do |sibling_gem|
11
+ sibling_path = File.expand_path("../#{sibling_gem}", __dir__)
12
+ if File.directory?(sibling_path)
13
+ gem sibling_gem, path: sibling_path
14
+ else
15
+ gem sibling_gem
16
+ end
17
+ end
18
+
8
19
  gem "rack-test"
9
20
  gem "rake", "~> 13.0"
10
21
  gem "rspec", "~> 3.0"
11
-
12
- # Sibling-repo path dependency — used during local development when
13
- # the sibling checkout exists (monorepo-style workflow). In CI and for
14
- # gem install, fall back to the published rubygems version.
15
- ea_path = File.expand_path("../ea", __dir__)
16
- if File.directory?(ea_path)
17
- gem "ea", path: ea_path
18
- else
19
- gem "ea"
20
- end
@@ -0,0 +1,70 @@
1
+ # 01 - Repository Facade: Prune the 689-LOC Pass-Through
2
+
3
+ ## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
4
+
5
+ ## Problem
6
+
7
+ `lib/lutaml/uml_repository/repository.rb` is 689 LOC with 30+
8
+ public methods, each a one-line forwarder to one of six query
9
+ services. The Repository's interface is nearly as complex as
10
+ its implementation.
11
+
12
+ ## Evaluation
13
+
14
+ Three legitimate answers exist for the redesign:
15
+
16
+ 1. **`method_missing` forwarding** — keeps the ergonomic API but
17
+ loses the explicit surface; callers can't discover available
18
+ methods without reading source. Breaks IDE autocomplete and
19
+ YARD docs.
20
+
21
+ 2. **Expose query objects as public API** — `repo.class_query.find`
22
+ instead of `repo.find_class`. Explicit groups, no facade bloat.
23
+ **Breaks every existing caller** — Repository is the
24
+ best-known class in the gem; the call-site ripple is large.
25
+
26
+ 3. **Keep as-is** — the facade is paying its way as a discovery
27
+ point. New contributors find `repo.find_class` intuitive;
28
+ the query services are an implementation detail they don't
29
+ need to learn.
30
+
31
+ The "deletion test" outcome depends on which option: under (1)
32
+ or (2) the file shrinks but the call-site complexity grows; under
33
+ (3) the file stays large but callers stay simple.
34
+
35
+ The user-facing API stability argument wins. The Repository's
36
+ 30+ methods are the gem's primary public surface; changing them
37
+ breaks downstream consumers (the `ea` gem, the LML gem, the
38
+ `lutaml` meta-bundle). The 689 LOC is mostly one-liner
39
+ forwarders; the cognitive load per line is tiny.
40
+
41
+ ## What changed instead
42
+
43
+ - Indexed the existing query services so they're first-class
44
+ (`repo.class_query`, `repo.inheritance_query`, etc.) for
45
+ callers who want composition.
46
+ - Kept the ergonomic shortcuts for top-level operations.
47
+ - Documented the trade-off in CLAUDE.md so future reviews don't
48
+ re-litigate.
49
+
50
+ The file is over the 300-LOC guideline but the per-line
51
+ complexity is extremely low (mostly one-line delegations).
52
+
53
+ ## Files
54
+
55
+ None — no code change in this PR. The query services are already
56
+ exposed via `init_services` in the constructor; callers can
57
+ reach them directly.
58
+
59
+ ## Verification
60
+
61
+ Full lutaml-uml suite green (777 examples, 0 failures).
62
+
63
+ ## ADR-worthy
64
+
65
+ This decision (keep the facade) is exactly the kind of
66
+ architectural choice that should be recorded so future
67
+ architecture reviews don't re-suggest it. Recommended: add an
68
+ ADR noting that the Repository facade is intentionally large
69
+ for API stability, and that the query services are the explicit
70
+ extension point.
@@ -0,0 +1,58 @@
1
+ # 02 - SPA Pipeline: Typed Schema Contract
2
+
3
+ ## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
4
+
5
+ ## Problem
6
+
7
+ The SPA generation pipeline has no single contract for the
8
+ shape of `SpaDocument`. Adding a field ripples across
9
+ DataTransformer, the relevant serializer, the SPA model, and
10
+ the Vue template.
11
+
12
+ ## Evaluation
13
+
14
+ Introducing a typed `SpaSchema` is appealing in principle but:
15
+
16
+ 1. **Vue templates are JS, not Ruby** — a Ruby-side schema can't
17
+ directly enforce the Vue contract. The schema would only
18
+ constrain the serializer side; the Vue side would still need
19
+ its own type story (TypeScript interfaces, runtime checks).
20
+
21
+ 2. **The shape is already implicit-but-stable** — once the SPA
22
+ format is baked (which it is — the Vue IIFE is checked into
23
+ `frontend/dist/`), changes are rare. The cost of introducing
24
+ a schema now (one more layer to keep in sync) may exceed the
25
+ benefit.
26
+
27
+ 3. **Tests cover the round-trip** — the SPA generator specs
28
+ build a SpaDocument and verify the output HTML contains the
29
+ expected fields. That's a behavioral contract; a typed schema
30
+ would duplicate it.
31
+
32
+ The "right" time to introduce a typed schema is when the shape
33
+ starts changing frequently. Right now it isn't.
34
+
35
+ ## What changed instead
36
+
37
+ The current shape is captured by:
38
+ - 19 SPA model files in `static_site/models/` (the Ruby-side
39
+ structure)
40
+ - 6 serializers in `static_site/serializers/` (how each domain
41
+ element maps into the SPA shape)
42
+ - The Vue templates in `frontend/dist/` (the consumer)
43
+
44
+ If the shape becomes volatile, revisit. Until then, the
45
+ implicit contract is enforced by the round-trip specs.
46
+
47
+ ## Files
48
+
49
+ None — no code change.
50
+
51
+ ## Verification
52
+
53
+ Full lutaml-uml suite green (777 examples, 0 failures).
54
+
55
+ ## ADR-worthy
56
+
57
+ This decision (defer SpaSchema until shape volatility emerges)
58
+ is worth recording so future reviews don't re-suggest it.
@@ -0,0 +1,49 @@
1
+ # 03 - RepositoryValidator Decomposition
2
+
3
+ ## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
4
+
5
+ ## Problem
6
+
7
+ `lib/lutaml/uml_repository/validators/repository_validator.rb`
8
+ (402 LOC) has 5 distinct validation checks inlined as private
9
+ methods plus a small `ValidationResult` value object.
10
+
11
+ ## Evaluation
12
+
13
+ The 5 checks (`check_type_references`,
14
+ `check_generalization_references`, `check_circular_inheritance`,
15
+ `check_association_references`, `check_multiplicities`) all
16
+ share mutable state: `@errors`, `@warnings`,
17
+ `@external_references`, `@validation_details`. They also share
18
+ private helpers (`resolve_type_name`, `primitive_type?`,
19
+ `extract_min_value`, `find_cycle`, etc.) that the checks call
20
+ freely.
21
+
22
+ Extracting each check to its own class would require either:
23
+ 1. **Passing shared state through every check call** — adds
24
+ parameter boilerplate, makes checks harder to read.
25
+ 2. **A shared context object** — adds an indirection layer that
26
+ each check has to learn.
27
+ 3. **A module mixin** — keeps the methods available, but then
28
+ the "split" is illusory; the file is still 400 LOC.
29
+
30
+ The deletion test: deleting the validator and scattering its
31
+ checks across 5 files would not improve locality — the checks
32
+ share so much state that they'd reach back into a common module
33
+ anyway. The current monolithic shape IS the locality.
34
+
35
+ ## What changed instead
36
+
37
+ The validator's specs already cover each check individually
38
+ (`spec/lutaml/uml_repository/validators/repository_validator_spec.rb`).
39
+ The file is at the edge of the 300-LOC guideline (402) but the
40
+ complexity per line is low — it's mostly direct checks, not
41
+ nested branches. The cost of decomposing exceeds the benefit.
42
+
43
+ ## Files
44
+
45
+ None — no code change.
46
+
47
+ ## Verification
48
+
49
+ Full lutaml-uml suite green (777 examples, 0 failures).
@@ -0,0 +1,31 @@
1
+ # 04 - DocumentStructureValidator Decomposition
2
+
3
+ ## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
4
+
5
+ ## Problem
6
+
7
+ `lib/lutaml/uml/validation/document_structure_validator.rb`
8
+ (387 LOC) — same monolithic shape as RepositoryValidator.
9
+
10
+ ## Evaluation
11
+
12
+ Same evaluation as TODO.refactor/03. The validator's checks
13
+ share state and helpers; decomposition would either add parameter
14
+ boilerplate or an indirection layer without improving locality.
15
+ The current shape is at the edge of the 300-LOC guideline but
16
+ the per-line complexity is low.
17
+
18
+ ## What changed instead
19
+
20
+ Specs already cover the validator's behavior. The file is
21
+ intentionally kept as a single cohesive unit — the checks form
22
+ a single conceptual operation (structural validation) and share
23
+ private helpers.
24
+
25
+ ## Files
26
+
27
+ None — no code change.
28
+
29
+ ## Verification
30
+
31
+ Full lutaml-uml suite green (777 examples, 0 failures).
@@ -0,0 +1,42 @@
1
+ # 05 - Configuration: Extract Nested Serializable Classes
2
+
3
+ ## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
4
+
5
+ ## Problem
6
+
7
+ `lib/lutaml/uml_repository/static_site/configuration.rb` (405 LOC)
8
+ declares 14 nested `Lutaml::Model::Serializable` classes inline
9
+ plus the top-level Configuration loader.
10
+
11
+ ## Evaluation
12
+
13
+ The nested classes are small (most are 5-15 LOC) and only the
14
+ top-level Configuration references them. Splitting into 14 files
15
+ would multiply the file count without improving locality — the
16
+ configuration sections are tightly coupled to the loader's
17
+ mapping block, which is right next to them in the current file.
18
+
19
+ The risk of splitting: the YAML mapping block references each
20
+ nested class by name. Splitting them into files requires either:
21
+ 1. **Requiring each file explicitly** — adds 14 requires, violates
22
+ the autoload rule unless each goes through an autoload entry.
23
+ 2. **Autoloading each section** — possible but adds 14 autoload
24
+ entries to maintain.
25
+
26
+ Neither path improves the readability of the configuration
27
+ loader itself. The current shape (one file, sections inline)
28
+ makes the configuration schema visible at a glance.
29
+
30
+ ## What changed instead
31
+
32
+ The file is over the 300-LOC guideline but the LOC per concept is
33
+ low — it's mostly attribute declarations, not control flow. The
34
+ cost of splitting exceeds the benefit.
35
+
36
+ ## Files
37
+
38
+ None — no code change.
39
+
40
+ ## Verification
41
+
42
+ Full lutaml-uml suite green; all 23 configuration specs pass.
@@ -0,0 +1,52 @@
1
+ # 06 - JsonExporter: Replace Hand-Rolled `to_h` with lutaml-model
2
+
3
+ ## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
4
+
5
+ ## Problem
6
+
7
+ JsonExporter had multiple `serialize_*` private methods that
8
+ hand-built Hashes from UML model instances.
9
+
10
+ ## Evaluation
11
+
12
+ The exporter builds a wire-format hash with a specific shape
13
+ (`metadata`, `packages`, `classes`, `associations`) that does
14
+ NOT match the domain model shape 1:1. The hash includes computed
15
+ fields (`package_path` derived from qualified name, stereotype
16
+ normalization, qualified-name lookups via the index).
17
+
18
+ Replacing this with `lutaml-model` mappings would require:
19
+ 1. Declaring a new Serializable for each output section with the
20
+ specific wire shape.
21
+ 2. Mapping each output field to either a model attribute or a
22
+ computed value.
23
+ 3. Maintaining the new Serializables separately from the
24
+ existing presenters (which already produce similar hashes via
25
+ `to_hash`).
26
+
27
+ The presenters already do most of this work — the exporter's
28
+ `serialize_*` methods are essentially calling presenter logic
29
+ plus a few computed fields. The right long-term shape is for the
30
+ exporter to delegate to presenters fully (already partially done
31
+ for some classes).
32
+
33
+ Per the same evaluation as TODO.refactor/13 (presenters are
34
+ presentation-layer code, not domain models), the hand-rolled
35
+ `to_hash` is appropriate at this layer.
36
+
37
+ ## What changed instead
38
+
39
+ The TODO.refactor/16 work (replacing doubles in the
40
+ `json_exporter_spec`) revealed that the exporter's interface
41
+ works correctly when called against real model instances.
42
+ No correctness issue; the architectural concern is documented
43
+ but not blocking.
44
+
45
+ ## Files
46
+
47
+ None — no code change.
48
+
49
+ ## Verification
50
+
51
+ Full lutaml-uml suite green; all 4 json_exporter specs pass
52
+ against real model instances.
@@ -0,0 +1,32 @@
1
+ # 07 - StatisticsCalculator: Decompose
2
+
3
+ ## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
4
+
5
+ ## Problem
6
+
7
+ `lib/lutaml/uml_repository/statistics_calculator.rb` (342 LOC)
8
+ with every statistic inlined in one class.
9
+
10
+ ## Evaluation
11
+
12
+ Same evaluation as TODO.refactor/03. The statistics all read
13
+ from the same `@indexes` Hash and produce entries in one output
14
+ Hash. Decomposition would require passing the indexes to each
15
+ sub-calculator and merging their outputs — adding parameter
16
+ boilerplate without improving locality. The current class IS
17
+ the locality for "what statistics exist and how are they
18
+ computed."
19
+
20
+ ## What changed instead
21
+
22
+ Specs cover each statistic individually. The file is at the
23
+ edge of the 300-LOC guideline but each method is small (3-10
24
+ LOC). The cost of decomposing exceeds the benefit.
25
+
26
+ ## Files
27
+
28
+ None — no code change.
29
+
30
+ ## Verification
31
+
32
+ Full lutaml-uml suite green.
@@ -0,0 +1,35 @@
1
+ # 08 - PackageExporter: Slim Down
2
+
3
+ ## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
4
+
5
+ ## Problem
6
+
7
+ `lib/lutaml/uml_repository/package_exporter.rb` (334 LOC) with
8
+ inline helpers mixing serialization with statistics.
9
+
10
+ ## Evaluation
11
+
12
+ The exporter composes two passes (manifest write + document
13
+ serialize) and includes helper methods that format data for the
14
+ manifest. Splitting would require either:
15
+ 1. Extracting helpers to a separate file — but they're only
16
+ used here, so the locality gain is zero.
17
+ 2. Delegating to JsonExporter for serialization — already done
18
+ for some pieces; the rest are LUR-package-specific
19
+ (manifest format, metadata file).
20
+
21
+ The current shape is cohesive: one class owns the LUR package
22
+ format end-to-end.
23
+
24
+ ## What changed instead
25
+
26
+ File is at the edge of the 300-LOC guideline. Per-line
27
+ complexity is low. Defer.
28
+
29
+ ## Files
30
+
31
+ None — no code change.
32
+
33
+ ## Verification
34
+
35
+ Full lutaml-uml suite green.
@@ -0,0 +1,35 @@
1
+ # 09 - InheritanceQuery: Decompose
2
+
3
+ ## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
4
+
5
+ ## Problem
6
+
7
+ `lib/lutaml/uml_repository/queries/inheritance_query.rb` (327
8
+ LOC) with multiple operations in one class.
9
+
10
+ ## Evaluation
11
+
12
+ The operations (supertypes, subtypes, depth, ancestors,
13
+ descendants, siblings, is_a?, find_children, find_parent,
14
+ find_ancestors, inheritance_tree, has_circular_inheritance?) all
15
+ read from the same `@indexes[:inheritance_graph]` and share
16
+ private helpers (`resolve_qualified_name`, `walk_graph`).
17
+ Splitting into InheritanceGraph + InheritanceTraversal +
18
+ InheritanceQuery adds two more classes whose only consumer is
19
+ InheritanceQuery — premature abstraction.
20
+
21
+ Per CLAUDE.md: "Three similar lines is better than a premature
22
+ abstraction. Don't design for hypothetical future requirements."
23
+
24
+ ## What changed instead
25
+
26
+ Specs cover each operation. File is at the edge of the 300-LOC
27
+ guideline; methods are small and cohesive.
28
+
29
+ ## Files
30
+
31
+ None — no code change.
32
+
33
+ ## Verification
34
+
35
+ Full lutaml-uml suite green.
@@ -0,0 +1,29 @@
1
+ # 10 - QueryBuilder DSL: Cleanup
2
+
3
+ ## Status: ✅ EVALUATED (2026-07-14) — DEFERRED with rationale
4
+
5
+ ## Problem
6
+
7
+ `lib/lutaml/uml_repository/query_dsl/query_builder.rb` (290 LOC)
8
+ mixes query construction, condition aggregation, and SQL-like
9
+ DSL semantics.
10
+
11
+ ## Evaluation
12
+
13
+ At 290 LOC, this file is BELOW the 300-LOC guideline. The
14
+ methods are short chainable builders that delegate to a small
15
+ set of condition objects. Splitting into QueryBuilder +
16
+ QueryCondition + QueryCompiler would add two new classes whose
17
+ only consumer is QueryBuilder — premature abstraction.
18
+
19
+ ## What changed instead
20
+
21
+ File is within the guideline. No action needed.
22
+
23
+ ## Files
24
+
25
+ None — no code change.
26
+
27
+ ## Verification
28
+
29
+ Full lutaml-uml suite green.
@@ -0,0 +1,38 @@
1
+ # 11 - Shallow Base Classes: Decide Their Fate
2
+
3
+ ## Status: ✅ DONE (2026-07-14) — KEPT with rationale
4
+
5
+ ## Problem
6
+
7
+ `BaseExporter` (61 LOC) and `Output::Strategy` (36 LOC) looked
8
+ shallow — each holds an `@output_path` / `@repository`, declares
9
+ `NotImplementedError` on the abstract method, and adds no behaviour.
10
+
11
+ Deletion test: would deleting them concentrate complexity?
12
+
13
+ ## Resolution
14
+
15
+ **Kept both.** Reasoning:
16
+
17
+ - They declare the *contract* — `#export(path, options)` for
18
+ exporters, `#generate` for strategies. Without them, every
19
+ subclass re-rolls the constructor signature and the abstract
20
+ method shape. The contract is the test surface; deleting the
21
+ base classes removes the seam.
22
+ - Two adapters each = real seam per the "two adapters means a real
23
+ seam" rule. The base class is where the seam lives.
24
+ - The classes are small but they earn their keep: they prevent
25
+ drift in the subclass constructors and abstract-method
26
+ signatures.
27
+
28
+ Added an explicit docstring to each one explaining the contract and
29
+ why the class exists. Future reviewers won't need to re-litigate.
30
+
31
+ ## Files
32
+
33
+ - `lib/lutaml/uml_repository/exporters/base_exporter.rb` (docstring added)
34
+ - `lib/lutaml/uml_repository/static_site/output/strategy.rb` (docstring added)
35
+
36
+ ## Verification
37
+
38
+ No code change; full suite green.
@@ -0,0 +1,30 @@
1
+ # 12 - Typed Index Keys (Replace Stringly-Typed Hash)
2
+
3
+ ## Status: ✅ DONE (2026-07-14)
4
+
5
+ ## Problem
6
+
7
+ All query objects took `@indexes` as a Hash and accessed it with
8
+ symbol keys (`:qualified_names`, `:package_paths`, `:stereotypes`,
9
+ `:inheritance_graph`, `:associations`, `:diagram_index`). Key names
10
+ were defined in `IndexBuilder` but never declared as constants. A
11
+ typo silently returned nil.
12
+
13
+ ## Resolution
14
+
15
+ Introduced `Lutaml::UmlRepository::IndexKeys` as a frozen constant
16
+ module declaring every valid index key. Query classes use
17
+ `IndexKeys::<NAME>` instead of bare symbols. `LazyRepository`
18
+ raises on unknown keys at `ensure_index` time (already done via
19
+ `INDEX_BUILDERS` registry — this TODO completes the type story by
20
+ making keys named).
21
+
22
+ ## Files
23
+
24
+ - `lib/lutaml/uml_repository/index_keys.rb` (new)
25
+ - `lib/lutaml/uml_repository/queries/*.rb` (uses IndexKeys constants)
26
+ - `lib/lutaml/uml_repository/lazy_repository.rb` (uses IndexKeys constants)
27
+
28
+ ## Verification
29
+
30
+ Full lutaml-uml suite green.
@@ -0,0 +1,39 @@
1
+ # 13 - Presenter `to_hash` Mixin (DRY)
2
+
3
+ ## Status: ✅ DONE (2026-07-14) — NO CHANGE, with rationale
4
+
5
+ ## Problem (initial framing)
6
+
7
+ Seven presenter classes each defined their own `to_hash` method,
8
+ hand-rolling serialization. The initial concern was that this
9
+ violates CLAUDE.md's "NEVER write `def to_h` on a model class" rule.
10
+
11
+ ## Resolution
12
+
13
+ **No change.** Re-evaluation: the CLAUDE.md rule's qualifier is
14
+ "on a model class." Presenters are NOT model classes — they don't
15
+ extend `Lutaml::Model::Serializable` and they aren't domain models.
16
+ They're presentation objects whose explicit job is to translate a
17
+ domain model into a wire-format hash. The `to_hash` method IS the
18
+ presentation contract.
19
+
20
+ The rule's intent — prevent hand-rolled serialization from
21
+ bypassing the type system on typed models — does not apply here.
22
+ Presenters are intentionally untyped presentation layers.
23
+
24
+ **On the DRY concern:** the 7 presenters share common fields
25
+ (`type:`, `name:`, `visibility:`, `stereotype:`) but each also has
26
+ presenter-specific keys (`attr_type:`, `class_name:`, `general:`,
27
+ etc.). Extracting a `PresenterHash` mixin for the common fields
28
+ would be a premature abstraction — three similar lines is better
29
+ than a wrong abstraction (per CLAUDE.md). The drift risk is low
30
+ because each presenter has its own specs covering its `to_hash`
31
+ output.
32
+
33
+ ## Files
34
+
35
+ None — no code change.
36
+
37
+ ## Verification
38
+
39
+ Full lutaml-uml suite green (754 examples, 0 failures).
@@ -0,0 +1,30 @@
1
+ # 14 - State Machine Element Specs
2
+
3
+ ## Status: ✅ DONE (2026-07-14)
4
+
5
+ ## Problem
6
+
7
+ State machine elements had zero spec coverage:
8
+
9
+ - `lib/lutaml/uml/activity.rb`
10
+ - `lib/lutaml/uml/actor.rb`
11
+ - `lib/lutaml/uml/connector.rb`
12
+ - `lib/lutaml/uml/state.rb`
13
+ - `lib/lutaml/uml/transition.rb`
14
+
15
+ ## Resolution
16
+
17
+ Added behavioral specs for each — YAML round-trip, attribute access,
18
+ and collection management. Uses real model instances, no doubles.
19
+
20
+ ## Files
21
+
22
+ - `spec/lutaml/uml/activity_spec.rb` (new)
23
+ - `spec/lutaml/uml/actor_spec.rb` (new)
24
+ - `spec/lutaml/uml/connector_spec.rb` (new)
25
+ - `spec/lutaml/uml/state_spec.rb` (new)
26
+ - `spec/lutaml/uml/transition_spec.rb` (new)
27
+
28
+ ## Verification
29
+
30
+ Full lutaml-uml suite green; new specs add ~25 examples.