Life Architecture
Product narrative: templ/prd.md. This document is the Flowbot implementation contract.
1. Position in Flowbot
Layer 3 — Business Logic
├── internal/modules/life/ # Use-case orchestration, seeds, lore outbox consumer
├── pkg/life/ # Pure formulas (cascade, loot, buffs)
└── pkg/capability/life/ # LLM quest evaluation and lore generation
Layer 2 — Capability
└── capability.Invoke(hub.CapLife, …) → LLM adapter
Layer 1 / 0 — Store + infra
└── life_* tables via LifeStore / ent; event_outbox for async lore
HTML UI lives in internal/modules/web under /service/web/life* (same pattern as hub/workflow pages).
2. Package map
internal/modules/life/ # Service, Bootstrap, outbox consumer, config
internal/modules/life/seed/ # embed JSON (equipment, loot tables)
internal/modules/fx.go # Register()
pkg/life/ # cascade, loot, buffs (no I/O)
pkg/capability/life/ # EvaluateQuest, GenerateInstanceLore
internal/store/ent/schema/ # life_*.go (int64 PK + string flag)
internal/store/life.go # LifeStore facade (package store)
internal/store/postgres/ # only if methods are on Adapter (prefer LifeStore)
internal/modules/web/ # life_*_webservice.go; SetLifeService via life.OnService
pkg/views/pages|partials/ # life_*.templ
public/js/life-radar.js # Character radar chart
public/js/life-stats.js # Stats dashboard charts (Chart.js)
public/css/custom.css # Life page styles (`.life-*`)
3. Identity model
- Platform
userstable is unchanged (account only). life_profilesis 1:1 with the signed-in web operator.life_profiles.user_id= web session UID fromauthenticateWeb/route.RequestContext.UID(string, e.g.user-admin).- All domain rows FK to
life_profile_id(int64), not platform user id.
4. Deviations from PRD
| PRD | Implementation |
|---|---|
| UUID primary keys | int64 PK + string flag (types.Id()) — repo convention |
UserInventory.is_equipped |
Removed; wear state only in life_equipped_slots |
| Pity counter entity missing | life_profiles.pity_by_tier JSON map |
| Unique memorial gear as new Equipment rows | Inventory instance overrides (instance_name, instance_lore, instance_buffs) |
| Equipment rust logic | Fail quest applies 24h tarnished_until; buffs ignored while active; Complete clears rust |
| Sync AI lore on drop | Sync drop + outbox; lore filled async |
| Full AI always | EvaluateQuest / GenerateInstanceLore require chat LLM; errors surface to caller |
| AIContext unused | Loaded into EvaluateQuest (personality, completion rate, mood) |
| Gear AI privilege unused | Equipped ai_unlocked_privilege merged into EvaluateQuest |
| LifeGoal CRUD | Create / edit / pause / delete on Character; optional goal_flag on quest create |
| Drop rate preview | Toast on create + pending card ~N% drop |
| AI lore on every drop | Lore outbox only for Boss quests or difficulty SS/SSS |
5. ER / tables
| Table | Role |
|---|---|
life_profiles |
level, exp, gold, class_type, base_drop_rate_bonus, pity_by_tier |
life_characteristics |
INT / PHY / WIL / CHA / CRE / FIN / WRI / FOC per profile |
life_skills |
skills under a characteristic |
life_goals |
PARA LifeGoal |
life_quests |
One-Time / Daily / Boss quests; stores AI title plus original user prompt |
life_ai_contexts |
DM personality + mood JSON (1:1 profile) |
life_equipments |
Catalog templates (seeded) |
life_inventories |
Owned instances + instance overrides + lore_status |
life_equipped_slots |
One row per profile; slot → inventory id |
life_loot_tables |
drop_tier → base chance + item flag pool |
life_action_logs |
completion + dice + drop audit |
life_rewards |
Player-defined real-life rewards (name, price, optional cooldown) |
life_reward_redemptions |
Gold spend audit with name/price snapshots |
life_achievements |
Memorial achievement catalog (seeded) |
life_achievement_progress |
Per-profile counters toward unlocks |
life_achievement_unlocks |
Unlocked memorial achievements |
life_plan_nodes |
Goal / milestone / action plan tree |
life_action_specs |
Action definitions (todo / recurring / habit / checkpoint) |
life_action_occurrences |
Today-board occurrences for actions |
life_action_dependencies |
Checkpoint dependency edges |
life_evidence |
Quest evidence submissions |
life_adjudications |
AI quest rulings (suggested → accepted) |
life_habit_checkins |
Habit check-in audit |
Reserved on inventory/slots: tarnished_until (nullable time). Lore: lore_status ∈ none | pending | ready | failed.
6. Service and capability API
LifeService (internal/modules/life):
| Method | Purpose |
|---|---|
EnsureProfile |
Create profile + seed characteristics + empty slots/ai context |
CreateQuestFromPrompt |
Evaluate via capability → persist quest |
CompleteQuest |
Cascade + loot + action log + achievements + optional lore outbox + Daily respawn (one DB tx; loot resolved in-tx from live pity) |
FailQuest |
Mark failed + 24h equipment rust (one DB tx) |
CreateGoal / UpdateGoal / SetGoalStatus / DeleteGoal |
PARA goal CRUD + pause/activate |
ListActionLogs |
Recent completion audit for Quests UI |
ListAchievements |
Catalog + progress/unlock state for Achievements UI |
ListRewardsPage / CreateReward / UpdateReward / DeactivateReward / RestoreReward / RedeemReward |
Real-life rewards market (gold sink) |
GetStatsPage |
30-day analytics aggregates for Stats UI |
BuildSkillTree |
Skill-tree evidence UI model |
CreatePlanNode / ConfirmHabitAction / PreviewGoalBreakdown / ImportGoalBreakdown |
Plan tree + AI breakdown |
ListTodayActions / CompleteActionOccurrence / SkipActionOccurrence / CheckInHabit |
Today board execution |
SubmitQuestEvidence / AdjudicateQuest / ApplyQuestAdjudication |
Quest evidence + DM ruling |
Equip / Unequip |
Update life_equipped_slots |
ListQuests / ListPendingQuestDMViews / ListInventory / GetCharacter |
Read models for UI |
SetClassType |
Update class (default Architect) |
ProcessPendingLore |
Outbox consumer for lore (retries on LLM failure) |
Capability (hub.CapLife):
| Op | Purpose |
|---|---|
EvaluateQuest |
LLM JSON eval → difficulty, fear, rewards, drop_tier, skill/stat |
AdjudicateQuest |
LLM JSON ruling from quest evidence |
GenerateInstanceLore |
LLM JSON → instance name/lore for dropped gear |
BreakdownGoalTree |
LLM JSON → suggested plan tree |
7. Bootstrap / seeds
EnsureProfile: seed (and backfill) characteristicsINT,PHY,WIL,CHA,CRE,FIN,WRI,FOC;class_type=Architect; empty equipped slots + AI context.- Skills:
GetOrCreateon evaluate/create (no default skill list). Bootstrap: idempotent upsert of equipment + loot tables + achievements frominternal/modules/life/seed/*.json.
8. HTTP routes and User nav
Prefix: /service/web
| Method | Path | Purpose |
|---|---|---|
| GET | /life |
Dashboard |
| GET | /life/stats |
Stats shell (30-day analytics) |
| GET | /life/stats/panel |
Stats HTMX panel (tz IANA query) |
| GET | /life/character |
Stats, class, goals, equipped, plan tree |
| POST | /life/character/class |
Set class type |
| POST | /life/character/plan |
Create plan node |
| POST | /life/character/plan/:flag/confirm-habit |
Confirm habit candidate |
| POST | /life/character/plan/breakdown/preview |
AI plan breakdown preview |
| POST | /life/character/plan/breakdown/import |
Import AI plan breakdown |
| POST | /life/goals |
Create goal |
| POST | /life/goals/:flag |
Update goal title/category |
| POST | /life/goals/:flag/status |
Pause / activate goal |
| POST | /life/goals/:flag/delete |
Delete goal |
| GET | /life/quests |
Quest list + today board + evidence/DM |
| POST | /life/quests |
Create quest from prompt |
| POST | /life/quests/:flag/complete |
Complete quest (cascade + loot) |
| POST | /life/quests/:flag/fail |
Fail quest (24h gear rust) |
| POST | /life/quests/:flag/evidence |
Submit quest evidence |
| POST | /life/quests/:flag/adjudicate |
Request AI adjudication |
| POST | /life/quests/:flag/adjudication/:adjudicationFlag/apply |
Apply adjudication verdict |
| POST | /life/actions/:flag/complete |
Complete today action occurrence |
| POST | /life/actions/:flag/skip |
Skip today action occurrence |
| POST | /life/habits/:flag/checkin |
Habit check-in |
| GET | /life/skills |
Skill tree |
| GET | /life/inventory |
Backpack |
| POST | /life/inventory/:flag/equip |
Equip inventory item |
| POST | /life/inventory/slots/:slot/unequip |
Clear equipped slot |
| GET | /life/achievements |
Memorial achievements |
| GET | /life/rewards |
Player-defined real-life rewards market |
| POST | /life/rewards |
Create reward |
| POST | /life/rewards/:flag |
Update reward |
| POST | /life/rewards/:flag/deactivate |
Soft-delete reward |
| POST | /life/rewards/:flag/restore |
Restore soft-deleted reward |
| POST | /life/rewards/:flag/redeem |
Spend gold (honor system) |
Nav: Life module sidebar — Life, Stats, Quests, Skills, Inventory, Achievements, Rewards, Character.
9. Core flows and outbox contract
flowchart LR
UserNav[UserNav] --> WebLife[web_life_handlers]
WebLife --> LifeSvc[modules_life_Service]
LifeSvc --> PkgLife[pkg_life]
LifeSvc --> Store[LifeStore]
LifeSvc --> CapLife[capability_life]
CapLife --> LLM[chat_LLM]
LifeSvc --> Outbox[event_outbox]
Outbox --> LoreWorker[ProcessPendingLore]
LoreWorker --> CapLife
LoreWorker --> Store
Create quest
Web → CreateQuestFromPrompt → capability.Invoke(EvaluateQuest) → get-or-create skill → insert life_quests.
Complete quest
In one transaction:
- Re-read profile pity; roll loot with pity; resolve drop equipment when needed.
- Apply
pkg/lifecascade (skill → characteristic → profile) including pity write. - Mark quest completed.
- Clear rust on equipped gear.
- Maybe insert inventory + lore outbox (Boss, or difficulty SS/SSS).
- Write
life_action_logs. - Daily respawn clone when type is
Daily. - Update achievement progress / unlocks.
HTMX returns immediate feedback (exp/gold/drop). Lore fills later. Completion-rate blend runs after commit.
Fail quest
In one transaction: mark quest Failed and set 24h tarnished_until on equipped slots + equipped inventory. Completion-rate blend runs after commit.
Outbox
- Event type string:
life.inventory.lore_requested - Payload:
event_id,type,life_profile_id,inventory_id - Consumer:
modules/lifeProcessPendingLore(poll unpublished outbox rows of this type) - On success: set instance lore fields,
lore_status=ready, mark outbox published - On LLM / transient failure: leave outbox unpublished for retry; do not mark
lore_status=failed - On poison / malformed payload / malformed lore response / missing inventory: mark outbox published; if inventory is known and still
pending, setlore_status=failed
10. Boundaries
- Modules must not import
pkg/providers/*. - Web handlers must not run ent queries; call
LifeServiceonly. - Formulas only in
pkg/life. - Persistence:
LifeStorein packagestore(internal/store/life.go; not a separate*_store.gopackage); schemaslife_*. - No JSON API under
/service/lifein v1. - Web installs the domain service via
life.OnService(web.SetLifeService)at module register time. - Capability LLM failures use
types.ErrProvider; web maps them to a generic toast (no raw LLM text).
11. Config
modules:
- name: life
enabled: true
default_class: Architect
12. Testing notes
pkg/life: table-driven unit tests (cascade, loot, pity, buffs, soft HP, blend).- Module / web: package tests with fakes where needed.
- Cross-boundary BDD under
tests/specs/life_spec_test.go(requires Docker).
13. Implementation status
Implemented in-tree: schemas (life_*), LifeStore, pkg/life, pkg/capability/life, internal/modules/life, web routes/UI, User nav, seed catalog, BDD specs.