Quest Thread and Mission State Machine
Context
This page owns the runtime state-machine shape for Valenar's Quest Thread and Mission primitives. The taxonomy above and below these primitives is owned elsewhere:
- The quest taxonomy (Activity Beat / Mission / Local Quest / Quest
Thread / Act Arc / Saga Arc) lives in
gd-quest-and-lore-design.md. - The live planning vocabulary (objective / clue or lead / mission /
activity / policy) lives in
gd-objectives-clues-missions.md. - The pressure-driven mission-candidate generator lives in
gd-dynamic-quest-pressure-model.md.
What is committed here is the per-instance runtime carrier and the
lifecycle states the runtime walks. The binding authority is
../lore/adr/ad-0007-quest-thread-and-mission-scope-primitives.md;
this page is the owner-doc surface that ad-0007 routes per-thread and
per-mission state-machine concerns to.
The cross-cutting canonical-pattern ADR
../lore/adr/ad-0013-scope-state-channel-template-archetype-pattern.md
records that QuestThread and Mission are pattern instantiations:
scope X plus state channel plus template<X> archetype. This page
follows that pattern verbatim and does not introduce a fifth primitive
shape.
Primitive Shape
Quest Threads and Missions are runtime scopes whose authored archetype data lives on matching templates:
scope QuestThreadcarries per-instance runtime state. At minimum:LifecycleState: QuestThreadLifecycleState,ActScopeKind: int,DefinitionTemplateId,ScopedList<Mission>for child missions, andScopedList<ClueRef>for accumulated clues. The exact field roster beyond this minimum is owned by the Wave 5a runtime-backing wave per ad-0007 lines 47-57.scope Missioncarries per-instance runtime state. At minimum:LifecycleState: MissionLifecycleState,DefinitionTemplateId,ParentThreadId, andScopedList<ActivityBeatRef>for the activity beats the mission contains. Mission walks to its parent QuestThread via the existing SECS scope-walk mechanism.template<QuestThread>andtemplate<Mission>supply identity, narrative metadata, declaredact_scope, declared lane key (one of the seven committed cross-act lane keys oract-scoped), authored beat structure, and the binding rules from the dynamic quest pressure contract. The runtime scope rows reference the archetype viaDefinitionTemplateId.
No host-side parallel state store may carry QuestThread or Mission
per-instance state outside the SECS scope rows. No tag-cluster pattern
may stand in for MissionLifecycleState. The "Templates are data"
tenet keeps mutable per-instance state on the scope, and the authored
archetype on the template.
States
QuestThreadLifecycleState
Seven values, committed by ad-0007 lines 67-70:
| State | Meaning |
|---|---|
Hidden | Thread exists at the template layer but the scope row is not yet surfaced. |
Available | Discovery predicate has fired; the player or policy layer can accept missions under it. |
Active | At least one child Mission has been accepted; the thread is the player's working surface. |
Suspended | Precondition has been violated mid-run; the thread holds its accumulated clues and waits for the precondition to restore. |
Resolved | All required child Missions resolved per the template's resolution rule. |
Failed | A critical-path child Mission entered MissionLifecycleState.Failed. |
Abandoned | Player explicitly abandoned the thread (or dismissed it before accepting any Mission). |
MissionLifecycleState
Seven values, committed by ad-0007 lines 70-71:
| State | Meaning |
|---|---|
Pending | Mission row exists under an Active parent thread but its own preconditions have not fired. |
Available | Preconditions are satisfied; the Mission is offered for acceptance. |
Accepted | Player or policy committed to running the Mission; queued but not yet executing. |
Active | Queued and the executor is running the Mission's ActivityBeats. |
Resolved | All ActivityBeats resolved per the template<Mission> resolution rule. |
Failed | Any critical-path ActivityBeat failed. |
Abandoned | Player explicitly abandoned the Mission (or dismissed it before acceptance). |
The two enums follow the precedent set by FrontStatus in
gd-fronts.md and OperationStatus in
gd-operations.md: persistent state expressed as
a typed enum on the scope, not as a tag set.
Transitions
The transition rows below are design-level. Each row names a source
state, an authoritative trigger, the required facts a runtime predicate
must read, the side-effect set, and the target state. Concrete runtime
predicate bodies for Suspended / Resolved / Failed / Abandoned
on QuestThread, and Resolved / Failed on Mission, are
<deferred-to-Wave-5a-runtime-backing> cells: ad-0007 lines 47-57
defers the exact field roster (and therefore the exact predicate
shape) to Wave 5a; the design intent below is the binding-doc input
this page commits.
QuestThread transitions
| Source | Trigger / required facts | Side effects | Target |
|---|---|---|---|
Hidden | template<QuestThread>.DiscoveryTrigger predicate reads true under the dynamic quest pressure generator's evaluation cadence; ActState.CurrentAct >= template.MinAct. | on_action QuestThreadAvailable(threadId) (working name — promotion deferred to Wave 5a); Journal entry of class quest-available. | Available |
Available | Player (or policy layer per ad-0007 Consequences §2) accepts a child Mission whose Pending precondition reads true. | on_action QuestThreadActivated(threadId) (working name — promotion deferred to Wave 5a); Current Plan urgency-rank update. | Active |
Active | Precondition recorded on template<QuestThread> is violated mid-run. Predicate body <deferred-to-Wave-5a-runtime-backing> per ad-0007 lines 47-57. | on_action QuestThreadSuspended(threadId) (working name — promotion deferred to Wave 5a); thread retains ScopedList<ClueRef> and child Mission rows. | Suspended |
Suspended | Precondition restored. Predicate body <deferred-to-Wave-5a-runtime-backing> per ad-0007 lines 47-57. | on_action QuestThreadResumed(threadId) (working name — promotion deferred to Wave 5a). | Active |
Active | All required child Missions reach MissionLifecycleState.Resolved per the template's resolution rule. Predicate body <deferred-to-Wave-5a-runtime-backing> per ad-0007 lines 47-57. | on_action QuestThreadResolved(threadId) (working name — promotion deferred to Wave 5a); Journal entry of class quest-resolved. | Resolved |
Active | A critical-path child Mission enters MissionLifecycleState.Failed. Predicate body <deferred-to-Wave-5a-runtime-backing> per ad-0007 lines 47-57. | on_action QuestThreadFailed(threadId) (working name — promotion deferred to Wave 5a); Journal entry of class quest-failed. | Failed |
Active | Player commits the Abandon activity against the thread. Activity binding <deferred-to-Wave-5a-runtime-backing> per ad-0007 lines 47-57. | on_action QuestThreadAbandoned(threadId) (working name — promotion deferred to Wave 5a); Journal entry of class quest-abandoned. | Abandoned |
Available | Player dismisses the thread before accepting any Mission. Activity binding <deferred-to-Wave-5a-runtime-backing> per ad-0007 lines 47-57. | on_action QuestThreadAbandoned(threadId) (working name — promotion deferred to Wave 5a). | Abandoned |
Mission transitions
| Source | Trigger / required facts | Side effects | Target |
|---|---|---|---|
Pending | Parent scope QuestThread reads LifecycleState.Active AND Mission's own template preconditions read true. Predicate body <deferred-to-Wave-5a-runtime-backing> per ad-0007 lines 60-64. | on_action MissionAvailable(missionId, threadId) (working name — promotion deferred to Wave 5a); Current Plan candidate-rank update. | Available |
Available | Player or policy layer commits to the Mission. Activity binding <deferred-to-Wave-5a-runtime-backing> per ad-0007 lines 47-57. | on_action MissionAccepted(missionId, threadId) (working name — promotion deferred to Wave 5a). | Accepted |
Accepted | Queue executor reaches the Mission and starts its first ActivityBeat per the existing queue/activity model in gd-queue-and-activity-execution.md. | on_action MissionStarted(missionId) (working name — promotion deferred to Wave 5a); first ActivityBeat begins execution. | Active |
Active | All ActivityBeats resolved per template<Mission>.ResolutionRule. Predicate body <deferred-to-Wave-5a-runtime-backing> per ad-0007 lines 47-57. | on_action MissionResolved(missionId, threadId) (working name — promotion deferred to Wave 5a); parent thread ScopedList<Mission> updated; Journal entry of class mission-resolved. | Resolved |
Active | Any critical-path ActivityBeat fails. Predicate body <deferred-to-Wave-5a-runtime-backing> per ad-0007 lines 47-57. | on_action MissionFailed(missionId, threadId) (working name — promotion deferred to Wave 5a); Journal entry of class mission-failed. | Failed |
Active | Player commits the Abandon activity against the Mission. Activity binding <deferred-to-Wave-5a-runtime-backing> per ad-0007 lines 47-57. | on_action MissionAbandoned(missionId, threadId) (working name — promotion deferred to Wave 5a). | Abandoned |
Available | Player dismisses the Mission before accepting. Activity binding <deferred-to-Wave-5a-runtime-backing> per ad-0007 lines 47-57. | on_action MissionAbandoned(missionId, threadId) (working name — promotion deferred to Wave 5a). | Abandoned |
Triggers and Required Facts
Trigger predicates are named-state predicates only. Day counts,
time-since-start counters, and real-time clock values are forbidden as
gate inputs anywhere in this state machine — consistent with the
binding rule for Act gates in
../lore/adr/ad-0010-act-progression-actstate-scope.md
lines 96-99 and the broader "Never tune parameters as a substitute for
fixing architecture" tenet.
Inputs available to trigger predicates include:
- Channels on
scope QuestThreadandscope Missionthemselves. - The parent thread's
LifecycleState(Mission predicates read this). - The template-level
DiscoveryTrigger, preconditions, andResolutionRuledeclared intemplate<QuestThread>andtemplate<Mission>. ActState.CurrentActper../lore/adr/ad-0010-act-progression-actstate-scope.md.- Named-state predicates on existing scopes the dynamic quest pressure generator already reads (Settlement, Territory, Site, Route, MainCharacter, retinue characters).
Mission-candidate generation by the dynamic quest pressure model
remains a candidate stream rather than a direct state-machine driver:
the generator emits candidates that become scope Mission rows under
an Available parent thread, per ad-0007 Consequences §2.
Template Archetypes
The authored content layer uses two template archetypes:
template<QuestThread> { ThreadName }carries the thread's identity, declaredact_scope, lane key (act-scopedor one of the seven committed cross-act lane keys),DiscoveryTriggerpredicate slot, precondition slot,ResolutionRuleslot, and authored beat structure. Existing thread owner docs under../quest-threads/README.mdare the design surface; the scope rows that back them are authored in the Wave 5a runtime-backing wave.template<Mission> { MissionName }carries the mission's identity, declared parent-thread reference, ActivityBeat list, precondition slot, andResolutionRuleslot.
Two scopes plus two templates is the canonical pattern instantiation per ad-0013 lines 85-89. No third primitive shape (state-as-tag-set, state-as-host-field-only, state-as-Modifier-flag) is permitted as a fallback.
Side Effects
Every transition fires exactly one on_action label (working name
above; the working-name annotation is (working name — promotion to committed metadata deferred to Wave 5a) for each label). The
on_action keyword is the committed metadata vocabulary per
../../../../../.claude/rules/behavior-vocabulary.md;
the label names themselves are committed only when Wave 5a authors
them in Content/quests/events.secs.
Journal entries fire per the existing entry-class taxonomy in
../ux/gd-journal.md. The entry classes used by
this state machine are quest-available, quest-resolved,
quest-failed, quest-abandoned, mission-resolved,
mission-failed. No additional Journal entry class is introduced by
this page.
Current Plan urgency-rank updates fire per the existing rank model in
../ux/gd-current-plan.md. The
state-machine here does not specify rank values; rank arithmetic is
owned by the dynamic quest pressure model.
Mission Resolved on the last required child Mission cascades to the
QuestThread Active → Resolved transition when the parent template's
ResolutionRule reads satisfied. Mission Failed on a critical-path
child cascades to QuestThread Active → Failed the same way. The
cascade is the state-machine driver, not a separate event family.
Runtime Backing Status
Status: contract-only.
- Host/runtime owner: proposed
legacy/v1/examples/valenar/Host/Systems/QuestThreadSystem.cs,legacy/v1/examples/valenar/Host/Systems/MissionSystem.cs,legacy/v1/examples/valenar/Host/Data/QuestThreadData.cs,legacy/v1/examples/valenar/Host/Data/MissionData.cs. None exist today. Authored in the Wave 5a runtime-backing wave per ad-0007 lines 137-145. - Generated/.secs owner:
legacy/v1/examples/valenar/Content/quests/scopes.secs(newscope QuestThread,scope Mission),Content/quests/templates.secs(newtemplate<QuestThread>,template<Mission>),Content/quests/events.secs(the working-nameon_actionlabels promoted to committed labels), and any futureContent/quests/channels.secsfor derived per-thread channels. Matched by the parallellegacy/v1/examples/valenar/Generated/Quests/stand-in tree. - Read-model/UI owner: proposed
legacy/v1/examples/valenar/Host/ReadModels/QuestThreadsReadModel.csandlegacy/v1/examples/valenar/Host/ReadModels/MissionsReadModel.cs. Neither exists today. UI consumers are../ux/gd-current-plan.md,../ux/gd-journal.md, and../ux/gd-objectives-screen.md. - Tests:
<deferred-to-wave-7>— Wave 7 test-authoring wave authors unit tests against each transition row. Test names use the<StateMachine><Source>To<Target>Testconvention (e.g.QuestThreadHiddenToAvailableTest). - Known gaps: the exact field roster on
scope QuestThreadandscope Missionbeyond the minimum committed in ad-0007; theActScopeKindenum's exact values; theDiscoveryTrigger, precondition, andResolutionRuleslot signatures on the two template archetypes; the working-nameon_actionlabels' promotion to committed labels. - Illegal fallback behavior: no silent mission generation outside the
dynamic quest pressure generator; no fake urgency strings outside
the Current Plan rank model; no host-side parallel state store; no
tag-cluster pattern substitute for
MissionLifecycleState. An attempt to setLifecycleStatedirectly on a scope row outside the declared transitions raises an explicitInvalidStateExceptionrather than silently mutating state. - Next closure wave: Wave 5a runtime-backing wave authors the host data
classes, the
Content/quests/*.secssource, the matching Generated stand-ins, and the read-model surfaces. The Journal-readback hook wave follows.
Glossary Propagation
The following terms must land in the canonical glossary surfaces
(../gd-glossary.md, ../gd-canon.md,
../README.md) per
../../../../../.claude/rules/valenar-contract-backing.md § Canonical Glossary Targets.
Glossary propagation itself is the next docs wave (Wave 3c); this
section records the obligation:
QuestThread(scope primitive).Mission(scope primitive).QuestThreadLifecycleState(enum; seven values listed above).MissionLifecycleState(enum; seven values listed above).ActScopeKind(enum; values deferred to Wave 5a).DefinitionTemplateId(template id reference field on the two scopes).ScopedList<Mission>(child-mission list field onscope QuestThread).ScopedList<ClueRef>(accumulated-clue list field onscope QuestThread).ScopedList<ActivityBeatRef>(activity-beat list field onscope Mission).
Cross-References
../lore/adr/ad-0007-quest-thread-and-mission-scope-primitives.md— binding authority.../lore/adr/ad-0013-scope-state-channel-template-archetype-pattern.md— canonical pattern.../lore/adr/ad-0006-dynamic-quest-pressure-and-cross-act-lane-decisions.md— cross-act lane key set.../lore/adr/ad-0010-act-progression-actstate-scope.md—ActState.CurrentActinput../gd-quest-and-lore-design.md— quest taxonomy and Saga Arc envelope../gd-objectives-clues-missions.md— live planning vocabulary../gd-dynamic-quest-pressure-model.md— mission-candidate generator../gd-queue-and-activity-execution.md— queue executor that drivesAccepted → Activeon Mission.../quest-threads/gd-quest-thread-index.md— Quest Thread index.../quest-threads/gd-quest-thread-act-0-survive.md,../quest-threads/gd-quest-thread-act-0-the-old-stones.md,../quest-threads/gd-quest-thread-act-0-the-ruin-beneath.md,../quest-threads/gd-quest-thread-act-0-make-this-place-hold.md— Act 0 thread owners.../quest-threads/gd-quest-thread-core-truth.md,../quest-threads/gd-quest-thread-faction-polity.md,../quest-threads/gd-quest-thread-people-retinue.md,../quest-threads/gd-quest-thread-pressure-corruption.md,../quest-threads/gd-quest-thread-reclamation-crown.md,../quest-threads/gd-quest-thread-rune-crafting-true-harm.md,../quest-threads/gd-quest-thread-survival-settlement.md— seven cross-act lane thread owners.../ux/gd-current-plan.md,../ux/gd-journal.md,../ux/gd-objectives-screen.md— UI readback surfaces.../../../../../.claude/rules/secs-concepts.md— the 6-phase channel pipeline and scope primitive.../../../../../.claude/rules/behavior-vocabulary.md—on_actionandactivityallowlist.../../../../../.claude/rules/valenar-contract-backing.md— backing/propagation gate.