openup: architecture notebook + EA diagram renders
All checks were successful
build / build-test (push) Successful in 44s

- analysis-design/architecture-notebook.md: Architecture Notebook work
  product in the keyfob docx template structure (10 sections), grounded
  in mapping / design note / supplementary specs; embeds the component
  model and all 12 realization figures
- scripts/openup/export-diagram-images.ps1: read-only EA COM renderer
  (EA-15 findings: image API on ProjectInterface, OpenDiagram before
  export or blank stub, type 0 = PNG); renders reflect manual layouts
- renders: requirements/use-case-overview.png, analysis-design/
  uc-01..12-realization.png + component-architecture.png; embedded as
  figures in use-case-model.md and uc-realization-mapping.md
- BUILD-CHECKLIST 6.4 documents the exporter
This commit is contained in:
Jan-Peter von Hunnius 2026-10-05 10:00:45 +02:00
parent c0226df62d
commit 6e5d3fb45a
19 changed files with 351 additions and 0 deletions

View file

@ -0,0 +1,244 @@
# Skara Brae — Architecture Notebook
Version 1.0 (2026-10-05) · OpenUP Analysis & Design artifact: `analysis-design/architecture-notebook.md`
Summary work product in the keyfob-station template structure. Sources (all in-repo, normative):
`analysis-design/uc-realization-mapping.md` (§1–§3), `analysis-design/design-wi-2-architecture-cleanup.md`
(§2–§4, §11), `requirements/supplementary-specifications.md`, `requirements/vision.md`, and the EA model
`docs/openup/model/skara-brae.eapx` (exports under `docs/openup/model/exports/`). Detailed per-WI design
remains in the WI design notes; this notebook is the cross-WI architecture view. Drift rule applies:
a mismatch between this notebook and its sources is a defect fixed in the same cycle.
## 1. Purpose
This notebook describes the goals, decisions, constraints and significant elements that shape the design of
Skara Brae across work items. The analysis and design models live in the EA model (Requirements / Use Case
Model, Analysis-Design realizations and Design component model); this notebook summarizes them for review
and onboarding. One model covers the whole product — server, client and common — because the use cases are
player-facing and Requirement→realization traceability spans tiers (joint-model decision, 2026-10-03).
## 2. Architectural Goals and Philosophy
- **Dependency rule inward** (Clean Architecture): domain in `sb-core`/`sb-shared` with zero HTTP/framework
knowledge; adapters (`sb-api`, `sb-db`, `sb-auth`) and the client at the edge (D1, CON-05).
- **Behavior continuity**: refactors are wire-format and game-semantics identical (NFR-11, NFR-19).
- **Determinism by construction**: domain randomness is injected (WI-1); identical seeds produce identical
outcomes (NFR-05, D5).
- **One taxonomy of failure**: a single domain error enum with one central HTTP status mapping (D2).
- **Data as source of truth**: game content is language-neutral JSON in `skara-brae-common`, compiled into
both consumers (NFR-13, D6).
- **Zero-warning quality gate**: fmt + clippy + full test suite on every commit (NFR-08..NFR-10).
- **Docs are architecture**: OpenUP markdown is the content source; the EA model renders it; drift is a
defect (D7).
## 3. Assumptions and Dependencies
- Single-process Rust server (Axum 0.8, edition 2024); all game state in memory behind `tokio::sync::RwLock`,
optional write-through persistence to SQLite via `SB_DB_PATH` (NFR-01, NFR-14).
- Static game data ships with the binary via `include_str!` from `skara-brae-common` (NFR-04, NFR-13).
- Clients are web/PWA (Vue 3 SPA) polling the REST API; server behavior is poll-frequency-independent
(NFR-02, NFR-03).
- `sb-common` is consumed as a sibling absolute-path dependency locally; git-submodule migration planned
(CON-01).
- Self-hosted pipeline: Forgejo (git), Jenkins CI, systemd deployment (vision.md; topology canon in Joplin
"Tech Stack & Architecture" note).
## 4. Architecturally Significant Requirements
The full set is `requirements/supplementary-specifications.md`; the categories that shape the architecture:
| Category | IDs | Architectural consequence |
|---|---|---|
| Performance / Scalability | NFR-01..NFR-04 | in-memory state, lazy regen (no timers), lock-free catalogue reads |
| Reliability / Correctness | NFR-05..NFR-07 | injected RNG, best-effort persistence, referential data integrity → `DomainError` |
| Quality Gates | NFR-08..NFR-12 | fmt/clippy/test gates; unwrap ledger; behavior-identical refactors |
| Data | NFR-13..NFR-15 | bilingual common package, SQLite format, i64 stat growth |
| Security | NFR-16..NFR-18 | argon2, JWT HS256 + auth middleware, role model (player/admin) |
| Constraints | CON-01..CON-06 | crate layout, Axum 0.8, rand 0.9 rule, one error enum, dependency rule, docs home |
| Usability / API | NFR-19..NFR-20 | wire-format freeze (lexicographic key order), status-code contract |
## 5. Decisions, Constraints, and Justifications
Full records in the WI design notes; the architecture-level decisions:
- **D1 — Layered crate architecture with inward dependencies.** `sb-api` (boundary/adapter) may import
`sb-core`, `sb-shared`, `sb-common`, `sb-db`, `sb-auth`; `sb-core` must not reference axum/http/
serde_json-json!/tokio/sb-db/sb-api (design note §3). Known debt: ShopService, HealingService, parts of
MagicService remain handler-side in sb-api — recorded, follow-up WI question Q3; the model must not paper
over them.
- **D2 — One domain error taxonomy.** `sb_core::error::DomainError` (thiserror 2) + single status mapping
`sb-api::routes::domain_status` realize the error paths of UC-05/06/08/12 (design note §4.1).
- **D3 — Constants centralized.** `sb_core::constants` is the single audit home for game-balance scalars
(NFR "no magic numbers"); static data tables stay in their modules (design note §2).
- **D4 — Response DTOs in sb-api, not sb-shared.** HTTP wire types never leak into the domain crate;
`AdventureOutcome` derives nothing (design note §2 placement decisions).
- **D5 — Injectable RNG with a single construction point.** `request_rng()` (`StdRng::from_os_rng`, WI-1);
domain functions take `rng: &mut impl Rng` (CON-03, NFR-05).
- **D6 — Bilingual shared data package.** `skara-brae-common`: `data/` JSON single source, `rust/` + `ts/`
thin consumers, `codegen/` planned (NFR-13; tech-stack canon).
- **D7 — Joint EA model, markdown source of truth, surgical builders.** Model changes after the manual
layout session go through surgical scripts only; `build-model.ps1` rebuilds and is forbidden on the laid-
out model (mapping §1, BUILD-CHECKLIST §6).
## 6. Architectural Mechanisms
- **Lazy resource regeneration** — resources recompute from elapsed wall-clock time at specific call sites;
no timers or background ticks (NFR-02; UC-05/UC-08 "Regenerate Resources" sub-flow).
- **Static catalogue via LazyLock** — game data parsed once at first access; lock-free reads (NFR-04, UC-04).
- **DomainError → status mapping** — domain rule failures flow through one enum to one mapping; handlers own
store misses/authz (D2, NFR-20).
- **Wire-format freeze** — JSON keys serialize in lexicographic order; typed responses declare fields in that
order (NFR-19).
- **Injectable RNG determinism** — seeded domain tests, single entropy entry point (D5, NFR-05).
- **Best-effort write-through persistence** — SQLite behind an in-memory-authoritative process (NFR-06,
NFR-14).
## 7. Key Abstractions
The analysis vocabulary (boundary/controller/entity) of the use-case realizations — full mapping incl.
design elements in `uc-realization-mapping.md` §2; classes, stereotypes and interactions per UC in the EA
model (§2a lists the 24 associations + 4 «uses» dependencies).
| Class | Stereotype | Responsibility (design element) |
|---|---|---|
| AuthBoundary | «boundary» | `POST /api/auth/register`, `AuthResponse` (UC-01) |
| TokenService | «boundary» | `POST /api/auth/login`, `AuthResponse`, auth middleware (UC-02, guards UC-03…12) |
| CharacterCatalog | «boundary» | character routes (UC-03) |
| CatalogueBrowser | «boundary» | catalogue routes + `sb-shared::responses` (UC-04) |
| GymBoundary | «boundary» | gym routes + responses (UC-05) |
| EquipmentBoundary | «boundary» | equip/unequip routes (UC-06) |
| ShopBoundary | «boundary» | shop routes + responses (UC-07) |
| AdventureBoundary | «boundary» | adventure route + `AdventureRunResponse` (UC-08) |
| TempleBoundary | «boundary» | heal route + `HealResponse` (UC-09) |
| SongBoundary | «boundary» | song routes + responses (UC-10) |
| MagicBoundary | «boundary» | spell routes + responses (UC-11) |
| ReviewBoundary | «boundary» | level-up route + response (UC-12) |
| AccountRegistry | «controller» | `sb-api::routes::{register, seed_users}` (UC-01) |
| Authenticator | «controller» | `sb-api::routes::login` + sb-auth verify/create (UC-02) |
| CharacterFactory | «controller» | `sb-core::character::create_character` (UC-03) |
| TrainingService | «controller» | `sb-core::services::train_stat` (UC-05) |
| EquipmentService | «controller» | `sb-core::services::{equip_item, unequip_item}` (UC-06) |
| ShopService | «controller» | `sb-api::routes::{shop_action, buy_item, sell_item}` (UC-07; **debt**: handler-side, extraction candidate Q3) |
| AdventureService | «controller» | `sb-core::adventure::run_adventure` (UC-08; WI-2 extraction) |
| HealingService | «controller» | `sb-api::routes::heal_character` (UC-09; **debt**: handler-side, extraction candidate Q3) |
| SongService | «controller» | `sb-api::routes::set_song` + `available_songs` (UC-10) |
| MagicService | «controller» | spell routes + `sb-core::spells` helpers (UC-11; **debt**: parts handler-side) |
| ReviewBoard | «controller» | `sb-core::character::level_up` (UC-12) |
| UserAccount | «entity» | `sb-shared::user::{User, Role}` (UC-01/02) |
| Character | «entity» | `sb-shared::character::Character` + Race/Class/Stats (UC-03) |
| ResourceRegenerator | «entity» | `sb-core::character::apply_regen` + regen constants (UC-03/05/08 sub-flow) |
| GameData | «entity» | `sb-core::data::{EnemyDef, ZoneDef, EncounterEntry, LootDrop}` (UC-04) |
| TrainingGrounds | «entity» | `TRAINING_GROUNDS`, `training_cost` (UC-05) |
| Equipment/Inventory | «entity» | `Character.equipment/.inventory`, `sb-shared::item::Item` (UC-06) |
| ShopStock | «entity» | `AppState.shop_stock` (UC-07) |
| CombatEngine | «entity» | `sb-core::combat::{resolve_combat_full, resolve_attack, crit tables, open_ended_roll}` (UC-08) |
| Combatant | «entity» | `sb-core::combat::CombatStats` (UC-08) |
| SpellEffect / BardSong | «entity» | `SpellEffect`, `sb-core::spells`, `sb-core::songs` (UC-08/10/11) |
| BardSong | «entity» | `BARD_SONGS`, `Character.active_song` (UC-10) |
| Spell | «entity» | `SpellDef`, `Character.{known_spells, active_spell, spell_points}` (UC-11) |
| Progression | «entity» | `xp_for_level`, `level_up_cost`, `MAX_LEVEL` (UC-12) |
Cross-cutting: `DomainError` (error paths UC-05/06/08/12), `Constants` (no-magic-numbers NFR), `Responses`
(wire-format freeze NFR-19) — carried in the model's Architecture Overview package.
## 8. Layers
The joint component model (mapping §3); dependencies point inward only.
| Tier | Component | Role |
|---|---|---|
| Presentation | skara-brae-client | Vue 3.5 SPA (Pinia, Vite), JWT bearer, REST via proxy; player-facing boundary of UC-01…12 |
| Boundary / adapter | sb-api | Axum routes/handlers, `domain_status`, `responses`; boundaries of all UCs |
| Adapter | sb-auth | argon2 + JWT (UC-01/02; middleware guards UC-03…12) |
| Adapter | sb-db | persistence via `sb-db::Database` |
| Domain | sb-core | controllers/entities: adventure, services, character, combat, spells, songs, data/items |
| Domain types | sb-shared | `User`, `Character`, `Item`, response types; zero HTTP imports |
| Data | sb-common | bilingual data package (`data/` JSON, `rust/`, `ts/`, `codegen/`); UC-04 data source |
![Component Architecture](component-architecture.png)
*Figure 1: Joint component architecture — server crates, client, common (EA model render).*
## 9. Architectural Views
### 9.1 Use Case View
Architecturally significant use cases: UC-08 (Run Adventure — WI-2 design driver, combat + data integrity),
UC-03/UC-12 (character lifecycle), UC-04 (shared-data consumption), UC-05/06/07 (rule-driven economy
actions with the DomainError error paths), UC-01/02 (auth, middleware guard). Full model:
`requirements/use-case-model.md` + overview figure there.
| Realization | Use Case | Analysis classes |
|---|---|---|
| UC-01 Realization | Register Account | AuthBoundary, AccountRegistry, UserAccount |
| UC-02 Realization | Authenticate | TokenService, Authenticator |
| UC-03 Realization | Manage Characters | CharacterCatalog, CharacterFactory, Character, ResourceRegenerator |
| UC-04 Realization | Browse Game Catalogue | CatalogueBrowser, GameData |
| UC-05 Realization | Train Stat | GymBoundary, TrainingService, TrainingGrounds |
| UC-06 Realization | Manage Equipment | EquipmentBoundary, EquipmentService, Equipment/Inventory |
| UC-07 Realization | Trade at Shop | ShopBoundary, ShopService, ShopStock |
| UC-08 Realization | Run Adventure | AdventureBoundary, AdventureService, CombatEngine, Combatant, SpellEffect / BardSong |
| UC-09 Realization | Heal at Temple | TempleBoundary, HealingService |
| UC-10 Realization | Practice Bard Song | SongBoundary, SongService, BardSong |
| UC-11 Realization | Study Magic | MagicBoundary, MagicService, Spell |
| UC-12 Realization | Level Up at Review Board | ReviewBoundary, ReviewBoard, Progression |
### 9.2 Analysis View (Use-Case Realizations)
Each realization package in the EA model carries a class diagram (boundary/controller/entity with §2a
associations); renders in this folder, generated by `scripts/openup/export-diagram-images.ps1`:
![UC-01 Realization](uc-01-realization.png)
![UC-02 Realization](uc-02-realization.png)
![UC-03 Realization](uc-03-realization.png)
![UC-04 Realization](uc-04-realization.png)
![UC-05 Realization](uc-05-realization.png)
![UC-06 Realization](uc-06-realization.png)
![UC-07 Realization](uc-07-realization.png)
![UC-08 Realization](uc-08-realization.png)
![UC-09 Realization](uc-09-realization.png)
![UC-10 Realization](uc-10-realization.png)
![UC-11 Realization](uc-11-realization.png)
![UC-12 Realization](uc-12-realization.png)
### 9.3 Interaction View
Not yet modeled: no sequence diagrams exist for the Skara Brae MVP (the EA model carries scenarios with
per-step actors, not interactions). Candidate first sequences: UC-08 adventure request→combat→loot, and
UC-01/02 auth flows. Adding them is a future work item (PlantUML prototype → EA per the workflow canon).
### 9.4 Operational View
Single Rust process per deployment (systemd unit on the self-hosted boxes; Forgejo + Jenkins pipeline per
vision.md). All state in memory, optional write-through SQLite (NFR-01/06/14); no Redis for MVP. The Vue SPA
is served statically and talks to the API over HTTPS (Caddy, tech-stack canon). Clients poll; no push
channel in the MVP (WebSocket notifications are a later-phase feature, vision.md).
### 9.5 Design Diagram Index
| Diagram | File | Documents |
|---|---|---|
| Use Case Overview | `../requirements/use-case-overview.png` | actors, use cases, associations |
| UC-01…UC-12 Realization | `uc-01-realization.png` … `uc-12-realization.png` | analysis classes + associations per UC |
| Component Architecture | `component-architecture.png` | joint component model, dependency rule |
All three sets are renders of the EA model (`docs/openup/model/skara-brae.eapx`) produced by
`scripts/openup/export-diagram-images.ps1`; regenerate after model changes (read-only, layout-preserving).
## 10. References
- `requirements/vision.md`, `requirements/use-case-model.md`, `requirements/supplementary-specifications.md`
- `analysis-design/uc-realization-mapping.md` (§1 structure, §2 mapping, §2a interactions, §3 component model)
- `analysis-design/design-wi-1-injectable-rng.md`, `design-wi-2-architecture-cleanup.md` (detailed WI designs)
- EA model: `docs/openup/model/skara-brae.eapx` (+ `exports/` JSON, `BUILD-CHECKLIST.md`)
- Client-side UC realizations: future work item (joint-model decision, mapping §1)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -144,6 +144,10 @@ sb-api → {sb-core, sb-shared, sb-db, sb-auth, sb-common}; sb-core → {sb-shar
sb-db → sb-shared; sb-auth → sb-shared. The dependency rule: inward toward
sb-core/sb-shared (domain); adapters (sb-api/sb-db/sb-auth) and the client at the edge.
![Component Architecture](component-architecture.png)
*Figure: joint component architecture (rendered from the EA model via `scripts/openup/export-diagram-images.ps1`). Per-UC realization figures: `uc-01-realization.png` … `uc-12-realization.png` in this folder; they are embedded in the Architecture Notebook (§9.2).*
## 4. Research summary — keyfob-station EA pipeline (reference)
Source: read-only clone `http://192.168.1.10:3000/jan/keyfob-station.git` (inspected 2026-10-03; no modifications).

View file

@ -240,3 +240,12 @@ would destroy manual layout). Sources: `uc-realization-mapping.md` §2a (interac
associations; Requirements tree untouched.
4. Manual layout preserved: the scripts add no/modify no DiagramObjects in existing
diagrams (only the new `Design` diagram carries its own object layout).
### 6.4 Diagram image export — `scripts/openup/export-diagram-images.ps1`
Read-only on the model: renders 14 diagrams to PNG for the markdown documents
(`requirements/use-case-overview.png`, `analysis-design/uc-01..uc-12-realization.png`,
`analysis-design/component-architecture.png`). EA-15 findings baked into the script:
image methods live on **ProjectInterface** (not Repository); `PutDiagramImageToFile(id,
path, 0)` = PNG; the diagram must be `OpenDiagram`-ed first or a blank 241×151 stub is
written. Regenerate after model changes; renders reflect the manual layout.

View file

@ -28,6 +28,10 @@ Scope: the **existing implemented system** (server MVP as of `wi-2-architecture-
| UC-11 | Study Magic | Player | — |
| UC-12 | Level Up at Review Board | Player | — |
![Skara Brae Use Case Overview](use-case-overview.png)
*Figure 1: Use case overview (rendered from the EA model, `docs/openup/model/skara-brae.eapx`, via `scripts/openup/export-diagram-images.ps1`).*
Shared sub-flow **Regenerate Resources**: the system recomputes time-based regeneration **at specific call sites only** — list characters, view character, run adventure, and train stat (routes/mod.rs:264–266, 331, 442, 642). Energy +1 per 180 s, nerve +1 per 300 s, HP +5% of max per 300 s (only if alive and injured), capped at maxima; `last_regen` advances to now. It is **not** applied by equip/unequip, gym view, shop, heal, songs, spells, or level-up.
---

Binary file not shown.