Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 3 additions & 3 deletions .github/skills/rules-score/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,8 @@ dependable_element(
)
```

Note how the diagram alias must equal the target name — `docs/static_design.puml` declares
`package "my_element" ... { component "MyComponent" { component "MyUnit" } }`, matching
Note how the diagram name must equal the target name — `docs/static_design.puml` declares
`package my_element ... { component MyComponent { component MyUnit } }`, matching
`dependable_element(name = "my_element")`, `component(name = "MyComponent")`, and
`unit(name = "MyUnit")`. This is enforced by the `bazel_component` validator (see
**score-architecture**).
Expand Down Expand Up @@ -213,7 +213,7 @@ warnings while a design is in progress; switch to `"release"` before certificati
- **ASIL vs. integrity_level**: requirement records use `ScoreReq.Asil` = `QM`/`B`/`D` only;
`dependable_element.integrity_level` uses `A`/`B`/`C`/`D` (D > C > B > A) for the element
hierarchy.
- **Naming**: diagram aliases must equal Bazel target names. `bazel_component` is case-insensitive;
- **Naming**: diagram names must equal Bazel target names. `bazel_component` is case-insensitive;
all other validators are case-sensitive.
- **Traceability spine**: `AssumedSystemReq → FeatReq → CompReq` (version-pinned `@`) →
`lobster-tracing` in tests → `test_case_coverage.lock.yaml`; `FailureMode.interface` links safety
Expand Down
110 changes: 53 additions & 57 deletions .github/skills/score-architecture/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,19 +193,19 @@ The validator identifies elements by their **stereotype**, not by the PlantUML k
```text
@startuml static_design

package "Safety Software SEooC Example" as safety_software_seooc_example <<SEooC>> {
component "ComponentExample" as component_example <<component>> {
component "Unit 1" as unit_1 <<unit>>
component "Unit 2" as unit_2 <<unit>>
component "Sub Component Example" as sub_component_example <<component>>
package safety_software_seooc_example <<SEooC>> {
component component_example <<component>> {
component unit_1 <<unit>>
component unit_2 <<unit>>
component sub_component_example <<component>>

interface "InternalInterface" as InternalInterface
interface InternalInterface
unit_1 -l-( InternalInterface
unit_2 )-r- InternalInterface
}
}

interface "SampleLibraryAPI" as SampleLibraryAPI
interface SampleLibraryAPI

safety_software_seooc_example )-d- SampleLibraryAPI

Expand All @@ -228,13 +228,13 @@ interfaces), declare a `portin` / `portout` inside the `<<SEooC>>` or `<<compone
(from the `bazel_component` validator spec):

```text
package "MySeooc" as MySeooc <<SEooC>> {
portin " " as p_in ' required interface port
portout " " as p_out ' provided interface port
package MySeooc <<SEooC>> {
portin p_in ' required interface port
portout p_out ' provided interface port
}

interface "IRequired" as IRequired
interface "IProvided" as IProvided
interface IRequired
interface IProvided

p_in -( IRequired : requires
p_out )- IProvided : provides
Expand All @@ -255,7 +255,7 @@ cross-checked against each other and against the implementation.
| View | Attribute | Cross-checked against | Purpose |
|------|-----------|-----------------------|---------|
| **Static** | `static` | Bazel `dependable_element`/`component`/`unit` tree; internal API; public API; sequences | Structural component/unit tree — the anchor for all other checks |
| **Dynamic** (sequence) | `dynamic` | Static component diagram; internal API | Unit interactions — participant aliases and cross-unit calls |
| **Dynamic** (sequence) | `dynamic` | Static component diagram; internal API | Unit interactions — participant names and cross-unit calls |
| **Public API** | `public_api` | Static diagram (top-level interfaces bound from the SEooC) | Interfaces the SEooC exposes; also feeds `FailureMode.interface` traceability |
| **Internal API** | `internal_api` | Static component diagram; sequences | Interfaces between components/units inside the SEooC |

Expand All @@ -264,8 +264,8 @@ Real example diagrams (verbatim, from [`examples/seooc/design/`](../../../bazel/
```text
' dynamic_design.puml — sequence diagram
@startuml
participant "Unit 1" as unit_1 <<unit>>
participant "Unit 2" as unit_2 <<unit>>
participant unit_1 <<unit>>
participant unit_2 <<unit>>

unit_1 -> unit_2 : GetData()
return Data*
Expand All @@ -276,7 +276,7 @@ return Data*
' public_api.puml — top-level interface exposed by the SEooC
@startuml
namespace safety_software_seooc_example {
interface "SampleLibraryAPI" as SampleLibraryAPI {
interface SampleLibraryAPI {
+ GetNumber(): int
}
}
Expand All @@ -288,7 +288,7 @@ namespace safety_software_seooc_example {
@startuml
namespace safety_software_seooc_example {
namespace component_example {
interface "InternalInterface" as InternalInterface <<interface>>{
interface InternalInterface <<interface>>{
{abstract} GetData(BindingType binding): Data*
}
}
Expand All @@ -310,56 +310,52 @@ Every element across the `static`, `unit_design` (class), and `dynamic` (sequenc
resolved to a **canonical identifier**. Two elements are the same architecture element exactly
when their identifiers match — that is how the validators link a `static` component to its
`unit_design` class and to a `dynamic` sequence participant. You never write an identifier
yourself; it is assembled from three inputs joined with `.`:
yourself; it is assembled from two inputs joined with `.`:

| # | Input | Comes from | Example |
|---|-------|-----------|---------|
| 1 | Root anchor | The Bazel package of the owning `architectural_design`/`unit_design` target (`/` → `.`) | `unit_1/docs` → `unit_1.docs` |
| 2 | Internal scope | The `package`/`component`/`namespace` nesting the element is written in | `logging.Recorder` |
| 3 | Leaf | The element's alias (`as X`), or its name if there is no alias | `Backend` |
| 1 | Internal scope | The `package`/`component`/`namespace` nesting the element is written in | `logging.Recorder` |
| 2 | Name | The name written in the declaration | `Backend` |

joined: `unit_1.docs.logging.Recorder.Backend`. `::` and `.` are equivalent separators; a
dotted/`::`-qualified reference (an interface binding, a class relationship, …) is always read
**relative to the root anchor**, never as an absolute path.
joined: `logging.Recorder.Backend`. `::` and `.` are equivalent separators; a
dotted/`::`-qualified reference (an interface binding, a class relationship, …) is read relative
to the enclosing scope.

**Sequence diagrams are the exception**: a participant has no nesting to draw scope from, so a
**quoted label that is a qualified path** is its identifier, even when an alias exists — the alias
is only a local shortcut for drawing arrows. Any other label is prose, and the identifier is the
alias.
**The name is the identity.** It must be an identifier path (segments of letters, digits and `_`
separated by `.` or `::`). Prose such as `component "Unit 1" as unit_1` is an error; put prose
into a note, a stereotype or a relation description. An `as` alias is only a local reference key:
relations, messages, `activate`, `destroy`, `create` and `ref over` refer to an aliased element
by its alias, never by its name.

| What you write | Identifier comes from |
|-----------------|-----------------------|
| `participant "logging::Recorder::Backend" as Backend` | the label (`logging.Recorder.Backend`) |
| `participant "Unit 1" as unit_1` | label is prose → the **alias** (`unit_1`) |
| `participant "backend : logging::Recorder::Backend" as Backend` | spaces or `:` make it prose → the **alias** (`Backend`) |
| `actor Client` | the bare name |

If a participant represents a nested unit, write the full qualified label —
`"Component::Unit"` — so the identifier matches the `static` diagram. A prose label without an
alias is an error, and a prose label with an alias that doesn't resolve to the same identifier as
the component diagram is a **cross-diagram validation mismatch** (see **Active validations**,
`component_sequence.md`). Messages, `activate`, `deactivate`, `destroy`, `create` and `ref over`
name a participant by its alias (or bare name); the display name of an aliased participant is not
a reference.
| `participant "logging::Recorder::Backend" as Backend` | the name (`logging.Recorder.Backend`); `Backend` is the local key |
| `participant Backend` | the name (`Backend`) |
| `component unit_1 <<unit>>` | the name (`unit_1`) |
| `participant "Unit 1" as unit_1` | error: free-text name |

If a participant represents a nested unit, write the full qualified name — `"Component::Unit"` —
so the identifier matches the `static` diagram. A name that does not resolve to the same identifier
as the component diagram is a **cross-diagram validation mismatch** (see **Active validations**,
`component_sequence.md`).

`ExternalEndpoint` is a reserved participant name for an actor outside the described architecture;
it is emitted verbatim (no root anchor, no scope) so it always matches itself across diagrams.
Declare it as `participant ExternalEndpoint` (or with that alias), at most once per diagram.
it is emitted verbatim (no scope) so it always matches itself across diagrams.
Declare it as `participant ExternalEndpoint` (or `participant "ExternalEndpoint" as ext`), at most
once per diagram.

**Best practices**:
- Give every architecture-relevant element an explicit `as` alias; never rely on a prose label.
- Keep `architectural_design` and `unit_design` for one subsystem under the same root Bazel
package — different packages get different root anchors, and their identifiers can never match.
- Name every architecture-relevant element with the Bazel target / C++ name; skip the alias unless
the name is long or qualified.
- Mirror the nesting between the `static` and `unit_design` diagrams; scope segments must be
identical on both sides.
- In sequence diagrams, write the full qualified label (`"Component::Unit"`) once nesting is
involved; use the bare alias only when the unit is top-level. Use the alias for arrows either
way.
- In sequence diagrams, write the full qualified name (`"Component::Unit"`) once nesting is
involved; use the bare name only when the unit is top-level.
- Use `ExternalEndpoint` verbatim for out-of-scope actors.
- Treat identifiers as derived, not authored — to change one, change the nesting, alias, or owning
Bazel package, not the identifier itself.
- Treat identifiers as derived, not authored — to change one, change the nesting or the name, not
the identifier itself.

> Full rule set (root-anchor construction, qualified-reference resolution, uniqueness errors):
> Full rule set (qualified-reference resolution, uniqueness errors):
> `plantuml/parser/docs/element-identifiers.md` (this repo) — the resolver's authoring guide.

---
Expand Down Expand Up @@ -399,7 +395,7 @@ architectural_design(
)
```

`static` accepts more than one `.puml` file. They are merged by entity id (the full parent-alias
`static` accepts more than one `.puml` file. They are merged by entity id (the full parent-name
dot-path) into a single architecture: re-declaring the same entity (same id) in more than one
file is allowed and merges its relations, as long as `stereotype`/element type agree everywhere
it's declared — this is how `overview_design.puml` above can bare-declare the SEooC and its
Expand Down Expand Up @@ -568,7 +564,7 @@ the source of truth for what is checked and which notation is valid**.
| **Bazel ↔ component** | `bazel_component.md` | `dependable_element`/`component`/`unit` targets ↔ `static` PlantUML | insensitive |
| **Component ↔ public API** | `component_public_api.md` | top-level interfaces in `static` ↔ `public_api` class diagram; must be bound from the SEooC | sensitive |
| **Component ↔ internal API** | `component_internal_api.md` | interfaces in `static` ↔ `internal_api` diagram | sensitive |
| **Component ↔ sequence** | `component_sequence.md` | unit aliases + interface connections in `static` ↔ `dynamic` sequence diagrams | sensitive |
| **Component ↔ sequence** | `component_sequence.md` | unit ids + interface connections in `static` ↔ `dynamic` sequence diagrams | sensitive |
| **Sequence ↔ internal API** | `sequence_internal_api.md` | sequence method calls ↔ `internal_api` interfaces (method name, consumer/provider role, interface coverage) | sensitive |
| **Class design ↔ implementation** | `class_design_implementation.md` | `unit_design` class diagram ↔ C++ implementation (entities, methods, variables, enums, relationships, templates) | sensitive + type normalization |

Expand All @@ -580,7 +576,7 @@ Additional element-level checks (see [`docs/user_guide/general.rst`](../../../ba
| **Integrity level** | a `dependable_element` must not `deps` on one with a lower `integrity_level` (D > C > B > A) | violation fails the build |

> **Only `bazel_component` is case-insensitive** — every other validator matches names
> case-sensitively. Keep diagram aliases identical to the code/target names.
> case-sensitively. Keep diagram names identical to the code/target names.
>
> `maturity = "development"` downgrades scope and coverage violations to warnings; switch back to
> `"release"` before certification.
Expand All @@ -599,12 +595,12 @@ Traceability from a feature requirement down to implementing components runs thr

## Conventions

- Diagram aliases must **match the Bazel target name** — `bazel_component` compares
- Diagram names must **match the Bazel target name** — `bazel_component` compares
case-insensitively, but every other validator is case-sensitive, so keep them identical.
- Model down to the `unit` level in the static diagram; every implemented unit must appear (and no
extra ones — missing *and* extra elements both fail).
- Sequence diagrams are validated: participant aliases must equal the component-diagram unit
aliases, and every cross-unit call must correspond to an interface connection (and be declared in
- Sequence diagrams are validated: participant names must equal the component-diagram unit
names, and every cross-unit call must correspond to an interface connection (and be declared in
the internal API). Keep them at the unit-interaction level.
- Unit-design class diagrams are validated against the C++ implementation — the design is the
contract (implementation-only members are allowed, design-only members are not).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

@startuml SeoocExample_DynamicDesign

participant "Unit 1" as unit_1 <<unit>>
participant "Unit 2" as unit_2 <<unit>>
participant unit_1 <<unit>>
participant unit_2 <<unit>>

unit_1 -> unit_2 : GetData()
return Data*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

namespace safety_software_seooc_example {
namespace component_example {
interface "InternalInterface" as InternalInterface <<interface>>{
interface InternalInterface <<interface>>{
{abstract} GetData()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
@startuml SeoocExample_PublicApi

namespace safety_software_seooc_example {
interface "SampleLibraryAPI" as SampleLibraryAPI {
interface SampleLibraryAPI {
+ GetNumber()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@

@startuml SeoocExample_StaticDesign

package "Safety Software SEooC Example" as safety_software_seooc_example <<SEooC>> {
component "ComponentExample" as component_example <<component>> {
component "Unit 1" as unit_1 <<unit>>
component "Unit 2" as unit_2 <<unit>>
component "Sub Component Example" as sub_component_example <<component>>
package safety_software_seooc_example <<SEooC>> {
component component_example <<component>> {
component unit_1 <<unit>>
component unit_2 <<unit>>
component sub_component_example <<component>>

interface "InternalInterface" as InternalInterface
interface InternalInterface
unit_1 -l-( InternalInterface
unit_2 -r- InternalInterface
}
}

interface "SampleLibraryAPI" as SampleLibraryAPI
interface SampleLibraryAPI

safety_software_seooc_example )-d- SampleLibraryAPI

Expand Down
42 changes: 21 additions & 21 deletions bazel/rules/rules_score/docs/user_guide/architectural_design.rst
Original file line number Diff line number Diff line change
Expand Up @@ -270,19 +270,19 @@ Any component-type element (``<<SEooC>>``, ``<<component>>``, or ``<<unit>>``) c

@startuml static_design

package "Safety Software SEooC Example" as safety_software_seooc_example <<SEooC>> {
component "ComponentExample" as component_example <<component>> {
component "Unit 1" as unit_1 <<unit>>
component "Unit 2" as unit_2 <<unit>>
component "Sub Component Example" as sub_component_example <<component>>
package safety_software_seooc_example <<SEooC>> {
component component_example <<component>> {
component unit_1 <<unit>>
component unit_2 <<unit>>
component sub_component_example <<component>>

interface "InternalInterface" as InternalInterface
interface InternalInterface
unit_1 -l-( InternalInterface
unit_2 -r- InternalInterface
}
}

interface "SampleLibraryAPI" as SampleLibraryAPI
interface SampleLibraryAPI

safety_software_seooc_example )-d- SampleLibraryAPI

Expand All @@ -297,17 +297,17 @@ When an element needs an explicitly named, standalone binding point — for exam

@startuml SeoocExample_StaticDesign

package "Safety Software SEooC Example" as safety_software_seooc_example <<SEooC>> {
component "ComponentExample" as component_example <<component>> {
component "Unit 1" as unit_1 <<unit>>
package safety_software_seooc_example <<SEooC>> {
component component_example <<component>> {
component unit_1 <<unit>>
}

portin " " as p_required ' required interface port
portout " " as p_public ' provided interface port
portin p_required ' required interface port
portout p_public ' provided interface port
}

interface "RequiredInterface" as RequiredInterface
interface "SampleLibraryAPI" as SampleLibraryAPI
interface RequiredInterface
interface SampleLibraryAPI

p_required -( RequiredInterface : requires
p_public )- SampleLibraryAPI : provides
Expand All @@ -330,11 +330,11 @@ the same component can appear in both without repeating its full contents:
' overview_design.puml
@startuml overview_design

package "Safety Software SEooC Example" as safety_software_seooc_example <<SEooC>> {
component "ComponentExample" as component_example <<component>>
package safety_software_seooc_example <<SEooC>> {
component component_example <<component>>
}

interface "SampleLibraryAPI" as SampleLibraryAPI
interface SampleLibraryAPI
safety_software_seooc_example )-d- SampleLibraryAPI

@enduml
Expand All @@ -344,10 +344,10 @@ the same component can appear in both without repeating its full contents:
' static_design.puml
@startuml static_design

package "Safety Software SEooC Example" as safety_software_seooc_example <<SEooC>> {
component "ComponentExample" as component_example <<component>> {
component "Unit 1" as unit_1 <<unit>>
component "Unit 2" as unit_2 <<unit>>
package safety_software_seooc_example <<SEooC>> {
component component_example <<component>> {
component unit_1 <<unit>>
component unit_2 <<unit>>
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@

@startuml

package "Integrator SEooC" as integrator_seooc <<SEooC>> {
component "IntegratorComponent" as integrator_component <<component>> {
component "IntegratorUnit" as integrator_unit <<unit>>
package integrator_seooc <<SEooC>> {
component integrator_component <<component>> {
component integrator_unit <<unit>>
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
@startuml

' The package alias must match the dependable_element target name.
package "my_element" as my_element <<SEooC>> {
component "MyComponent" as MyComponent <<component>> {
component "MyUnit" as MyUnit <<unit>>
package my_element <<SEooC>> {
component MyComponent <<component>> {
component MyUnit <<unit>>
}
}

Expand Down
Loading
Loading