openup: doc restructure - template alignment + design sequences (non-PSP)
Align the OpenUP markdown set to the blank templates (process-data repo, templates/openup/) per BUILD-CHECKLIST 7. Scenario step text unchanged; the EA model needs no change (brief descriptions already live in the model's UC/actor notes). - vision.md: positioning tables, stakeholder summary, user environment, needs-and-features table (R1/R2), other product requirements - use-case-model.md: per-UC specification sections (brief description, actor descriptions, named alternative/exception subsections with numbered steps, post-conditions, special requirements) - supplementary-specifications.md: category tables with priority and use-case columns + reverse coverage matrix; IDs unchanged - test/quality-check-procedure.md: migrated from Joplin (single content home); stale open items marked resolved (WI-1/WI-2) - analysis-design/sequences/: 12 PlantUML design sequences (lifelines = analysis classes, messages = design elements; per the PlantUML layout style guide) + PNG renders; embedded in Architecture Notebook 9.3 - Joplin consolidated: project content notes deleted (all live in docs/game + docs/openup); Joplin keeps the status note only
BIN
docs/openup/analysis-design/sequences/uc-01-register.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
23
docs/openup/analysis-design/sequences/uc-01-register.puml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
@startuml uc-01-register
|
||||
autonumber
|
||||
title UC-01 Register Account (design sequence)
|
||||
actor Guest
|
||||
participant "AuthBoundary\n«POST /api/auth/register»" as boundary <<boundary>>
|
||||
participant "AccountRegistry\nsb-api::routes::register" as control <<control>>
|
||||
participant "UserAccount\nsb-shared::user::User" as entity <<entity>>
|
||||
|
||||
== Register ==
|
||||
Guest -> boundary : name (3-32), password (>= 6)
|
||||
boundary -> control : register(name, password)
|
||||
alt name invalid / password too short
|
||||
control --> boundary : 400
|
||||
else name taken
|
||||
control --> boundary : 409 CONFLICT
|
||||
else valid
|
||||
control -> entity : hash password (argon2, sb-auth)
|
||||
control -> entity : assign player role + next id
|
||||
control -> entity : persist (sb-db)
|
||||
control --> boundary : JWT HS256 (player role)
|
||||
boundary --> Guest : {token, user_id, name, role}
|
||||
end
|
||||
@enduml
|
||||
BIN
docs/openup/analysis-design/sequences/uc-02-authenticate.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
|
|
@ -0,0 +1,20 @@
|
|||
@startuml uc-02-authenticate
|
||||
autonumber
|
||||
title UC-02 Authenticate (design sequence)
|
||||
actor Guest
|
||||
participant "TokenService\n«POST /api/auth/login»" as boundary <<boundary>>
|
||||
participant "Authenticator\nsb-api::routes::login" as control <<control>>
|
||||
|
||||
== Login ==
|
||||
Guest -> boundary : name, password
|
||||
boundary -> control : login(name, password)
|
||||
alt unknown name or wrong password
|
||||
control --> boundary : 401 UNAUTHORIZED
|
||||
else valid
|
||||
control -> control : verify_password (sb-auth::hash)
|
||||
control -> control : create_token (sb-auth::jwt, real role)
|
||||
control --> boundary : {token, user_id, name, role}
|
||||
boundary --> Guest : {token, user_id, name, role}
|
||||
end
|
||||
note over boundary : auth_middleware guards\nUC-03..UC-12
|
||||
@enduml
|
||||
|
After Width: | Height: | Size: 70 KiB |
|
|
@ -0,0 +1,31 @@
|
|||
@startuml uc-03-manage-characters
|
||||
autonumber
|
||||
title UC-03 Manage Characters (design sequence)
|
||||
actor Player
|
||||
participant "CharacterCatalog\n«/api/users/{id}/characters»" as boundary <<boundary>>
|
||||
participant "CharacterFactory\nsb-core::character::create_character" as control <<control>>
|
||||
participant "Character\nsb-shared::character" as entity <<entity>>
|
||||
participant "ResourceRegenerator\napply_regen" as regen <<entity>>
|
||||
|
||||
== Create ==
|
||||
Player -> boundary : POST name, race, class (user id)
|
||||
boundary -> control : create_character(rng, user, name, race, class)
|
||||
alt user missing
|
||||
control --> boundary : 404
|
||||
else name invalid
|
||||
control --> boundary : 400
|
||||
else valid
|
||||
control -> entity : roll stats (d6+5 +- race mods)
|
||||
control -> entity : max HP/energy/nerve from class
|
||||
control -> entity : gold 50 + level-1 spells (casters)
|
||||
control -> entity : assign next character id, persist
|
||||
control --> boundary : character
|
||||
boundary --> Player : character (full)
|
||||
end
|
||||
|
||||
== List / View ==
|
||||
Player -> boundary : GET characters (own; admin: any)
|
||||
boundary -> regen : apply_regen(character)
|
||||
regen --> boundary : character (regenerated)
|
||||
boundary --> Player : characters (regen applied)
|
||||
@enduml
|
||||
BIN
docs/openup/analysis-design/sequences/uc-04-browse-catalogue.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
|
|
@ -0,0 +1,17 @@
|
|||
@startuml uc-04-browse-catalogue
|
||||
autonumber
|
||||
title UC-04 Browse Game Catalogue (design sequence)
|
||||
actor Player
|
||||
participant "CatalogueBrowser\n«/api/{races,classes,items,enemies,adventures}»" as boundary <<boundary>>
|
||||
participant "GameData\nsb-core::data + items" as entity <<entity>>
|
||||
|
||||
== Browse ==
|
||||
Player -> boundary : GET catalogue (optional type/quality/slot filters)
|
||||
boundary -> entity : read LazyLock catalogue (sb-common JSON)
|
||||
alt unknown item id
|
||||
entity --> boundary : 404
|
||||
else hit (possibly empty filter result)
|
||||
entity --> boundary : static data (classes + is_caster)
|
||||
boundary --> Player : races / classes / items / enemies / zones
|
||||
end
|
||||
@enduml
|
||||
BIN
docs/openup/analysis-design/sequences/uc-05-train-stat.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
28
docs/openup/analysis-design/sequences/uc-05-train-stat.puml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
@startuml uc-05-train-stat
|
||||
autonumber
|
||||
title UC-05 Train Stat (design sequence)
|
||||
actor Player
|
||||
participant "GymBoundary\n«/api/characters/{id}/gym»" as boundary <<boundary>>
|
||||
participant "TrainingService\nsb-core::services::train_stat" as control <<control>>
|
||||
participant "ResourceRegenerator\napply_regen" as regen <<entity>>
|
||||
participant "TrainingGrounds\nTRAINING_GROUNDS + training_cost" as entity <<entity>>
|
||||
|
||||
== Train ==
|
||||
Player -> boundary : POST stat name
|
||||
boundary -> regen : apply_regen(character)
|
||||
regen --> boundary : character (regenerated)
|
||||
boundary -> control : train_stat(character, stat)
|
||||
control -> entity : cost = training_cost (x1.3 per 10 points)
|
||||
alt not enough energy
|
||||
control --> boundary : 422 (DomainError)
|
||||
else not enough gold
|
||||
control --> boundary : 422 (DomainError)
|
||||
else unknown stat
|
||||
control --> boundary : 400 (DomainError)
|
||||
else ok
|
||||
control -> entity : stat +1 (energy -1, gold -cost)
|
||||
control -> entity : constitution: recompute max HP (clamp current)
|
||||
control --> boundary : {success, message, character}
|
||||
boundary --> Player : {success, message, character}
|
||||
end
|
||||
@enduml
|
||||
BIN
docs/openup/analysis-design/sequences/uc-06-manage-equipment.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
|
|
@ -0,0 +1,30 @@
|
|||
@startuml uc-06-manage-equipment
|
||||
autonumber
|
||||
title UC-06 Manage Equipment (design sequence)
|
||||
actor Player
|
||||
participant "EquipmentBoundary\n«/api/characters/{id}/{equip,unequip}»" as boundary <<boundary>>
|
||||
participant "EquipmentService\nsb-core::services::{equip,unequip}" as control <<control>>
|
||||
participant "Equipment/Inventory\nCharacter.equipment/.inventory" as entity <<entity>>
|
||||
|
||||
== Equip ==
|
||||
Player -> boundary : POST equip item id
|
||||
boundary -> control : equip_item(character, item)
|
||||
alt item not in inventory
|
||||
control --> boundary : 400 (DomainError)
|
||||
else ok
|
||||
control -> entity : inventory -> slot\n(previous item returns to inventory)
|
||||
control --> boundary : character
|
||||
boundary --> Player : updated character
|
||||
end
|
||||
|
||||
== Unequip ==
|
||||
Player -> boundary : POST unequip slot
|
||||
boundary -> control : unequip_item(character, slot)
|
||||
alt slot empty
|
||||
control --> boundary : 400 (DomainError)
|
||||
else ok
|
||||
control -> entity : slot -> inventory
|
||||
control --> boundary : character
|
||||
boundary --> Player : updated character
|
||||
end
|
||||
@enduml
|
||||
BIN
docs/openup/analysis-design/sequences/uc-07-trade-at-shop.png
Normal file
|
After Width: | Height: | Size: 75 KiB |
|
|
@ -0,0 +1,37 @@
|
|||
@startuml uc-07-trade-at-shop
|
||||
autonumber
|
||||
title UC-07 Trade at Shop (design sequence)
|
||||
actor Player
|
||||
participant "ShopBoundary\n«/api/characters/{id}/shop»" as boundary <<boundary>>
|
||||
participant "ShopService\nsb-api::routes::{shop_action,buy,sell}" as control <<control>>
|
||||
participant "ShopStock\nAppState.shop_stock" as entity <<entity>>
|
||||
|
||||
== View ==
|
||||
Player -> boundary : GET shop
|
||||
boundary --> Player : always-stocked + rotating stock, inventory
|
||||
|
||||
== Buy ==
|
||||
Player -> boundary : POST buy + item id
|
||||
boundary -> control : buy_item(character, item)
|
||||
control -> entity : check stock (non-permanent: decrement)
|
||||
alt stock exhausted
|
||||
entity --> control : 0 remaining
|
||||
control --> boundary : 410 GONE
|
||||
else gold < price
|
||||
control --> boundary : 422
|
||||
else ok
|
||||
control -> entity : stock -1, gold -price, inventory +item
|
||||
control --> boundary : {success, message, gold}
|
||||
boundary --> Player : {success, message, gold}
|
||||
end
|
||||
|
||||
== Sell ==
|
||||
Player -> boundary : POST sell + item id
|
||||
boundary -> control : sell_item(character, item)
|
||||
alt item not owned / unknown action
|
||||
control --> boundary : 400
|
||||
else ok
|
||||
control -> entity : inventory -item, gold +value/2, stock +1 (non-permanent)
|
||||
boundary --> Player : {success, message, gold}
|
||||
end
|
||||
@enduml
|
||||
BIN
docs/openup/analysis-design/sequences/uc-08-run-adventure.png
Normal file
|
After Width: | Height: | Size: 119 KiB |
|
|
@ -0,0 +1,52 @@
|
|||
@startuml uc-08-run-adventure
|
||||
autonumber
|
||||
title UC-08 Run Adventure (design sequence)
|
||||
actor Player
|
||||
participant "AdventureBoundary\n«/api/characters/{id}/adventure»" as boundary <<boundary>>
|
||||
participant "AdventureService\nsb-core::adventure::run_adventure" as control <<control>>
|
||||
participant "ResourceRegenerator\napply_regen" as regen <<entity>>
|
||||
participant "CombatEngine\nsb-core::combat" as combat <<entity>>
|
||||
participant "Combatant\nCombatStats" as combatant <<entity>>
|
||||
participant "SpellEffect / BardSong\nsb-core::{spells,songs}" as fx <<entity>>
|
||||
|
||||
== Validate ==
|
||||
Player -> boundary : POST adventure (zone, default skara_brae_streets)
|
||||
boundary -> control : run_adventure(rng, character, zone)
|
||||
alt zone unknown
|
||||
control --> boundary : 404
|
||||
else HP <= 0
|
||||
control --> boundary : 400
|
||||
else nerve < zone cost
|
||||
control --> boundary : 422
|
||||
else ok
|
||||
end
|
||||
control -> regen : apply_regen(character)
|
||||
regen --> control : character (regenerated)
|
||||
control -> control : nerve -= zone cost
|
||||
|
||||
== Encounter ==
|
||||
control -> combatant : pick_enemy(rng, weighted table)
|
||||
alt unknown enemy id (data error)
|
||||
control --> boundary : 500 DomainError::UnknownEncounterEnemy
|
||||
end
|
||||
|
||||
== Combat (rounds) ==
|
||||
loop until one side falls
|
||||
control -> combat : open_ended_roll(rng, attack)
|
||||
combat -> combat : OB vs DB, crit table\n(level-gap damage caps)
|
||||
combat -> fx : bard song effect, active spell dmg/heal
|
||||
combat --> control : round result
|
||||
end
|
||||
|
||||
== Resolve ==
|
||||
alt player victory
|
||||
control -> combatant : XP + gold per enemy def
|
||||
control -> combatant : HP = last defender HP
|
||||
control -> combatant : loot rolls (catalogue items only)
|
||||
else defeat
|
||||
control -> combatant : HP = 0, no rewards
|
||||
end
|
||||
control -> fx : spell-point upkeep (win or lose)
|
||||
control --> boundary : {zone, enemy, combat_log, loot}
|
||||
boundary --> Player : {zone, enemy, combat_log, loot}
|
||||
@enduml
|
||||
BIN
docs/openup/analysis-design/sequences/uc-09-heal-at-temple.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
|
|
@ -0,0 +1,22 @@
|
|||
@startuml uc-09-heal-at-temple
|
||||
autonumber
|
||||
title UC-09 Heal at Temple (design sequence)
|
||||
actor Player
|
||||
participant "TempleBoundary\n«/api/characters/{id}/heal»" as boundary <<boundary>>
|
||||
participant "HealingService\nsb-api::routes::heal_character" as control <<control>>
|
||||
|
||||
== Heal ==
|
||||
Player -> boundary : POST heal (optionally partial)
|
||||
boundary -> control : heal_character(character, partial)
|
||||
control -> control : missing HP\nfull: all; partial: half (ceil)\ncost = healed, min 1 gold
|
||||
alt already at full HP
|
||||
control --> boundary : 400
|
||||
else gold < cost
|
||||
control --> boundary : 422
|
||||
else ok
|
||||
control -> control : gold -= cost, HP restored, persist
|
||||
control --> boundary : {hp, max_hp, gold, healed: true}
|
||||
boundary --> Player : {hp, max_hp, gold, healed: true}
|
||||
end
|
||||
note over control : handler-side logic - recorded debt\n(Architecture Notebook D1, Q3)
|
||||
@enduml
|
||||
|
After Width: | Height: | Size: 63 KiB |
|
|
@ -0,0 +1,29 @@
|
|||
@startuml uc-10-practice-bard-song
|
||||
autonumber
|
||||
title UC-10 Practice Bard Song (design sequence)
|
||||
actor Player
|
||||
participant "SongBoundary\n«/api/characters/{id}/songs»" as boundary <<boundary>>
|
||||
participant "SongService\nsb-api::routes::set_song" as control <<control>>
|
||||
participant "BardSong\nBARD_SONGS + active_song" as entity <<entity>>
|
||||
|
||||
== View ==
|
||||
Player -> boundary : GET songs
|
||||
boundary -> entity : available_songs (level-gated)
|
||||
boundary --> Player : songs + effects + active song
|
||||
|
||||
== Set / Clear ==
|
||||
Player -> boundary : POST song id (or empty to clear)
|
||||
boundary -> control : set_song(character, song)
|
||||
alt non-Bard
|
||||
control --> boundary : 403
|
||||
else unknown song
|
||||
control --> boundary : 404
|
||||
else level too low
|
||||
control --> boundary : 422
|
||||
else ok
|
||||
control -> entity : active_song = song (or None), persist
|
||||
control --> boundary : {success, message, active_song}
|
||||
boundary --> Player : {success, message, active_song}
|
||||
end
|
||||
note over entity : active song applies in UC-08 combat (step 5)
|
||||
@enduml
|
||||
BIN
docs/openup/analysis-design/sequences/uc-11-study-magic.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
51
docs/openup/analysis-design/sequences/uc-11-study-magic.puml
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
@startuml uc-11-study-magic
|
||||
autonumber
|
||||
title UC-11 Study Magic (design sequence)
|
||||
actor Player
|
||||
participant "MagicBoundary\n«/api/characters/{id}/spells»" as boundary <<boundary>>
|
||||
participant "MagicService\nsb-api::routes::{get_spells,learn}" as control <<control>>
|
||||
participant "Spell\nSpellDef + known/active/points" as entity <<entity>>
|
||||
|
||||
== View spellbook ==
|
||||
Player -> boundary : GET spells
|
||||
boundary -> control : get_spells(character)
|
||||
control -> entity : caster class, spell level = (level/3)+1
|
||||
control --> boundary : spells (marked known) + learn costs 2-7
|
||||
boundary --> Player : spellbook + spell points
|
||||
|
||||
== Learn spell level ==
|
||||
Player -> boundary : POST learn level (1-7)
|
||||
boundary -> control : learn_spell_level(character, level)
|
||||
alt non-caster
|
||||
control --> boundary : 403
|
||||
else level out of 1-7
|
||||
control --> boundary : 400
|
||||
else level above current spell level
|
||||
control --> boundary : 422
|
||||
else gold < learn cost
|
||||
control --> boundary : 422
|
||||
else no new spells at level
|
||||
control --> boundary : 400
|
||||
else ok
|
||||
control -> entity : gold -= cost, add unknown spell ids
|
||||
control -> entity : recompute + refill max spell points, persist
|
||||
control --> boundary : {learned_count, spell_points, max_spell_points, gold}
|
||||
boundary --> Player : {learned_count, ...}
|
||||
end
|
||||
|
||||
== Activate / Clear active spell ==
|
||||
Player -> boundary : POST spell id (or empty)
|
||||
boundary -> control : activate_spell(character, spell)
|
||||
alt spell not known
|
||||
control --> boundary : 403
|
||||
else spell unknown
|
||||
control --> boundary : 404
|
||||
else spell points insufficient
|
||||
control --> boundary : 422
|
||||
else ok
|
||||
control -> entity : points -= cost, active_spell = spell, persist
|
||||
control --> boundary : {active_spell, spell_points}
|
||||
boundary --> Player : {active_spell, spell_points}
|
||||
end
|
||||
note over entity : active spell acts in UC-08 combat\n(dmg/heal per round + upkeep)
|
||||
@enduml
|
||||
BIN
docs/openup/analysis-design/sequences/uc-12-level-up.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
26
docs/openup/analysis-design/sequences/uc-12-level-up.puml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
@startuml uc-12-level-up
|
||||
autonumber
|
||||
title UC-12 Level Up at Review Board (design sequence)
|
||||
actor Player
|
||||
participant "ReviewBoundary\n«/api/characters/{id}/level-up»" as boundary <<boundary>>
|
||||
participant "ReviewBoard\nsb-core::character::level_up" as control <<control>>
|
||||
participant "Progression\nxp_for_level + level_up_cost" as entity <<entity>>
|
||||
|
||||
== Level up ==
|
||||
Player -> boundary : POST level-up
|
||||
boundary -> control : level_up(character)
|
||||
control -> entity : xp_for_level(next) = 18*100*level^2
|
||||
control -> entity : cost = 500 * next level
|
||||
alt at max level 100
|
||||
control --> boundary : 422 (DomainError)
|
||||
else XP below threshold
|
||||
control --> boundary : 422 (DomainError)
|
||||
else gold < cost
|
||||
control --> boundary : 422 (DomainError)
|
||||
else ok
|
||||
control -> entity : level += 1, gold -= cost
|
||||
control -> entity : recompute + fully restore\nHP/energy/nerve/max SP, persist
|
||||
control --> boundary : {success, message, character}
|
||||
boundary --> Player : {success, message, character}
|
||||
end
|
||||
@enduml
|
||||