# A1–A8 Interactive Architecture Viewpoints Status: design contract, version 0.2, 2026-07-18. This document defines how STEM cards describe architecture, code and runtime evidence. It is a house convention, not a new UML standard. The convention is organized in the spirit of ISO/IEC/IEEE 42010:2022, uses UML 2.5.1 notation, adds selected MARTE 1.3 concepts for real-time systems, and uses PlantUML as the textual source of rendered diagrams. ## Scope and rollout A1–A8 is runtime-independent. The fixed viewpoints are intended to support embedded RTOS software, ordinary C++ object models, POSIX or standard threads, and asynchronous Boost.Asio applications without changing the navigation grammar. The rollout is deliberately staged: 1. **current application:** FreeRTOS C++ cards on RV32I/Hazard3; 2. **later application:** `std::thread`/`std::jthread` and Boost.Asio; 3. **not yet scheduled:** concrete Asio/thread card repositories and lesson numbering. The later profile is specified below so current diagrams do not bake in RTOS- only assumptions. It is a compatibility target, not a claim that the current FreeRTOS card already implements or teaches Asio. ## Standards boundary - **ISO/IEC/IEEE 42010:2022** supplies the concepts of architecture descriptions, viewpoints, views and model kinds. A1–A8 is our concrete set of viewpoints. We do not claim full ISO conformance merely because a card uses these labels. - **UML 2.5.1** supplies the notation for classes, objects, components, interactions, activities and state machines. It does not prescribe A1–A8. - **MARTE 1.3** may annotate schedulable resources, time, hardware and allocation in RTOS cards. A MARTE property must come from configuration, code or measurement; authors must not invent period, deadline or execution time merely to fill a box. - **arc42** is a useful wider documentation template. A1–A8 is the smaller, interactive teaching slice used inside a card, not a replacement for an architecture description of the whole product. - **PlantUML** is the text-first renderer. The `.puml` source, not a manually edited SVG, is authoritative. Primary references: - - - - - - - ## The fixed navigation model The tab names remain stable across cards. A subtitle identifies the actual mechanism being taught. ```text A1 CONTEXT A2 STRUCTURE A3 DISPATCH A4 APPLICATION A5 FLOW A6 STATE A7 RUNTIME A8 PATTERNS ``` These eight tabs ask eight different questions: | ID | Question | Typical model kind | |---|---|---| | A1 CONTEXT | Where is the subject and what lies outside its boundary? | component, package or deployment view | | A2 STRUCTURE | What static elements and relationships construct it? | class, component or object structure | | A3 DISPATCH | How is an abstract call bound to the actual implementation? | class plus sequence/call-path view | | A4 APPLICATION | How does application code specialize or compose it? | application class/object view | | A5 FLOW | What happens in what order? | sequence or activity view | | A6 STATE | Which lifecycles and state transitions exist? | state machine, never an unlabeled flowchart | | A7 RUNTIME | What actually exists in memory and in the debugger now? | object diagram plus evidence overlay | | A8 PATTERNS | Which design roles and trade-offs explain the construction? | participant map plus decision record | Not every card needs all eight views. The top-level navigation keeps all eight positions stable; an omitted view is visibly unavailable and states why. A small card should not create decorative diagrams to fill empty tabs. ## Viewpoint contracts ### A1 CONTEXT **Purpose:** establish system boundary and dependency direction before class details appear. Required content: - subject under study; - upstream application actor or component; - wrapped platform/API; - runtime platform, hardware or simulator when relevant; - one verb on every boundary-crossing relation. For an RTOS wrapper this normally reads: ```text Application task │ specializes / uses ▼ C++ wrapper │ calls ▼ FreeRTOS C API │ schedules on ▼ Hazard3 simulator ``` A1 must not list every method. It is a map, not a class reference. ### A2 STRUCTURE **Purpose:** show the static construction that the compiler sees. Required content: - class/interface/component names; - relevant fields and operations only; - visibility where it is part of the lesson; - precise relationship type: generalization, realization, composition, aggregation or dependency; - storage/ownership role when the structure owns a resource. Class rows receive semantic anchors only when a card can connect them to a source line or a compile-time proof. Inheritance, `friend`, templates and visibility are CODE facts; they are never presented as runtime events. ### A3 DISPATCH **Purpose:** explain the binding mechanism between the generic boundary and the concrete implementation. The title is fixed; the subtitle is mechanism-specific. Examples: ```text A3 DISPATCH · CRTP static dispatch A3 DISPATCH · virtual dispatch through vtable A3 DISPATCH · C callback and function pointer A3 DISPATCH · event to subscriber A3 DISPATCH · executor and completion handler ``` The diagram must identify: 1. the call origin; 2. the erased or abstract boundary; 3. the cast, lookup or binding operation; 4. the concrete target; 5. whether the decision is compile-time or runtime; 6. the ABI proof that distinguishes the mechanism. This viewpoint prevents a wrapper from being mislabeled. A CRTP wrapper must show `static_cast` and the absence of virtual dispatch. A dynamic wrapper must show object address, `vptr`, vtable entry and indirect call. ### A4 APPLICATION **Purpose:** show one concrete use without repeating the complete library. The generic mechanism is collapsed to its public contract. The view maps every pattern or framework role to the names used in the exercise, for example `Strategy -> PaymentStrategy` and `Context -> ShoppingCart`. At least one application invariant is printed next to the model. Example: "`ShoppingCart` depends on the strategy interface, not a payment variant." ### A5 FLOW **Purpose:** show causal order for one named scenario. Rules: - state the precondition and observable postcondition; - use sequence diagrams for collaborating participants and activity diagrams for algorithmic branching; - number messages in the same order as interactive steps; - show returns only when they carry teaching information; - split construction, scheduling and callback dispatch into phases when one page would hide the boundary. A5 is the primary home of RUN steps and deterministic checkpoints. ### A6 STATE **Purpose:** separate lifecycle from execution flow. When two state domains exist, draw two explicit lanes rather than merging them. For a FreeRTOS C++ task these are commonly: ```text C++ object lifetime: absent -> constructed -> alive -> destroyed Kernel task state: Ready <-> Running <-> Blocked; Suspended; Deleted ``` Every transition has a trigger, guard or event. Values returned by platform APIs are platform states, not invented wrapper states. A wrapper-specific state machine is allowed only when the code really stores and enforces it. ### A7 RUNTIME **Purpose:** connect the model to a reproducible observation. A7 is deliberately a hybrid, not pure UML. It combines an object diagram with selected debugger facts: - object and subobject addresses; - `this`, callback context and handles; - fields, ownership pointers, TCB and stack interval; - PC, SP and relevant argument registers; - vptr/vtable target for dynamic dispatch; - symbol/absence-of-symbol proof for static dispatch; - timestamp, target, ELF revision and checkpoint identity. Every value is either captured from a deterministic run or marked as a symbolic example. A screenshot without target, source revision and timestamp is illustration, not evidence. ### A8 PATTERNS **Purpose:** explain why the design has its shape and what it costs. Each claimed pattern records: - intent and problem; - forces/constraints; - canonical participant roles; - exact mapping from role to project class/function; - collaboration that makes the pattern real; - invariant or test; - benefit and cost; - rejected alternative; - conditions in which the pattern should not be used. Pattern names are not decorative stereotypes. `Facade` is valid only when a simpler boundary to a subsystem can be identified. `RAII` is valid only when acquisition and release are safely paired for every legal lifetime. A callback bridge may be an Adapter without making the whole wrapper an Adapter. ## Authored viewpoint record The future machine-readable form should preserve this conceptual record. This is a design target, not yet a required `card_source.json` schema extension: ```json { "id": "a3-dispatch", "number": "A3", "label": "DISPATCH", "subtitle": "Dynamic polymorphism through vtable", "purpose": "Trace the C callback to the concrete C++ override.", "stakeholders": ["student", "teacher", "embedded developer"], "concerns": ["C-to-C++ bridge", "void* context", "vtable", "ABI"], "model_kind": "uml-sequence", "source": "doc/assets/a3-dispatch.puml", "fallback_text": "doc/assets/a3-dispatch.txt", "spread_group": "task-dispatch", "steps": [ { "id": "dispatch-vptr", "number": 4, "mode": "RUN", "svg_target": "dispatch-vptr", "code_ref": "include/FreeRTOS/Task.hpp:110", "snapshot_ref": "task.dispatch.vptr", "evidence": "a0, object address, vptr, vtable entry and jalr target" } ] } ``` Required semantics: - `id`, `number` and `label` are stable teaching identity; - `subtitle` names the actual mechanism and may differ between cards; - `source` is the text source of the rendered asset; - `fallback_text` is a compact readable map, not generated alt text; - `code_ref` is repository-relative and validated; - CODE steps may have no snapshot and never reset the program; - RUN steps require a deterministic replay target; - `evidence` says what proves the claim, not merely what should be visible; - `spread_group` joins logical continuations without changing A4 print pages. ## Plain technical rendering profile "Plain text UML" means two related things in this project: 1. diagrams are authored as reviewable text; 2. their rendered form resembles a restrained technical drawing rather than a presentation slide. The default profile therefore uses: - white background, black/charcoal strokes and no shadows; - square or nearly square corners; - one monospaced family for identifiers, fields, methods and evidence; - one sans-serif family only for explanatory prose; - orthogonal relations where the renderer can keep labels legible; - left-aligned members and short, explicit verbs on relations; - no gradients, icons, ornamental colors or oversized titles; - line weight and spacing, not color, as the primary print hierarchy; - visible step anchors such as `01`–`08` that remain useful in SVG and print; - two muted selectable tones in HTML and one subtle active outline; - non-selectable content in neutral gray, never confused with a selectable inactive element. PlantUML can generate ASCII/Unicode text output for sequence diagrams. We may ship that output as the fallback for sequences. Other diagram kinds receive a small hand-maintained text map because PlantUML's ASCII renderer does not cover all UML kinds. The fallback communicates topology and reading order; SVG and TeX remain the visual outputs. ## Orientation, type scale and page visibility The default reading direction is vertical. A horizontal layout is reserved for a genuine sequence or timeline whose primary fact is execution order over time. A binding chain is vertical unless time or participant lifelines are being modelled explicitly. | Viewpoint | Default orientation | Reason | |---|---|---| | A1 CONTEXT | vertical | responsibility descends from application to platform | | A2 STRUCTURE | vertical | base types, specializations and dependencies form tiers | | A3 DISPATCH | vertical | binding stages form a top-to-bottom chain; use horizontal only for a genuine sequence | | A4 APPLICATION | vertical | application objects lead to observers and invariants | | A5 FLOW | horizontal | messages and time advance in reading order | | A6 STATE | vertical | lifecycle transitions form explicit top-to-bottom paths | | A7 RUNTIME | geometry-dependent | use vertical for an ownership descent, horizontal for a memory/CPU evidence pipeline | | A8 PATTERNS | geometry-dependent | use vertical for tiers and horizontal for a short chain of roles and consequences | Use `top to bottom direction` unless the diagram is a genuine sequence or timeline (normally A5). A diagram must not be made horizontal merely to fill page width. Physical page orientation follows the rendered model rather than the viewpoint name: use one portrait page, one landscape page, or an explicit semantic page composition. Prefer a single page after a deliberate layout change; introduce tiles only when readable labels and complete semantic blocks still cannot fit. The default PlantUML type scale is `14` for ordinary labels, `15` for class names and `13` for class attributes. A smaller value is allowed only when the diagram remains readable at the normal A4 viewer scale. Prefer fewer words, shorter relation labels and a second logical page over shrinking text. All diagrams belonging to one card use one shared PlantUML style include. The style is print-first and keeps the following invariants: - one monospaced family for nodes, relations and sequence messages; - a `190 px` minimum class width, while content may make a node wider; - `1.5 px` node borders and `1.3 px` arrows; - a near-white node fill, blue-grey borders and dark text with sufficient monochrome-print contrast; - the same base styling for class, state and sequence diagrams; - no shadows, decorative gradients or per-diagram colour inventions. Interactive selection is an overlay owned by the viewer. The source diagram must remain fully legible with no selection, while selectable nodes receive a clearer idle treatment than non-interactive context nodes. Selection changes emphasis, not geometry: it must not resize a node, move a label or alter the diagram layout. At `content scale = 100%`, the HTML viewer preserves the intrinsic PlantUML scale. A diagram that exceeds the declared A4-safe viewport is split into semantic tiles; it is not silently compressed to `width: 100%`. The joined HTML spread reconstructs the logical page in a CSS grid, while ordinary view and print retain separate physical A4 sheets. Canvas zoom remains an explicit viewer action and may be used to inspect the joined overview. Every diagram starts on a new A4 page in HTML and TeX. Its SVG must fit within the page-safe margins with all labels, arrowheads and numbered anchors visible. Long diagrams continue on a new page with stable global step numbers and may be joined by the HTML spread view. Every physical tile remains legible on its own, but its `part`, row and column identify its exact position in the logical model. ## Large diagrams and page continuity A viewpoint is one logical model even when it needs several A4 pages. - `page_grid` declares columns, rows and explicit semantic `cuts_x`/`cuts_y`; - a cut may cross whitespace or a connector, but never a class, object, state, participant, note, numbered anchor or other closed semantic block; - the generator validates the rendered SVG geometry and rejects a colliding seam instead of shrinking the diagram or accepting a broken print; - every interactive step belongs to exactly one tile through `step_tiles`; - all tiles use one logical figure number and one global navigation cursor; - each page carries `A`, the same viewpoint title, `part x/y`, and a shared `spread_group`; - duplicated participants at a page boundary are marked `continued`, not treated as new objects; - step numbers remain global across parts; - connectors leaving a page use a named continuation token, for example `D3 -> A5.2`; - HTML can join the parts side by side; print keeps independent A4 pages; - the joined HTML view fits the complete logical row into the viewport, keeps page tops aligned and centers the semantic join rather than either sheet; - a screen-only continuation cue in every part header indicates available neighbours to the left, right, above and below; - selecting a step scrolls to the correct part and preserves one cursor; - no information is encoded only in the physical proximity of two pages. The source diagram may use a modest, explicit PlantUML `scale` when that is necessary to place a semantic seam inside the A4-safe viewport. This is a design decision, not automatic fit-to-width. The resulting normal print must still meet the shared type-scale readability gate; HTML hover/canvas zoom is a convenience and never a substitute for readable print. ## Interaction and correspondence rules The diagram, source tree and debugger share one navigation identity: ```text SERIES -> CARD -> TASK -> BLOCK -> VIEWPOINT/PHASE -> STEP -> SNAPSHOT ``` For every selectable anchor: - its visible step number occurs exactly once inside that diagram; - click and keyboard navigation select the same step; - CODE opens the exact code or binary proof and does not mutate runtime; - RUN opens code and may replay only after explicit activation; - compile-time relations use code, `nm`, `readelf` or disassembly evidence; - runtime messages use checkpoint evidence; - focus is a subtle overlay; unselected selectable targets stay visible; - a missing `snapshot_ref` is valid for CODE and invalid for RUN; - the authored SVG target is validated after PlantUML rendering. ## Diagram-first teaching contract The diagram is the primary entry point into an example. A printed card must teach the algorithm, responsibility boundary, state transition or dispatch mechanism before it exposes implementation syntax. Complete source listings do not belong on the card. The card carries the repository URL/QR code, while an interactive CODE anchor opens only the relevant symbol or source range in Neovim. The intended learning path is: ```text goal and scope -> relevant A1-A8 viewpoint -> numbered diagram element -> CODE source anchor or RUN checkpoint -> Neovim / Termdebug / Hazard3 evidence -> timestamped snapshot ``` Consequently: - a card uses only the A1-A8 viewpoints which materially explain the subject; - the diagram explains the mechanism, source confirms its implementation and the debugger supplies evidence; - CODE navigation does not restart or mutate the program; - RUN navigation may replay only to a declared deterministic checkpoint; - source excerpts may appear temporarily in the interactive Neovim view, but are not duplicated as full static listings in HTML, TeX or print; - every source jump is attached to a concrete diagram element, relation, message or state rather than to an unrelated code catalogue; - the repository link remains the canonical route to the complete source. This order is deliberate: students first reason about algorithms, states and responsibilities, then inspect the C or C++ syntax that implements them. ## Editable UML layout and JSON persistence Interactive UML views may expose an explicit `EDIT` mode. It applies only to UML/model assets; it must not turn the whole card page into a free-form editor or interfere with normal CODE/RUN navigation. PlantUML remains the authoritative semantic source for participants, relationships and diagram kind. Manual visual decisions are stored in a sidecar JSON document named after the source, for example: ```text doc/assets/a2-structure.puml doc/assets/a2-structure.layout.json ``` Saving a layout persists the complete authored presentation state, not only a temporary browser transform. At minimum the sidecar records: - diagram identity, source revision and layout schema version; - canvas size, page orientation, scale and optional page-grid metadata; - stable element identity (`id`/`svg_target`), position and stacking order; - width, height, local scale and rotation when the model kind allows it; - displayed name, stereotype, short description and member text overrides; - font family, font size, weight, style, line height and text alignment; - fill, stroke, border width, corner treatment and semantic style role; - group membership, collapsed state and explicit grouping/ungrouping; - relation endpoints, labels and manually placed routing points; - numbered CODE/RUN anchor placement without changing its teaching identity; - editor metadata such as update timestamp and optional author. A representative record is: ```json { "schema": "stem.uml-layout/v1", "diagram_id": "task01.a2-structure", "source": "a2-structure.puml", "source_digest": "sha256:...", "canvas": { "width": 1120, "height": 760, "orientation": "landscape", "scale": 1 }, "elements": { "task-base": { "x": 310, "y": 72, "width": 360, "height": 290, "z": 2, "text": { "title": "Task", "description": "Static task wrapper", "font_family": "IBM Plex Mono", "font_size": 14, "font_weight": 500, "align": "left" }, "style": { "role": "wrapper", "fill": "#eef5f8", "stroke": "#397c99", "stroke_width": 1.5 } } }, "relations": { "task-to-api": { "from": "task-base", "to": "freertos-api", "label": "calls", "points": [[490, 362], [490, 430], [690, 430]] } }, "updated_at": "2026-07-19T12:00:00+02:00" } ``` Editor requirements: - dragging, resizing, text editing, grouping and relation routing update an in-memory draft first; - `Save layout` validates stable IDs and writes the sidecar atomically; - `Cancel` restores the last saved sidecar without changing PlantUML; - `Reset layout` removes visual overrides and returns to the generated PlantUML geometry only after explicit confirmation; - rerendering PlantUML reapplies compatible overrides by stable ID and reports orphaned overrides instead of silently discarding them; - a source digest mismatch is visible to the editor but does not destroy the saved layout; - normal view and print consume the same saved JSON, so the authored geometry is reproducible outside the editing session; - CODE/RUN step IDs, source references and snapshot identities cannot be renamed accidentally by a visual edit. Exact manual coordinates cannot be represented reliably through PlantUML layout hints alone. The sidecar is therefore the canonical manual presentation layer, while `.puml` remains the canonical semantic layer. ## Two valid A3 profiles for FreeRTOS wrappers The viewpoint set intentionally supports both of these designs. ### Current teaching wrapper in K02 ```text A2 STRUCTURE Task, SumTask, SupervisorTask A3 DISPATCH CRTP static dispatch void* -> Task* -> Derived* -> run() A7 RUNTIME same object address; direct/inlined target; no vtable symbol ``` This matches the current K02 acceptance gate: no virtual dispatch, vtable, RTTI or hosted runtime. ### `jonenz/FreeRTOS-Cpp` reference design ```text A2 STRUCTURE TaskBase <- Task / StaticTask <- application task A3 DISPATCH dynamic dispatch void* -> TaskBase* -> taskEntry() -> virtual taskFunction() A7 RUNTIME object address, vptr, vtable entry and indirect target ``` The upstream `Task.hpp` creates dynamic/static kernel tasks in constructors, passes `this` to `callTaskFunction(void*)`, and uses a virtual `taskFunction()`. Its `TaskBase` destructor may call `vTaskDelete(handle)` when configured. These are reference facts, not automatic design choices for our teaching wrapper. Adopting that design would require changing K02's lifecycle, ABI and no-vtable acceptance contracts explicitly. Reference: ## Target profile: Boost.Asio and standard threads This is the required future compatibility profile. It does not rename the fixed tabs. Mechanism-specific wording belongs in subtitles: ```text A1 CONTEXT · application, Asio, io_context, worker threads, OS A2 STRUCTURE · Server, Session, socket, strand and ThreadPool A3 DISPATCH · executor and completion-handler dispatch A4 APPLICATION · one concrete Session or Client A5 FLOW · asynchronous initiation and completion A6 STATE · operation, session and thread lifecycles A7 RUNTIME · threads, executors, handlers and owned buffers A8 PATTERNS · Proactor and verified collaboration roles ``` The fixed label remains `A5 FLOW`, not `A5 ASYNC FLOW`; the UI subtitle may be `Async completion flow`. Likewise the fixed label remains `A7 RUNTIME`, with `Threads / executors` as a subtitle. This keeps cards comparable while still naming the actual mechanism. ### A1–A8 mapping **A1 CONTEXT** shows this dependency/execution stack: ```text Application Session / Server │ initiates ▼ Boost.Asio async operations, executors and strands │ queue completions in ▼ io_context │ run() is called by ▼ std::thread / std::jthread workers │ use ▼ operating-system sockets and scheduling ``` **A2 STRUCTURE** shows the static ownership and dependency graph: `Server`, `Session`, `tcp::socket`, executor/strand, buffers, `io_context` and the worker collection. It must make lifetime ownership explicit, especially when a session uses `shared_from_this()` to outlive an initiating call. **A3 DISPATCH** traces an initiating function through the outstanding operation, associated executor, completion queue and selected handler. It must not draw `async_read` as if it created a user thread. Completion handlers are invoked only by threads currently running `io_context::run()`; several such threads form a pool from which `io_context` may choose. A strand adds strictly sequential handler invocation, not physical affinity to one thread. **A4 APPLICATION** collapses Asio infrastructure and shows one concrete protocol/session contract, for example `EchoSession::start/read/onRead/write`. **A5 FLOW** uses the explicit sequence: ```text initiate -> outstanding -> operation completes -> handler queued -> one run() thread invokes handler -> next operation or completion ``` **A6 STATE** separates at least three domains when present: ```text jthread object: absent -> joinable -> stop requested -> joined async operation: initiated -> outstanding -> queued -> running -> done application Session: Created -> Reading -> Processing -> Writing -> Closed ``` The `std::jthread` destructor requests stop and joins when the object is still joinable. A session state is application data; it must not be inferred merely from a handler being present in a queue. **A7 RUNTIME** records `io_context`, worker IDs, current handler and thread, executor/strand identity, socket descriptor, operation kind, session address, buffer lifetime and relevant smart-pointer ownership. The evidence view uses `info threads`, backtraces and object inspection rather than RTOS TCB fields. **A8 PATTERNS** may identify Proactor, callback/continuation, serialized execution through a strand, thread pool, RAII thread ownership and lifetime management. These are classified carefully: `std::jthread` ownership is a C++ idiom, a thread pool is a concurrency architecture, and not every completion handler constitutes a GoF Command. Boost.Asio documents its asynchronous model as Proactor; the card must still map each claimed role to project code. The central invariant for every future Asio card is: ```text asynchronous operation != thread operation completion -> associated executor -> io_context -> one thread currently executing run() ``` Primary references for this profile: - - - - - ## Definition of done for a card view A viewpoint is ready when: 1. its purpose and question are stated; 2. the chosen UML/model kind fits that question; 3. every relation has one unambiguous meaning; 4. every selectable item has a stable anchor and code reference; 5. runtime claims have reproducible evidence; 6. compile-time claims use compiler/binary evidence rather than fake runtime; 7. the text fallback remains understandable without SVG; 8. the view prints in monochrome and works as an interactive HTML asset; 9. continuation across pages preserves identity and step order; 10. A8 names costs and rejected alternatives, not only benefits.