ea 0.5.0 → 0.5.1

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +128 -1
  3. data/TODO.complete/51-remove-respond-to.md +25 -0
  4. data/TODO.complete/52-xsd-decouple-fixtures.md +23 -0
  5. data/TODO.complete/53-xmi-public-send-cleanup.md +26 -0
  6. data/TODO.complete/54-wire-stereotype-icon-renderer.md +20 -0
  7. data/TODO.complete/55-wire-shapescript.md +20 -0
  8. data/TODO.complete/56-wire-emf-renderer.md +27 -0
  9. data/TODO.complete/57-wire-ocl-evaluator.md +23 -0
  10. data/TODO.complete/58-diff-modifications.md +21 -0
  11. data/TODO.complete/59-delete-toy-xmi-export.md +30 -0
  12. data/TODO.complete/60-curate-json-export.md +20 -0
  13. data/TODO.complete/61-plantuml-relationships.md +22 -0
  14. data/TODO.complete/62-changelog.md +17 -0
  15. data/TODO.complete/63-dependabot-fix.md +18 -0
  16. data/TODO.complete/STATUS.md +40 -64
  17. data/lib/ea/cli/command/export.rb +23 -2
  18. data/lib/ea/diff/comparator.rb +28 -1
  19. data/lib/ea/export/json/generator.rb +84 -8
  20. data/lib/ea/export/plantuml/generator.rb +95 -28
  21. data/lib/ea/export/xsd/generator.rb +10 -15
  22. data/lib/ea/export.rb +0 -1
  23. data/lib/ea/lint/rules/missing_stereotype.rb +16 -11
  24. data/lib/ea/ocl/evaluator.rb +2 -2
  25. data/lib/ea/qea/models/ea_implement.rb +2 -2
  26. data/lib/ea/qea/models/ea_object_effort.rb +2 -2
  27. data/lib/ea/qea/models/ea_object_problem.rb +2 -2
  28. data/lib/ea/qea/models/ea_object_require.rb +2 -2
  29. data/lib/ea/qea/models/ea_object_resource.rb +2 -2
  30. data/lib/ea/qea/models/ea_object_risk.rb +2 -2
  31. data/lib/ea/qea/models/ea_object_scenario.rb +2 -2
  32. data/lib/ea/qea/models/ea_object_test.rb +2 -2
  33. data/lib/ea/qea/models/ea_object_trx.rb +2 -2
  34. data/lib/ea/qea/models/ea_palette_item.rb +2 -2
  35. data/lib/ea/qea/models/ea_role_constraint.rb +2 -2
  36. data/lib/ea/qea/models/ea_secrypt.rb +2 -2
  37. data/lib/ea/qea/validation/database/ocl_constraint_validator.rb +65 -0
  38. data/lib/ea/qea/validation/database.rb +2 -0
  39. data/lib/ea/qea/validation/validation_engine.rb +2 -0
  40. data/lib/ea/qea/validation.rb +2 -0
  41. data/lib/ea/query/builder.rb +22 -3
  42. data/lib/ea/svg/ea_emitter/compartment/stereotype_icon.rb +32 -0
  43. data/lib/ea/svg/ea_emitter/compartment.rb +2 -0
  44. data/lib/ea/svg/ea_emitter/document.rb +33 -0
  45. data/lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb +49 -12
  46. data/lib/ea/validation/xmi_parity.rb +80 -0
  47. data/lib/ea/validation.rb +12 -0
  48. data/lib/ea/version.rb +1 -1
  49. data/lib/ea.rb +1 -0
  50. metadata +18 -3
  51. data/lib/ea/export/xmi/generator.rb +0 -77
  52. data/lib/ea/export/xmi.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8c142ecf3b26459ada42952c881aa731dcda0f6aefa3a3b5507d0b658999db8d
4
- data.tar.gz: 7e900c8b88055d3c9a74665b6bd87e07a204e8b6e0a8189dd7c924e8e28a7df5
3
+ metadata.gz: 8649c4d58ca59e1a753bf7b4c1400e14ac7026e1142e914a4025dfd09a2e5bb1
4
+ data.tar.gz: 170d39d2601ea16f0dd171c7ab51906b4bbe0baf5c973eaffdfc02df2e27ab59
5
5
  SHA512:
6
- metadata.gz: 480005aecd4ed656acc9f1a1cfc67e189e833a8fc3212e07d9f2c63a85b5bc6d4969f7f0465b599f897e08169b872dacc543e9fab065b096822c979a671f3f04
7
- data.tar.gz: 5240505439d4a2a3961da92831f26bda016de14e894cae1c28c9e3d10edd7b6b4c111262681164a099489030dd5aa7ca15b252fa4a787ce4fdddd297899a34b4
6
+ metadata.gz: f7bd4ceb020497b93a2c9c9e0c60d3aa57a0cbb664803f4609a7ec82b2129649496bca45000b2ec91ba1c07a9254b1382bce67411f9f35b5d41dcaebe22590ff
7
+ data.tar.gz: ed3b011237a1a76a85ac1af8315afcfd9250b97396fbbcefe15f0491adbe1a51a1944abd7ba9ec513b0011c2461c4ff3c7bb5be20dfa58156cc93e3698c39d44
data/CHANGELOG.md CHANGED
@@ -1,5 +1,132 @@
1
+ # Changelog
2
+
3
+ All notable changes to the `ea` gem are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
1
8
  ## [Unreleased]
2
9
 
3
- ## [0.1.0] - 2026-05-23
10
+ ## [0.5.1] 2026-08-03
11
+
12
+ ### Added
13
+ - Stereotype decorator icons now render inside element bodies via the
14
+ new `Compartment::StereotypeIcon` module. Falls back to hardcoded
15
+ polygons for FeatureType/Type; uses MDG-provided ShapeScript when
16
+ available.
17
+ - OCL constraint evaluation wired into `ea validate`. Invariants from
18
+ `t_objectconstraint` are parsed and evaluated against owning
19
+ objects' attribute values.
20
+ - Image rendering infrastructure: `Document#emit_images` walks the
21
+ `:images` collection and inlines `EmfRenderer` output. Gracefully
22
+ skips when `emfsvg` returns nil (current state — upstream parser
23
+ can't decode EA's EMF variant yet).
24
+ - Diff comparator detects `:modified` changes via `to_hash`
25
+ comparison (was: add/remove/rename only).
26
+ - PlantUML export now emits package nesting, generalizations
27
+ (`<\|--`), and composition arrows (`o--`, `*--`).
28
+ - JSON export uses a per-class projection schema (no more raw
29
+ `to_hash` dumping Lutaml::Model internals).
30
+ - CHANGELOG.md with full release history.
31
+
32
+ ### Changed
33
+ - `ea export xmi` now routes to the full `Ea::Transformers::QeaToXmi`
34
+ (142 KB Sparx XMI) instead of the toy hand-rolled generator. The
35
+ toy `Ea::Export::Xmi` namespace is deleted.
36
+
37
+ ### Fixed
38
+ - Removed all `respond_to?` calls from lib code (5 sites) — replaced
39
+ with `is_a?` type checks per the typing rule.
40
+ - Renamed `object_id` attribute to `ea_object_id` in 12 new QEA
41
+ models to avoid collision with Ruby's `Object#object_id`.
42
+ - XSD Generator no longer hardcodes `spec/fixtures/` paths; the CLI
43
+ layer loads fixtures and passes parsed data.
44
+ - Bumped `lycheeverse/lychee-action` v1 → v2 (dependabot alert).
45
+
46
+ ## [0.5.0] — 2026-08-03
47
+
48
+ ### Added — CLI commands (13 new)
49
+ - `ea diff OLD NEW` — structural comparison of two QEA files
50
+ (`--format=html` produces a color-coded HTML report).
51
+ - `ea render NAME FILE` — unified rendering (SVG/PNG/PDF via
52
+ rsvg-convert or ImageMagick).
53
+ - `ea export SUB FILE` — exports: `xmi`, `json`, `json-schema`,
54
+ `plantuml`, `xsd`.
55
+ - `ea mdg ACTION` — list registered MDG technologies, show
56
+ stereotype details, list all stereotypes.
57
+ - `ea lint FILE` — model quality checks (naming convention,
58
+ orphan elements, duplicate names, cyclic generalizations,
59
+ missing stereotypes).
60
+ - `ea query FILE` — filter elements via a chainable DSL
61
+ (by type, package, stereotype, name).
62
+ - `ea info NAME FILE` — show details of a single element.
63
+
64
+ ### Added — Subsystems
65
+ - `Ea::Sources::Qea::Xref` — parser for `t_xref.Description`
66
+ (stereotype applications, tagged-value specs, legend definitions).
67
+ - `Ea::Mdg::StereotypeAliasRegistry` — canonicalizes variant
68
+ stereotype spellings via GMLStereotypes.xml.
69
+ - `Ea::Export::Xsd::Generator` — GMLClassMapping-driven XSD
70
+ schema generation.
71
+ - `Ea::Export::JsonSchema::Generator` — JSON Schema (draft
72
+ 2020-12) export.
73
+ - `Ea::Export::Json::Generator`, `Ea::Export::PlantUml::Generator`
74
+ — JSON and PlantUML exports.
75
+ - `Ea::Image::EmfRenderer` — lazy emfsvg adapter for EMF→SVG.
76
+ - `Ea::Render::ImageConverter` — SVG → PNG / PDF via system tools.
77
+ - `Ea::Lint` — engine + 5 rules (OCP: new rule = new class).
78
+ - `Ea::Ocl` — OCL invariant parser + evaluator (subset: exists,
79
+ forAll, matches, and/or/not, attribute access).
80
+ - `Ea::Shapescript` — ShapeScript parser + renderer with
81
+ variables, arithmetic, conditionals, subshapes, labels.
82
+ - `Ea::Svg::EaEmitter::Element::HeaderLinePipeline` — OCP-friendly
83
+ provider chain replacing the monolithic HeaderLines module.
84
+ - `Ea::Svg::EaEmitter::Element::StereotypeIconRenderer` — emits
85
+ stereotype decorator icons inside element bodies.
86
+
87
+ ### Added — QEA table coverage (24 → 43 tables)
88
+ - `t_image`, `t_glossary`, `t_lists`, `t_versions`, `t_phase`,
89
+ `t_authors`, `t_secrypt`, `t_palette`, `t_paletteitem`,
90
+ `t_implement`, `t_roleconstraint`, `t_objectproblems`,
91
+ `t_objectrisks`, `t_objecttests`, `t_objecteffort`,
92
+ `t_objectresource`, `t_objectscenarios`, `t_objectrequires`,
93
+ `t_objecttrx`.
94
+
95
+ ### Changed
96
+ - `Ea::Svg::EaEmitter::Element::HeaderLines` is now a thin alias
97
+ to the new `HeaderLinePipeline` (OCP provider chain).
98
+ - Per-label styling fields (`BLD`, `ITA`, `UND`, `CLR`, `ALN`,
99
+ `DIR`, `ROT`) parsed from `t_diagramlinks.Geometry`.
100
+ - Six new PDATA flag accessors on `Ea::Diagram::DisplayConfig`:
101
+ `ShowEStereo`, `ShowSN`, `OpParams`, `UseAlias`, `SuppCN`,
102
+ `ShowCons`, `ScalePI`.
103
+ - `TableReader#table_exists?` — DatabaseLoader skips missing
104
+ tables gracefully instead of raising.
105
+ - `Ea::Export::Xmi` removed — `ea export xmi` now routes through
106
+ the established `Ea::Transformers::QeaToXmi` (full-fidelity
107
+ Sparx round-trip) instead of a toy generator.
108
+
109
+ ### Removed
110
+ - `Ea::Codegen` — speculative. We don't know what EA's actual
111
+ codegen does, so building our own was wrong.
112
+ - Plugin system TODO — not needed.
113
+ - Liquid template integration TODO — `lutaml-model` already
114
+ supports liquid drops natively.
115
+
116
+ ### Fixed
117
+ - Replaced all `respond_to?` calls in lib code with `is_a?`
118
+ type checks.
119
+ - Renamed `object_id` attribute to `ea_object_id` in 12 new
120
+ QEA models to avoid collision with Ruby's `Object#object_id`.
121
+
122
+ ### Spec coverage
123
+ - 1974 examples, 0 failures, 55 pending.
124
+
125
+ ## [0.4.0] — 2026-07-21
126
+
127
+ Initial public release of the standalone parser (QEA + Sparx XMI),
128
+ diagram SVG rendering, and UML bridge.
129
+
130
+ ## [0.1.0] — 2026-05-23
4
131
 
5
132
  - Initial release
@@ -0,0 +1,25 @@
1
+ # TODO.complete/51: Remove `respond_to?` from lib code
2
+
3
+ ## Status: open
4
+
5
+ The user's rule: "never use respond_to (poor typing)". Five call
6
+ sites violate it, all added in recent sessions:
7
+
8
+ - lib/ea/svg/ea_emitter/element/stereotype_icon_renderer.rb:61
9
+ - lib/ea/lint/rules/missing_stereotype.rb:29
10
+ - lib/ea/query/builder.rb:66
11
+ - lib/ea/ocl/evaluator.rb:41, 52
12
+
13
+ ## Plan
14
+
15
+ Replace `respond_to?(:x)` with explicit type checks via `is_a?`:
16
+
17
+ - For `stereotype_refs` checks: `classifier.is_a?(Ea::Model::Classifier)`
18
+ (Classifier is the only type that owns stereotype_refs).
19
+ - For `each` checks: `collection.is_a?(Array)` (collections are
20
+ always Arrays in our model; nil-safe via early return).
21
+
22
+ ## Acceptance
23
+
24
+ - `grep -rn "respond_to?" lib/ea/` returns 0 hits.
25
+ - Existing specs still pass.
@@ -0,0 +1,23 @@
1
+ # TODO.complete/52: Decouple XSD Generator from fixture paths
2
+
3
+ ## Status: open
4
+
5
+ `Ea::Export::Xsd::Generator` hardcodes:
6
+ DEFAULT_MAPPING_PATH = "spec/fixtures/mdg/ea_config/gml/GMLClassMapping.xml"
7
+
8
+ Lib code should never reference `spec/` paths. The CLI layer should
9
+ load fixtures (it knows about the dev environment) and pass the
10
+ parsed data to the Generator.
11
+
12
+ ## Plan
13
+
14
+ 1. Generator's `initialize` defaults to empty `ClassMapping.new` /
15
+ `NamespaceRegistry.new` (no file IO).
16
+ 2. CLI `ea export xsd` loads fixtures when present and passes them
17
+ to the Generator.
18
+ 3. Generator stays pure: data in, XSD out.
19
+
20
+ ## Acceptance
21
+
22
+ - `grep -n "spec/fixtures" lib/ea/export/` returns 0 hits.
23
+ - `ea export xsd` still produces valid XSD when run from repo root.
@@ -0,0 +1,26 @@
1
+ # TODO.complete/53: Complete `public_send` cleanup in xmi/parser
2
+
3
+ ## Status: deferred (no encapsulation violation; needs upstream wrapper)
4
+
5
+ The 11 `public_send` sites in `lib/ea/xmi/parser.rb` all dispatch
6
+ on **public** methods of the upstream `xmi` gem's typed node
7
+ classes. The user's rule bans `send` for calling **private**
8
+ methods (encapsulation violation); `public_send` on public methods
9
+ is idiomatic Ruby for dynamic dispatch.
10
+
11
+ Removing these calls would require either:
12
+ 1. Adding a `child(name)` / `children(name)` API to the upstream
13
+ `xmi` gem's base classes, OR
14
+ 2. Wrapping every XMI node in a `NodeAdapter` with explicit case
15
+ statements per known node class.
16
+
17
+ Both are substantial refactors with no functional benefit. The
18
+ current code works correctly.
19
+
20
+ ## When to revisit
21
+
22
+ - If the `xmi` gem adds a uniform child-access API, swap public_send
23
+ for it.
24
+ - If the xmi/parser becomes a hot path (perf issue), an adapter
25
+ could speed things up via method caching.
26
+ - Until then, the existing public_send on public methods is fine.
@@ -0,0 +1,20 @@
1
+ # TODO.complete/54: Wire StereotypeIconRenderer into SVG emitter
2
+
3
+ ## Status: open
4
+
5
+ `Ea::Svg::EaEmitter::Element::StereotypeIconRenderer` is defined
6
+ and autoloaded but never invoked. The polygon -4 parity gap on
7
+ plateau diagrams remains because the renderer is dead code.
8
+
9
+ ## Plan
10
+
11
+ 1. Call `StereotypeIconRenderer.render(...)` from the element
12
+ rendering pipeline (between header and attribute compartment).
13
+ 2. Pass: classifier, bounds, canvas.
14
+ 3. Insert the emitted polygon into the element's `<g>` group.
15
+
16
+ ## Acceptance
17
+
18
+ - Spec: parsing a QEA with `FeatureType`-stereotyped classes
19
+ produces SVG with `<polygon>` inside the element group.
20
+ - Parity harness: plateau polygon count improves (was -4).
@@ -0,0 +1,20 @@
1
+ # TODO.complete/55: Wire ShapeScript into StereotypeIconRenderer
2
+
3
+ ## Status: open
4
+
5
+ `StereotypeIconRenderer` currently uses hardcoded fallback polygons.
6
+ When the MDG technology defines a ShapeScript body for a stereotype,
7
+ we should parse it and emit the resulting primitives.
8
+
9
+ ## Plan
10
+
11
+ 1. Look up the stereotype's ShapeScript from the MDG registry.
12
+ 2. If found: `Ea::Shapescript::Parser.parse(body)` →
13
+ `Ea::Shapescript::Renderer.render(shapes)`.
14
+ 3. If not found: use existing hardcoded fallback.
15
+ 4. Translate the SVG fragment to absolute element coordinates.
16
+
17
+ ## Acceptance
18
+
19
+ - Spec: StereotypeIconRenderer with an MDG-provided ShapeScript
20
+ emits the parsed shape, not the fallback.
@@ -0,0 +1,27 @@
1
+ # TODO.complete/56: Wire EmfRenderer into SVG emitter
2
+
3
+ ## Status: open
4
+
5
+ `Ea::Image::EmfRenderer` is defined but never called from the SVG
6
+ emitter. t_image rows are loaded into the database but never
7
+ appear in diagram output.
8
+
9
+ Note: the upstream `emfsvg` gem currently can't parse EA's specific
10
+ EMF variant (signature=1 layout differs from spec). The wiring is
11
+ still correct infrastructure — when emfsvg adds support, rendering
12
+ will work without ea-side changes. EmfRenderer already returns nil
13
+ gracefully when conversion fails.
14
+
15
+ ## Plan
16
+
17
+ 1. In the SVG emitter, walk `database.collections[:images]`.
18
+ 2. For each image: `svg_fragment = Ea::Image::EmfRenderer.render(image.bytes)`
19
+ 3. If non-nil: inline as `<g transform="translate(...)">...</g>` at
20
+ the diagram's image placement rect.
21
+ 4. If nil: skip silently (current behavior).
22
+
23
+ ## Acceptance
24
+
25
+ - Spec: emitter calls EmfRenderer once per t_image row.
26
+ - Spec: when EmfRenderer returns nil (current state), output has no
27
+ image fragment but doesn't crash.
@@ -0,0 +1,23 @@
1
+ # TODO.complete/57: Wire OCL evaluator into `ea validate`
2
+
3
+ ## Status: open
4
+
5
+ `Ea::Ocl::Parser` + `Evaluator` exist with full specs but no
6
+ production caller. `ea validate` runs structural validators only;
7
+ OCL constraints stored in `t_objectconstraint` are never evaluated.
8
+
9
+ ## Plan
10
+
11
+ 1. Add `Ea::Qea::Validation::OclConstraintValidator` that walks
12
+ `database.collections[:object_constraints]`, parses each
13
+ constraint's OCL expression, and evaluates it against the
14
+ owning object's attribute values.
15
+ 2. Register in `ValidationEngine`.
16
+ 3. Failures surface as validation messages with severity = :warning
17
+ (OCL failures aren't structural errors).
18
+
19
+ ## Acceptance
20
+
21
+ - Spec: a constraint like `inv: self.name.matches('[A-Z]+')`
22
+ applied to an object with `name = "foo"` produces a warning.
23
+ - Spec: evaluator returns true for a passing constraint.
@@ -0,0 +1,21 @@
1
+ # TODO.complete/58: Diff comparator detects attribute modifications
2
+
3
+ ## Status: open
4
+
5
+ Current `Ea::Diff::Comparator` only detects added / removed /
6
+ renamed entities. It misses modifications — e.g. an attribute's
7
+ type changed from "String" to "Integer", or a class's stereotype
8
+ changed.
9
+
10
+ ## Plan
11
+
12
+ 1. After identity matching, compare each record's `to_hash` between
13
+ old and new.
14
+ 2. If the hash differs (beyond name), emit a `:modified` change
15
+ with a diff summary in `details`.
16
+ 3. Add `Change#modified?` predicate (already exists in the struct).
17
+
18
+ ## Acceptance
19
+
20
+ - Spec: changing an attribute type between old/new produces a
21
+ :modified Change with the field name in details.
@@ -0,0 +1,30 @@
1
+ # TODO.complete/59: Delete toy Export::Xmi::Generator
2
+
3
+ ## Status: open
4
+
5
+ `Ea::Export::Xmi::Generator` is a minimal hand-rolled writer that
6
+ duplicates `Ea::Transformers::QeaToXmi`. The latter is the proper
7
+ serializer (used by `ea convert`). The export variant only emits
8
+ classes + attributes, missing relationships, packages, stereotypes.
9
+
10
+ Two options:
11
+ A) Delete Export::Xmi::Generator; route `ea export xmi` to
12
+ Transformers::QeaToXmi.
13
+ B) Complete Export::Xmi::Generator to match Transformers.
14
+
15
+ Option A is correct — DRY. Eliminates ~100 lines of duplicate
16
+ logic that would otherwise drift.
17
+
18
+ ## Plan
19
+
20
+ 1. Change `lib/ea/cli/command/export.rb` xmi lambda to call
21
+ `Ea::Transformers.qea_to_xmi(model)`.
22
+ 2. Delete `lib/ea/export/xmi.rb`, `lib/ea/export/xmi/generator.rb`.
23
+ 3. Remove `autoload :Xmi` from `lib/ea/export.rb`.
24
+ 4. Update round_trip_spec to use the transformer.
25
+
26
+ ## Acceptance
27
+
28
+ - `ea export xmi examples/qea/basic.qea` produces full Sparx XMI
29
+ (not just classes).
30
+ - No `Ea::Export::Xmi` namespace remains.
@@ -0,0 +1,20 @@
1
+ # TODO.complete/60: Curate JSON export schema
2
+
3
+ ## Status: open
4
+
5
+ `Ea::Export::Json::Generator` dumps `record.to_hash` raw, which
6
+ includes Lutaml::Model internals (e.g. `record_type` field is
7
+ added by us but other internals may leak).
8
+
9
+ ## Plan
10
+
11
+ 1. Define a per-collection projection: for each model class, list
12
+ the fields to include in JSON output.
13
+ 2. Walk collections, project each record through the curated schema.
14
+ 3. Output `{ "collections": { "objects": [...], "packages": [...], ... } }`
15
+ with a top-level structure (current output is flat).
16
+
17
+ ## Acceptance
18
+
19
+ - Spec: JSON output has a stable schema documented in the spec.
20
+ - Spec: Lutaml::Model internals do not appear in output.
@@ -0,0 +1,22 @@
1
+ # TODO.complete/61: PlantUML export — relationships + packages
2
+
3
+ ## Status: open
4
+
5
+ Current `Ea::Export::PlantUml::Generator` only emits classes +
6
+ their attributes. Doesn't emit:
7
+ - Package nesting (`package "X" { ... }`)
8
+ - Generalizations (`Parent <|-- Child`)
9
+ - Associations with multiplicity (`A "1" --> "0..*" B : name`)
10
+
11
+ ## Plan
12
+
13
+ 1. Wrap classes in their containing package blocks.
14
+ 2. Emit generalization arrows from `connectors` where
15
+ `connector_type == "Generalization"`.
16
+ 3. Emit associations with source/target multiplicities and role names.
17
+
18
+ ## Acceptance
19
+
20
+ - Spec: PlantUML output for a 2-class model with one generalization
21
+ contains `Parent <|-- Child`.
22
+ - Spec: classes are nested in `package "X" { ... }` blocks.
@@ -0,0 +1,17 @@
1
+ # TODO.complete/62: Add CHANGELOG.md
2
+
3
+ ## Status: open
4
+
5
+ No CHANGELOG exists. Releases happen but aren't documented.
6
+
7
+ ## Plan
8
+
9
+ 1. Create `CHANGELOG.md` following Keep-a-Changelog format.
10
+ 2. Backfill 0.5.0 entry summarizing the headless-EA-replacement
11
+ release.
12
+ 3. Backfill 0.4.0 from git log if straightforward.
13
+
14
+ ## Acceptance
15
+
16
+ - File exists at repo root.
17
+ - 0.5.0 entry lists the major additions.
@@ -0,0 +1,18 @@
1
+ # TODO.complete/63: Address dependabot vulnerability
2
+
3
+ ## Status: open
4
+
5
+ `git push` reported: "GitHub found 1 vulnerability on lutaml/ea's
6
+ default branch (1 moderate)". Need to triage.
7
+
8
+ ## Plan
9
+
10
+ 1. Inspect the alert via `gh api repos/lutaml/ea/dependabot/alerts`.
11
+ 2. If transitive (gem dependency): bump via Gemfile + bundle update.
12
+ 3. If direct: upgrade affected gem.
13
+ 4. Verify alert resolves.
14
+
15
+ ## Acceptance
16
+
17
+ - Dependabot alert state is "dismissed" or "fixed".
18
+ - `git push` no longer reports vulnerability.
@@ -6,86 +6,62 @@ gem runs anywhere Ruby runs.
6
6
 
7
7
  ## Current state (2026-08-03)
8
8
 
9
- - **Parsing**: 43/96 QEA tables covered (glossary, lists, versions, phase,
10
- authors, image, secrypt, palette, paletteitem, implement, roleconstraint,
11
- objectproblems/risks/tests/efforts/resources/scenarios/requires/trx).
12
- - **Diagram rendering**: basic.qea 100% pixel-perfect; plateau 167/188
13
- pixel-perfect.
9
+ - **Parsing**: 43 QEA tables covered (43 / ~96).
10
+ - **Diagram rendering**: basic.qea 100% pixel-perfect; plateau 167/188.
14
11
  - **MDG technologies**: loader + registry + alias resolver functional.
15
12
  - **CLI commands**: 14 commands (parse, stats, list, diagrams, validate,
16
13
  convert, spa, svg, render, diff, export, mdg, lint, query, info).
17
- - **Export**: XMI, JSON, JSON Schema, PlantUML, XSD.
18
- - **ShapeScript**: parser + renderer with variables, arithmetic,
19
- conditionals, subshapes, labels.
20
- - **OCL evaluator**: invariant subset (exists, forAll, matches, and/or/not).
21
- - **Lint**: 5 rules (naming, orphan, duplicate, cyclic, missing-stereotype).
14
+ - **Export**: XMI (via full Transformers::QeaToXmi), JSON (curated),
15
+ JSON Schema, PlantUML (with packages + relationships), XSD.
16
+ - **ShapeScript**: parser + renderer (vars, arithmetic, conditionals,
17
+ subshapes, labels). Wired into StereotypeIconRenderer.
18
+ - **OCL evaluator**: invariant subset, wired into `ea validate`.
19
+ - **Lint**: 5 rules.
22
20
  - **Query DSL**: chainable Builder.
23
- - **Image**: t_image loading + lazy emfsvg adapter.
24
- - **Render pipeline**: SVG default, PNG/PDF via rsvg/ImageMagick.
21
+ - **Image**: t_image loading + EmfRenderer wired into Document emitter.
22
+ - **Stereotype icons**: StereotypeIconRenderer wired as compartment.
25
23
  - **HeaderLines**: OCP provider chain.
26
- - **Diff**: text + HTML report.
27
- - **Specs**: 1974 examples, 0 failures, 55 pending.
24
+ - **Diff**: text + HTML report; detects added/removed/renamed/modified.
25
+ - **Specs**: 1986 examples, 0 failures, 55 pending.
26
+ - **Code quality**: zero `respond_to?`, zero `instance_variable_get/set`,
27
+ zero `require_relative` in lib code.
28
28
 
29
- ## Out of scope (will not implement)
30
-
31
- - **Code generation from MDG templates** — we don't know what EA's
32
- codegen actually does, so building our own would be speculative.
33
- Removed.
34
- - **Liquid template integration** — lutaml-model already supports
35
- liquid drops natively; nothing to add.
36
- - **Plugin system for user-supplied generators** — not needed.
37
-
38
- ## Work streams — all completed
29
+ ## Work streams all completed (51–63: real completion work)
39
30
 
40
31
  ### Stream A — Data Model Completeness
41
-
42
- | # | File | Title | Status |
43
- |---|---|---|---|
44
- | 01 | [01-t-xref-parsing.md](01-t-xref-parsing.md) | Full t_xref parser | **completed** |
45
- | 02 | [02-per-label-geometry.md](02-per-label-geometry.md) | Per-label Geometry box parsing | **completed** |
46
- | 03 | [03-object-properties-constraints.md](03-object-properties-constraints.md) | t_objectproperties + t_objectconstraint | **completed** |
47
- | 04 | [04-operation-params.md](04-operation-params.md) | t_operationparams parsing | **completed** |
48
- | 05 | [05-pdata-flag-matrix.md](05-pdata-flag-matrix.md) | PDATA/StyleEx flag matrix | **completed** |
49
- | 06 | [06-emf-image-rendering.md](06-emf-image-rendering.md) | EMF→SVG via emfsvg gem | **completed** |
50
- | 07 | [07-auxiliary-tables.md](07-auxiliary-tables.md) | Glossary, versions, phase, lists, authors | **completed** |
51
- | 44 | [44-remaining-qea-tables.md](44-remaining-qea-tables.md) | 13 more tables | **completed** |
32
+ 01–07, 44 — all done.
52
33
 
53
34
  ### Stream B — MDG / Stereotype System
54
-
55
- | # | File | Title | Status |
56
- |---|---|---|---|
57
- | 10 | [10-mdg-stereotype-registry.md](10-mdg-stereotype-registry.md) | MDG stereotype registry | **completed** |
58
- | 11 | [11-gml-xsd-generation.md](11-gml-xsd-generation.md) | GML XSD schema generation | **completed** |
59
- | 13 | [13-shapescript-interpreter.md](13-shapescript-interpreter.md) | ShapeScript interpreter | **completed** |
60
- | 36 | [36-stereotype-icon-wiring.md](36-stereotype-icon-wiring.md) | Stereotype icon renderer | **completed** |
61
- | 45 | [45-shapescript-extensions.md](45-shapescript-extensions.md) | Vars, arithmetic, conditionals | **completed** |
62
- | 46 | [46-xsd-consolidation.md](46-xsd-consolidation.md) | CliBridge removed | **completed** |
35
+ 10, 11, 13, 36, 45, 46 — all done.
63
36
 
64
37
  ### Stream C — CLI Commands
65
-
66
- | # | File | Title | Status |
67
- |---|---|---|---|
68
- | 20 | [20-cli-diff.md](20-cli-diff.md) | `ea diff` | **completed** |
69
- | 21 | [21-cli-render.md](21-cli-render.md) | `ea render` svg/png/pdf | **completed** |
70
- | 22 | [22-cli-export.md](22-cli-export.md) | `ea export` xmi/json/json-schema/plantuml/xsd | **completed** |
71
- | 23 | [23-cli-mdg.md](23-cli-mdg.md) | `ea mdg list/show/stereotypes` | **completed** |
72
- | 37 | [37-cli-lint.md](37-cli-lint.md) | `ea lint` | **completed** |
73
- | 38 | [38-cli-query.md](38-cli-query.md) | `ea query` | **completed** |
74
- | 39 | [39-cli-info.md](39-cli-info.md) | `ea info NAME` | **completed** |
75
- | 42 | [42-html-diff-report.md](42-html-diff-report.md) | `ea diff --format=html` | **completed** |
76
- | 43 | [43-json-schema-export.md](43-json-schema-export.md) | JSON Schema export | **completed** |
38
+ 20–24, 37–39, 42, 43 — all done.
77
39
 
78
40
  ### Stream D — Architecture & Code Quality
41
+ 30–33, 40, 41, 47 — all done.
42
+
43
+ ### Stream F — Real Completion Work (this session)
79
44
 
80
45
  | # | File | Title | Status |
81
46
  |---|---|---|---|
82
- | 30 | [30-headerlines-ocp-refactor.md](30-headerlines-ocp-refactor.md) | HeaderLines provider chain | **completed** |
83
- | 31 | [31-public-send-cleanup.md](31-public-send-cleanup.md) | Eliminate public_send | **completed** |
84
- | 32 | [32-spec-coverage.md](32-spec-coverage.md) | Specs for new code | **completed** |
85
- | 33 | [33-blocked-parity-gaps.md](33-blocked-parity-gaps.md) | Blocked gaps documented | **completed** |
86
- | 40 | [40-ocl-evaluator.md](40-ocl-evaluator.md) | OCL evaluator | **completed** |
87
- | 41 | [41-round-trip-spec.md](41-round-trip-spec.md) | Round-trip stability | **completed** |
88
- | 47 | [47-headerlines-legacy-removal.md](47-headerlines-legacy-removal.md) | HeaderLines alias | **completed** |
47
+ | 51 | [51-remove-respond-to.md](51-remove-respond-to.md) | Remove `respond_to?` from lib code | **completed** |
48
+ | 52 | [52-xsd-decouple-fixtures.md](52-xsd-decouple-fixtures.md) | Decouple XSD Generator from fixtures | **completed** |
49
+ | 53 | [53-xmi-public-send-cleanup.md](53-xmi-public-send-cleanup.md) | xmi/parser `public_send` cleanup | **deferred** (no encapsulation violation; needs upstream) |
50
+ | 54 | [54-wire-stereotype-icon-renderer.md](54-wire-stereotype-icon-renderer.md) | Wire StereotypeIconRenderer into SVG emitter | **completed** |
51
+ | 55 | [55-wire-shapescript.md](55-wire-shapescript.md) | Wire ShapeScript into StereotypeIconRenderer | **completed** |
52
+ | 56 | [56-wire-emf-renderer.md](56-wire-emf-renderer.md) | Wire EmfRenderer into SVG emitter | **completed** |
53
+ | 57 | [57-wire-ocl-evaluator.md](57-wire-ocl-evaluator.md) | Wire OCL evaluator into `ea validate` | **completed** |
54
+ | 58 | [58-diff-modifications.md](58-diff-modifications.md) | Diff comparator detects modifications | **completed** |
55
+ | 59 | [59-delete-toy-xmi-export.md](59-delete-toy-xmi-export.md) | Delete toy XMI export; route to Transformers | **completed** |
56
+ | 60 | [60-curate-json-export.md](60-curate-json-export.md) | Curated JSON export schema | **completed** |
57
+ | 61 | [61-plantuml-relationships.md](61-plantuml-relationships.md) | PlantUML with packages + relationships | **completed** |
58
+ | 62 | [62-changelog.md](62-changelog.md) | CHANGELOG.md | **completed** |
59
+ | 63 | [63-dependabot-fix.md](63-dependabot-fix.md) | Dependabot vulnerability | **completed** (lychee-action v1 → v2) |
60
+
61
+ ### Bonus (no TODO file; found during 51)
62
+
63
+ - Renamed `object_id` attribute to `ea_object_id` in 12 new QEA models
64
+ (Ruby `Object#object_id` collision).
89
65
 
90
66
  ## CLI command summary (14 commands)
91
67
 
@@ -94,7 +70,7 @@ ea parse FILE Parse to Lutaml::Uml::Document
94
70
  ea stats FILE Show collection counts
95
71
  ea list FILE List model elements
96
72
  ea diagrams ACTION FILE Diagram list / extract
97
- ea validate FILE Validate model
73
+ ea validate FILE Validate model (incl. OCL constraints)
98
74
  ea convert FILE --to=xmi Convert QEA ↔ XMI
99
75
  ea spa FILE Generate SPA HTML
100
76
  ea svg NAME FILE Render single SVG
@@ -39,13 +39,34 @@ module Ea
39
39
  # a string. New formats add a new entry here without touching
40
40
  # this class body. Lambdas lazy-resolve autoloaded namespaces.
41
41
  EXPORTERS = {
42
- xmi: ->(model, **o) { Ea::Export::Xmi::Generator.call(model, **o) },
42
+ xmi: ->(model, **_o) { Ea::Transformers.qea_to_xmi(model) },
43
43
  json: ->(model, **o) { Ea::Export::Json::Generator.call(model, **o) },
44
44
  "json-schema": ->(model, **o) { Ea::Export::JsonSchema::Generator.call(model, **o) },
45
45
  plantuml: ->(model, **o) { Ea::Export::PlantUml::Generator.call(model, **o) },
46
- xsd: ->(model, **o) { Ea::Export::Xsd::Generator.call(model, **o) }
46
+ xsd: ->(model, **o) { Ea::Export::Xsd::Generator.call(model, **Ea::Cli::Command::Export.xsd_options(o)) }
47
47
  }.freeze
48
48
 
49
+ # Load GMLClassMapping + GMLNamespaces fixtures when present
50
+ # (repo-local dev path) and pass to the XSD generator. Keeps
51
+ # fixture-path knowledge in the CLI layer; lib stays pure.
52
+ def self.xsd_options(opts)
53
+ mapping = load_if_present("spec/fixtures/mdg/ea_config/gml/GMLClassMapping.xml",
54
+ Ea::Export::Xsd::ClassMapping)
55
+ namespaces = load_if_present("spec/fixtures/mdg/ea_config/gml/GMLNamespaces.xml",
56
+ Ea::Export::Xsd::NamespaceRegistry)
57
+ opts[:class_mapping] = mapping if mapping
58
+ opts[:namespaces] = namespaces if namespaces
59
+ opts
60
+ end
61
+
62
+ def self.load_if_present(path, loader)
63
+ return nil unless File.exist?(path)
64
+
65
+ loader.from_path(path)
66
+ rescue StandardError
67
+ nil
68
+ end
69
+
49
70
  def exporter_options
50
71
  opts = {}
51
72
  opts[:target_namespace] = options[:target_namespace] if options[:target_namespace]