Daggerheart creation workflow and readiness contract
This document defines the durable contract for Daggerheart character creation, workflow progress, reset behavior, and session-start readiness.
Scope and posture
- The workflow is a clean-slate contract (no backward-compatibility shims).
- Workflow state is derived from profile fields (no persisted cursor).
- Core APIs are generic, while step semantics are Daggerheart-specific.
Canonical step model
Daggerheart character creation is a strict 9-step sequence with an intentional UX ordering choice: the three free-form steps stay at the end so structured selection and validation happen first.
class_subclass(class_id,subclass_id, and any subclass-required setup such ascompanion)heritage(heritage.first_feature_ancestry_id,heritage.second_feature_ancestry_id,heritage.community_id, optionalheritage.ancestry_label)traits(traits_assignedplus SRD distribution validation)equipment(starting_weapon_ids[],starting_armor_id,starting_potion_item_id)experiences(experiences[])domain_cards(domain_card_ids[])details(details_recordedafter recording starting details)background(background, free-form text)connections(connections, free-form text)
This ordering is accepted product behavior. It is not treated as a rules defect unless it changes validation or readiness semantics.
Profile fields and storage shape
The canonical profile contract is carried by systems.daggerheart.v1.DaggerheartProfile and projected into daggerheart_character_profiles.
Required workflow-related fields:
class_id,subclass_idsubclass_creation_requirements[]heritage_jsoncompanion_sheet_jsonwhen the selected subclass requires ittraits_assigneddetails_recordedstarting_weapon_ids_json,starting_armor_id,starting_potion_item_idbackgroundexperiences_jsondomain_card_ids_jsonconnections
Progress evaluation semantics
Progress is computed from profile data via the Daggerheart evaluator.
- Raw field checks determine which requirements are satisfied.
- Reported
steps[].completeis prefix-gated: later steps are not complete until all prior steps are complete. next_stepis the first incomplete step;0means ready.readyis true only when all nine steps are complete.unmet_reasonsreports all missing requirements.
Generic workflow API
game.v1.CharacterService exposes four generic RPCs:
GetCharacterCreationProgressApplyCharacterCreationStepApplyCharacterCreationWorkflow(atomic bulk apply)ResetCharacterCreationWorkflow
ApplyCharacterCreationStep carries a system-specific oneof payload. For Daggerheart this is DaggerheartCreationStepInput.
ApplyCharacterCreationWorkflow carries all step payloads in one system-specific message (DaggerheartCreationWorkflowInput) and applies them in canonical order as one all-or-nothing write.
Single pipeline policy
Workflow fields are write-once-through-pipeline semantics:
- Step-by-step:
ApplyCharacterCreationStep - Bulk import/test setup:
ApplyCharacterCreationWorkflow
PatchCharacterProfile must not mutate workflow-owned fields. This prevents bypassing ordering/content validation and keeps readiness invariants centralized in one pipeline.
Strict apply gating
Apply requests are strictly ordered.
- The requested step must equal
next_step. - Out-of-order writes are rejected with
FailedPrecondition. - Content IDs are validated against Daggerheart content stores.
- Heritage selection is resolved into stored feature ids.
- Single ancestry stores the same ancestry id in both feature slots.
- Mixed ancestry stores the first ancestry’s first feature and the second ancestry’s second feature, plus a free-form ancestry label.
- Subclass content may declare creation requirements.
subclass-beastboundcurrently requires a companion sheet during step 1.
- Trait values are validated via Daggerheart trait validation and SRD starting distribution (
+2,+1,+1,+0,+0,-1). - Domain cards must belong to one of the selected class domains.
- Starting equipment is validated against tier-1 weapon/armor catalog entries and allowed starting potion ids.
- Companion sheets are validated as static creation-time data.
animal_kind,name,attack_description, and exactly two experience names are required.- Experience modifiers normalize to
+2. - Evasion/range/damage die are derived and stored as fixed defaults.
- Damage type must be
physicalormagic.
All successful applies write through system-owned command execution using sys.daggerheart.character_profile.replace (no direct projection mutation in request handlers).
Reset semantics
Reset is workflow-destructive by design.
ResetCharacterCreationWorkflowemitssys.daggerheart.character_profile.delete.- The Daggerheart adapter handles that event directly and removes the projected Daggerheart profile row for the character.
- Post-reset progress returns to step 1 with
ready = false.
Readiness enforcement
Readiness is enforced at two boundaries:
- Domain session start: Daggerheart module
CharacterReadydelegates to workflow evaluation and blockssession.startwhen incomplete. - Canonical readiness report API:
GetCampaignSessionReadinessevaluates the same domain readiness report used by session-start command handling and returns deterministic blockers for UI/operator consumers.
This keeps transport-level readiness surfaces consistent with domain decisions.
AI tool contract alignment
AI readiness and import flows use character_creation_workflow_apply for workflow fields. character_profile_patch is limited to non-workflow profile fields. These are broader AI orchestration contracts, not part of the GM-safe production AI bridge profile.
Implementation map
- Workflow evaluator:
internal/services/game/domain/systems/daggerheart/creation_workflow.go - Workflow provider + service dispatch:
internal/services/game/api/grpc/game/charactertransport/character_workflow.go - CharacterService RPC handlers:
internal/services/game/api/grpc/game/charactertransport/character_service.go - Profile adapter/reset handling:
internal/services/game/domain/systems/daggerheart/internal/adapter/profile.go - Session-start readiness evaluator:
internal/services/game/domain/readiness/session_start.go - Campaign readiness RPC:
internal/services/game/api/grpc/game/campaigntransport/campaign_readiness_service.go - AI tool handlers:
internal/services/ai/orchestration/gametools/