Skip to content

Commit abcb983

Browse files
authored
plantuml, rules_score: resolve split views as one group (#521)
Allow a view to be split into overview and detail .puml files. - puml_cli resolves all inputs of a run (--file and --folders) as one group per diagram kind; only .puml inputs are accepted and the same --file twice is an error - component: bodied top-level element attaches to a same-name/type/ stereotype nested declaration in another file; relation targets resolve across files only when the own file does not know them, sources must be local (undirected relations belong to the local end) - class: overview declares a bodyless class, detail adds members; same group rules as component - sequence: one file declares the cast, scenarios use aliases - activity: group equals per-file resolution - errors: GroupResolveError reports "in <file>: ..."; a file of the wrong diagram kind reports "expected a Component diagram, found a X diagram" - validation: component anchoring check runs in the architectural_design profile only; class declarations merge when at most one has a body; internal API interfaces declared in several files are one interface holding the union of the methods - rules_score: architectural_design and unit_design run one parser action per view; static accepts component diagrams only; .plantuml is no longer accepted (the CLI only took .puml) - tests: one generic group runner for all resolver tests, new group fixtures for component and class - TRLC requirements, docs, skill and seooc example updated
2 parents 950ff5f + aa87a5b commit abcb983

249 files changed

Lines changed: 7592 additions & 794 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/skills/score-architecture/SKILL.md‎

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -395,25 +395,51 @@ architectural_design(
395395
)
396396
```
397397

398-
`static` accepts more than one `.puml` file. They are merged by entity id (the full parent-name
399-
dot-path) into a single architecture: re-declaring the same entity (same id) in more than one
400-
file is allowed and merges its relations, as long as `stereotype`/element type agree everywhere
401-
it's declared — this is how `overview_design.puml` above can bare-declare the SEooC and its
402-
top-level component while `static_design.puml` elaborates their internals, without duplicating
403-
every nested unit and relation in both files. A parent whose children are split across files with
404-
no single file containing all of them is an error. Re-nesting an entity under a different parent
405-
across files is *not* caught here (different parent ⇒ different id ⇒ a different entity) — that
406-
class of mistake instead surfaces as a Bazel ↔ diagram mismatch (extra/missing entity) in the
398+
`static` accepts more than one `.puml` file, all of them component/package diagrams; class
399+
diagrams belong in the elaborating unit's own `unit_design(static=[...])` instead. All files are
400+
resolved *together* in one pass (`ComponentResolver::resolve_group`), then merged by entity id (the
401+
full parent-name dot-path) into a single architecture: re-declaring the same entity (same id) in
402+
more than one file is allowed and merges its relations, as long as `stereotype`/element type agree
403+
everywhere it's declared. A top-level element *with a body* in one file that has the same name,
404+
element type and stereotype as a *nested* declaration in exactly one other file is implicitly
405+
reparented onto that declaration's parent chain instead of becoming a new top-level entity (matching
406+
is by name, so the files may use different aliases). This is how `static_design.puml` can declare
407+
only `component_example`'s children with **no SEooC wrapper**, while `overview_design.puml` nests
408+
`component_example` under the SEooC boundary. A bodyless re-declaration and a rooted name (`::X`)
409+
stay separate top-level entities. Reparenting is transitive (a cycle is an `ImplicitParentCycle`
410+
error); matching nested declarations under more than one distinct parent is an
411+
`AmbiguousImplicitParent` error rather than a guess. A parent whose children are split across files
412+
with no single file containing all of them is an error. Re-nesting an entity under a different
413+
parent across files is *not* caught here (different parent => different id => a different entity);
414+
that class of mistake surfaces as a Bazel <-> diagram mismatch (extra/missing entity) in the
407415
`bazel_component` check below.
408416

409-
Each `.puml` file is parsed and resolved on its own, *before* the id-based merge above runs — a
410-
relation may only reference an alias declared in that same file. Referencing an alias that's only
411-
declared in another `static` file fails at PlantUML parse time (`Element Resolver:
412-
UnresolvedReference: <alias>`), not as a Design validation error. So keep each file
413-
self-contained: if a detail file wires up an entity's interfaces, (re-)declare those interfaces
414-
in that same file rather than assuming they're visible from the overview file.
415-
416-
`static`/`dynamic` accept `.puml`, `.plantuml`, `.png`, `.svg`, `.rst`, `.md`. To combine a
417+
A relation's *source* must be declared (or reparented) in the file that writes it; only its
418+
*target* may resolve to another file in the same `static` group (falling back to the group only
419+
when unresolved locally, erroring `AmbiguousReference` if more than one other file resolves it).
420+
Referencing a source that isn't local is a `CrossFileRelationSource` error — *unless* the relation
421+
is undirected (no arrowhead, no dependency-style decoration), in which case the two sides are
422+
swapped instead of erroring (the locally-declared endpoint becomes the source).
423+
424+
The same multi-file split applies to every other view, resolved as **one group per view** (ids are
425+
never shared across views; `unit_design` has one group for `static` and one for `dynamic`). Files
426+
are bucketed by diagram kind, detected per file in the order component, activity, class, sequence:
427+
428+
- **Class** (`public_api`, `internal_api`, `unit_design` `static`): overview nests `class X`
429+
without a body, the detail file declares `class X { ... }` with no wrapper (same reparenting as
430+
components; a bodyless declaration is a reference). Two bodies for one id =
431+
`class "<id>" is defined more than once`; different type/stereotype = conflict. Always write the
432+
`class` keyword — `package p { interface I }` is detected as a *component* diagram.
433+
- **Sequence** (`dynamic`, `unit_design` `dynamic`): one cast file declares `participant
434+
"pkg::Comp::Unit" as Alias`; scenario files use only the alias and need `activate` (a bare
435+
message-only file is detected as a component diagram). Message endpoints and `create` resolve
436+
against other files' explicit participants (local wins; distinct uids =
437+
`AmbiguousParticipantReference`); lifecycle and `ref over` stay file-local.
438+
- **Activity**: no names, nothing to share.
439+
440+
Full rules and examples: `plantuml/parser/docs/element-identifiers.md` (section 4).
441+
442+
`static`/`dynamic` accept `.puml`, `.png`, `.svg`, `.rst`, `.md`. To combine a
417443
diagram with prose, add both the RST/Markdown wrapper *and* the referenced `.puml` to the same
418444
list (as `public_api.puml` + `public_api.rst` above, which overrides `public_api.puml`'s
419445
generated wrapper page); the wrapper embeds the diagram with `.. uml:: file.puml`. `index.md`
@@ -434,10 +460,10 @@ every `.puml` gets an auto-generated wrapper page, and every directory gets a ge
434460
body a section title** — it becomes the page title, and without one Sphinx warns that a
435461
toctree entry has no title.
436462

437-
Build-time errors (each naming the offending files): a diagram named `index.puml`/
438-
`index.plantuml` (that stem is reserved for the navigation page); two files in one view
439-
resolving to the same staged path; a stem having both `.rst` and `.md`, or both `.puml` and
440-
`.plantuml`. For a page you want fully outside this scheme, leave the `.puml` out of the view
463+
Build-time errors (each naming the offending files): a diagram named `index.puml` (that stem
464+
is reserved for the navigation page); two files in one view resolving to the same staged path;
465+
a stem having both `.rst` and `.md`. For a page you want fully outside this scheme, leave the
466+
`.puml` out of the view
441467
attribute and reference it via your own `.. uml::` elsewhere in the docs tree.
442468

443469
### `unit_design`

‎bazel/rules/rules_score/docs/rule_reference.rst‎

Lines changed: 82 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -420,19 +420,19 @@ and ``safety_analysis``.
420420
* - ``static``
421421
- label list
422422
- no
423-
- Static-view files (``.puml``, ``.rst``, ``.md``, ``.svg``, ``.png``) (default ``[]``). Multiple ``.puml`` files are allowed — e.g. a boundary overview diagram alongside a detailed one — and are merged into a single architecture, see `Multiple static PlantUML files`_ below.
423+
- Static-view files -- component/package diagrams only (``.puml``, ``.rst``, ``.md``, ``.svg``, ``.png``); see `Multiple static PlantUML files`_ below for multi-file resolution. Class diagrams belong in the elaborating unit's own ``unit_design(static=[...])`` instead, never here (default ``[]``)
424424
* - ``dynamic``
425425
- label list
426426
- no
427-
- Dynamic-view files (default ``[]``)
427+
- Dynamic-view files; several sequence diagrams share a participant cast (see `Multiple files in the other views`_) (default ``[]``)
428428
* - ``public_api``
429429
- label list
430430
- no
431-
- Public-API diagram files (``.puml``); also generates traceability items for safety analysis, exposed via ``ArchitecturalDesignInfo.public_api_lobster_files`` (default ``[]``)
431+
- Public-API diagram files (``.puml``); also generates traceability items for safety analysis, exposed via ``ArchitecturalDesignInfo.public_api_lobster_files``. Several class diagrams resolve as one group (default ``[]``)
432432
* - ``internal_api``
433433
- label list
434434
- no
435-
- Internal-API diagram files (``.puml``) describing interfaces exposed between components inside the SEooC; their FlatBuffers output is exposed via ``ArchitecturalDesignInfo.internal_api`` for downstream validation (default ``[]``)
435+
- Internal-API diagram files (``.puml``) describing interfaces exposed between components inside the SEooC; their FlatBuffers output is exposed via ``ArchitecturalDesignInfo.internal_api`` for downstream validation. Several class diagrams resolve as one group (default ``[]``)
436436
* - ``maturity``
437437
- string
438438
- no
@@ -449,20 +449,51 @@ and ``safety_analysis``.
449449
Multiple static PlantUML files
450450
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
451451

452-
``static`` accepts more than one ``.puml`` file. This is intended for splitting
453-
a large architecture into a boundary **overview** diagram (the SEooC and its
454-
public interfaces only) and one or more **detail** diagrams that elaborate the
455-
internals, without repeating every relation and nested entity in both places.
456-
457-
The files are parsed independently and then merged by entity id (the full
458-
dot-path of parent aliases). Rules for the merge:
459-
452+
``static`` accepts more than one ``.puml`` file, all component diagrams. This
453+
is intended for splitting a large architecture into a boundary **overview**
454+
diagram (the SEooC and its public interfaces only) and one or more **detail**
455+
diagrams that elaborate the internals, without repeating every relation and
456+
nested entity in both places. (Class diagrams are never allowed in ``static``
457+
-- put them in the elaborating unit's own ``unit_design(static=[...])``
458+
instead.)
459+
460+
All ``.puml`` files passed to ``static`` are resolved
461+
**together**, in one pass, instead of one file at a time — see
462+
``ComponentResolver::resolve_group``. Once every file's elements are known,
463+
they're merged by entity id (the full dot-path of parent names). Rules for
464+
resolution and the merge:
465+
466+
* **Implicit reparenting:** a *top-level* element with a body in one file that
467+
has the same name, element type and stereotype as a *nested* declaration in
468+
exactly one other file is implicitly reparented onto that declaration's
469+
parent chain, instead of becoming a new top-level entity. Matching is by
470+
name, so the files may use different aliases for the same entity. This is
471+
what lets a detail file declare only a component's children (no wrapper)
472+
while an overview file nests that same component under the SEooC boundary.
473+
A bodyless element (``component X`` without ``{ ... }``) and a rooted name
474+
(``::X``) are never reparented; they stay separate top-level entities.
475+
Reparenting is transitive: if the parent itself needs reparenting, that is
476+
resolved first (a cycle is an ``ImplicitParentCycle`` error). Matching
477+
nested declarations under more than one distinct parent is an
478+
``AmbiguousImplicitParent`` error rather than an arbitrary guess.
479+
* A relation's *source* must be declared (or reparented) in the file that
480+
writes it; only its *target* may resolve to another file in the same
481+
``static`` group, written as the declaring file writes it (its alias, else
482+
its name). The group is consulted only when the target isn't found locally;
483+
more than one other file resolving it is an ``AmbiguousReference`` error.
484+
Referencing a source that isn't local is a
485+
``CrossFileRelationSource`` error -- *unless* the relation is undirected
486+
(no arrowhead and no dependency-style decoration), in which case the two
487+
sides are swapped instead of erroring (the locally-declared endpoint
488+
becomes the source), since an undirected relation has no inherent
489+
direction to violate.
460490
* Re-declaring the *same* entity (identical id) in more than one file is
461491
allowed as long as its ``stereotype`` and element type agree across files;
462492
their relations are unioned (duplicate relations are de-duplicated). This
463493
is what lets an overview file bare-declare a component or unit that a
464-
detail file elaborates further.
465-
* Re-declaring the same alias with a conflicting ``stereotype`` or element
494+
detail file elaborates further (whether by literally repeating the
495+
wrapper, or implicitly via reparenting above).
496+
* Re-declaring the same id with a conflicting ``stereotype`` or element
466497
type across files is an error.
467498
* A parent whose children are declared across more than one file is only
468499
allowed if a single file contains the full set of children (i.e. one file
@@ -476,14 +507,40 @@ dot-path of parent aliases). Rules for the merge:
476507
Bazel-vs-diagram comparison (an entity nested under the wrong parent shows
477508
up as an extra/missing entry there).
478509

479-
Each ``.puml`` file is parsed and resolved **independently**, before the
480-
merge step above ever runs. A relation (``-->``, ``..>``, etc.) may only
481-
reference aliases declared in the *same* file — an alias declared only in
482-
another ``static`` file is not visible yet at that point. Referencing such
483-
an alias fails at PlantUML parse time with an ``Element Resolver:
484-
UnresolvedReference: <alias>`` error, not as a Design validation error. Keep
485-
every file self-contained: if a detail file wires up an entity's
486-
interfaces, declare (or re-declare) those interfaces in that same file.
510+
.. _multiple-files-other-views:
511+
512+
Multiple files in the other views
513+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
514+
515+
Every view (``static``, ``dynamic``, ``public_api``, ``internal_api``; for
516+
``unit_design`` ``static`` and ``dynamic``) is resolved as **one group per
517+
view**: ids are shared inside a view, never across views. The files of a view
518+
are split by diagram kind first (component, class, sequence, activity); a group
519+
never mixes kinds. ``static`` of ``architectural_design`` is component-only.
520+
521+
The kind is detected per file, in the order component, activity, class,
522+
sequence. Give each file a construct of its own kind: ``class`` (or a body with
523+
members) for class diagrams, ``participant`` or ``activate`` for sequence
524+
diagrams. ``package p { interface I }`` and a file of only ``a -> b : call()``
525+
are *component* diagrams.
526+
527+
* **Class diagrams** (``public_api``, ``internal_api``, ``unit_design``
528+
``static``): same implicit reparenting and peer references as the component
529+
rules above. A declaration without a body is a reference. Re-declaring one
530+
id merges the declarations: a bodyless one merges into the one with a body
531+
(members, type aliases or enum literals; relationships do not count and are
532+
united); two bodies are ``class "<id>" is defined more than once``; a
533+
different type or stereotype is a conflict. A namespace is defined only in
534+
files that own a defined child.
535+
* **Sequence diagrams** (``dynamic``, ``unit_design`` ``dynamic``): no
536+
reparenting. A message endpoint or ``create`` target that the file does not
537+
declare resolves to the explicit ``participant`` declarations of the other
538+
files of the view, by alias, else by name. A local declaration wins; distinct
539+
uids from several files are ``AmbiguousParticipantReference``.
540+
``activate``/``deactivate``/``destroy`` and ``ref over`` stay file-local.
541+
* **Activity diagrams** carry no names and are independent per file.
542+
543+
See ``plantuml/parser/docs/element-identifiers.md`` (section 4) for examples.
487544

488545
.. _rule-unit-design:
489546

@@ -517,11 +574,11 @@ implementation.
517574
* - ``static``
518575
- label list
519576
- no
520-
- Static-view files (class diagrams, data-structure diagrams). When an ``.rst`` fragment references a ``.puml`` via ``.. uml::``, list both files together (default ``[]``)
577+
- Static-view files (class diagrams, data-structure diagrams). When an ``.rst`` fragment references a ``.puml`` via ``.. uml::``, list both files together. Class diagrams resolve as one group (default ``[]``)
521578
* - ``dynamic``
522579
- label list
523580
- no
524-
- Dynamic-view files (sequence, state diagrams) (default ``[]``)
581+
- Dynamic-view files (sequence, state diagrams). Sequence diagrams resolve as one group; a shared cast file declares the participants, scenario files reference them (default ``[]``)
525582
* - ``visibility``
526583
- —
527584
- no
@@ -609,7 +666,7 @@ which breaks the dependency cycle between measures and analysis.
609666
* - ``srcs``
610667
- label list
611668
- no
612-
- FTA PlantUML diagram files (``.puml`` / ``.plantuml``) (default ``[]``)
669+
- FTA PlantUML diagram files (``.puml``) (default ``[]``)
613670
* - ``deps``
614671
- label list
615672
- no

0 commit comments

Comments
 (0)