19 KiB
E7.M1 — Prototype story backlog (QuestStateMachine)
Working backlog for Epic 7 — Slice 1 (quest core and persistence). Decomposition and contracts: E7.M1 — QuestStateMachine.
Full-stack policy: Epics deliver playable game features. Every player-visible story has a client Linear issue created in this same decomposition pass — not an undocumented follow-up. Reference: E5M3 (paired server+client).
Labels (Linear): every issue E7.M1; add server or client + Story as listed.
Precursor (do not re-scope): E5.M3 encounter complete grants contract_handoff_token + records EncounterCompleteEvent (NEO-107) — prototype quest credit stand-in until E7.M2 QuestRewardBundle router lands. NEO-43 MissionRewardSkillXpGrant is the future skill-XP apply helper for E7.M2; E7.M1 tracks quest state only (no duplicate item grants on quest complete in Slice 1).
Upstream (must be landed): E3.M1 gather on interact (NEO-63); E3.M2 craft POST (NEO-70); E5.M3 encounter wiring + progress GET (NEO-106, NEO-108). E3S5 client economy HUD (NEO-72–NEO-75).
Prototype quest spine (frozen in E7M1-01): three solo onboarding quests + one multi-step chain quest (vision minimum 3–5 onboarding + one integrated chain). Solo only — no party credit (quest_scope_and_party.md).
Linear issues (created): attach docs/plans/NEO-*-implementation-plan.md on the story branch when work starts.
| Slug | Layer | Linear |
|---|---|---|
| E7M1-01 | server | NEO-112 |
| E7M1-02 | server | NEO-113 |
| E7M1-03 | server | NEO-114 |
| E7M1-04 | server | NEO-115 |
| E7M1-05 | server | NEO-116 |
| E7M1-06 | server | NEO-117 |
| E7M1-07 | server | NEO-118 |
| E7M1-08 | server | NEO-119 |
| E7M1-09 | server | NEO-120 |
| E7M1-10 | server | NEO-121 |
| E7M1-11 | client | NEO-122 |
| E7M1-12 | client | NEO-123 |
Dependency graph in Linear: E7M1-02 blocked by E7M1-01. E7M1-03 blocked by E7M1-02. E7M1-04 blocked by E7M1-03. E7M1-05 blocked by E7M1-03. E7M1-06 blocked by E7M1-05. E7M1-07 blocked by E7M1-06. E7M1-08 blocked by E7M1-07. E7M1-09 blocked by E7M1-06 (may parallel E7M1-08). E7M1-10 blocked by E7M1-07. E7M1-11 blocked by E7M1-08 and E7M1-09. E7M1-12 blocked by E7M1-11.
Board order: estimates 1–12 matching slug order (E7M1-01 = 1 … E7M1-12 = 12). On the Epic 7 project board, sort by Estimate (ascending).
Story order (recommended)
| Order | Slug | Layer | Depends on |
|---|---|---|---|
| 1 | E7M1-01 | server | E3.M1/E3.M2/E5.M3 content ids frozen |
| 2 | E7M1-02 | server | E7M1-01 |
| 3 | E7M1-03 | server | E7M1-02 |
| 4 | E7M1-04 | server | E7M1-03 |
| 5 | E7M1-05 | server | E7M1-03 |
| 6 | E7M1-06 | server | E7M1-05 |
| 7 | E7M1-07 | server | E7M1-06 |
| 8 | E7M1-08 | server | E7M1-07 |
| 9 | E7M1-09 | server | E7M1-06 |
| 10 | E7M1-10 | server | E7M1-07 |
| 11 | E7M1-11 | client | E7M1-08, E7M1-09 |
| 12 | E7M1-12 | client | E7M1-11 |
Downstream (separate modules): E7.M2 idempotent QuestRewardBundle apply + consume EncounterCompleteEvent; E7.M3 faction ledger (pre-production); E8.M1 party-credit quests.
Kickoff decisions (decomposition defaults)
| Topic | Decision | Rationale |
|---|---|---|
| Quest count | 3 onboarding + 1 multi-step chain (4 total) | Vision prototype minimum; readable manual QA |
| Party mode | Solo only | quest_scope_and_party.md; E8.M1 not landed |
| Accept flow | POST …/quests/{questId}/accept |
Explicit start; boot does not auto-grant all quests |
| Objective advance | Server hooks on gather/craft/encounter success | Client displays GET snapshot only (client_server_authority.md) |
| Objective kinds (v1) | gather_item, craft_recipe, encounter_complete, inventory_has_item |
Covers gather→craft→combat chain without custom scripting VM |
| Chain quest | 4 sequential steps in one QuestDef |
Single HUD row with step index; epic “integrated chain” |
| Prerequisites | prerequisiteQuestIds enforced on accept |
Onboarding order: gather → refine → combat → chain |
| Rewards (Slice 1) | State-only completion; no duplicate item/XP grants | Loot stays on gather/craft/encounter paths; E7.M2 owns bundles |
| Token handoff | Chain final step inventory_has_item contract_handoff_token |
Uses E5.M3 encounter loot; no consume mutation in Slice 1 |
| Idempotency | Once per player per quest id complete | Slice 1 AC: no duped quest completion |
| Store | In-memory IPlayerQuestStateStore (+ optional Postgres migration) |
Matches encounter prototype stores |
| HTTP read | GET /game/players/{id}/quest-progress |
Client HUD without local objective math |
| HUD fidelity | Prototype Labels / debug panels (not final art) | NEO-110 / NEO-122 precedent |
E7M1-01 — Prototype QuestDef catalog + schemas + CI
Goal: Lock content shape and CI validation for four frozen quests before server load.
In scope
content/schemas/quest-def.schema.json,content/schemas/quest-step-def.schema.json,content/schemas/quest-objective-def.schema.json.content/quests/prototype_quests.json— stable quest ids and step/objective graph (see module doc freeze table).scripts/validate_content.py: schema validation, duplicate ids, exact four-quest allowlist, cross-refitemId/recipeId/encounterIdto frozen catalogs,prerequisiteQuestIdsacyclic + known ids, objective kind enum.- Designer note in E7_M1 +
content/README.md.
Out of scope
- Server loader, runtime engine, HTTP, Godot.
Acceptance criteria
- PR gate validates quest JSON against schema.
- Exactly four prototype quest ids; duplicate
idfails CI. - Every objective reference resolves to a frozen catalog id.
- Stable id list documented in module doc freeze box.
Client counterpart: none (infrastructure-only).
E7M1-02 — Server quest catalog load (fail-fast)
Goal: Disk → host: startup load of content/quests/*.json with CI-parity validation.
In scope
- Loader + catalog types under
server/NeonSprawl.Server/Game/Quests/(path finalized in plan). - Fail-fast on malformed files, duplicate ids, allowlist mismatch with CI, broken cross-refs (items, recipes, encounters).
- Unit tests (AAA) for loader happy path and failure modes.
server/README.mdsection.
Out of scope
- Injectable registry interface (E7M1-03), HTTP projection, runtime state machine.
Acceptance criteria
- Host fails startup on invalid quest JSON (mirror CI rules).
- Loader tests cover allowlist + cross-ref failures.
Client counterpart: none (infrastructure-only).
E7M1-03 — Injectable quest definition registry + DI
Goal: IQuestDefinitionRegistry with TryGet, startup registration from E7M1-02 catalog.
In scope
- Interface + in-memory implementation;
AddQuestDefinitionRegistryDI extension. - Unit tests: lookup known ids, unknown id returns false.
- Wire in host startup alongside other content registries.
Out of scope
- HTTP, player quest state.
Acceptance criteria
- Registry resolves all four frozen quest ids at runtime.
- Unknown quest id fails closed for downstream operations.
Client counterpart: none (infrastructure-only).
E7M1-04 — GET /game/world/quest-definitions
Goal: Versioned read-only projection of frozen quest defs for client display names and step summaries.
In scope
QuestDefinitionsWorldApi+ DTOs inGame/Quests/.- Nested steps/objectives summary (ids, kinds, display copy — no server secrets).
- Bruno folder
bruno/neon-sprawl-server/quest-definitions/. - Integration tests (AAA).
Out of scope
- Per-player progress (E7M1-08).
Acceptance criteria
- GET returns all four prototype quests with stable JSON v1 envelope.
- Bruno request documents example response shape.
Client counterpart: consumed by NEO-122 for display names (optional cache).
E7M1-05 — Player quest state store + QuestStepState persistence
Goal: Durable per-player quest rows: status (not_started / active / completed), current step index, per-step objective counters.
In scope
IPlayerQuestStateStore+ in-memory implementation (registered in quest DI extension).- Optional Postgres migration
V00Xwhen DB configured (mirror encounter/inventory pattern). QuestStepState/PlayerQuestProgressRowtypes aligned with module contracts.- Unit tests: accept transitions, idempotent complete mark.
Out of scope
- Objective evaluation (E7M1-06), HTTP.
Acceptance criteria
- Store supports read/update per
(playerId, questId). - Completed quest cannot regress to active without explicit reset API (none in prototype).
Client counterpart: none (infrastructure-only).
E7M1-06 — QuestStateOperations (accept, step advance, complete)
Goal: Server-authoritative quest lifecycle without objective wiring yet.
In scope
QuestStateOperations.TryAccept,TryAdvanceStep,TryMarkCompletewith structuredreasonCodedenials (unknown_quest,prerequisite_incomplete,already_completed,already_active).- Prerequisite enforcement from
prerequisiteQuestIds. - Idempotent complete: second complete returns success without state change.
- Unit tests (AAA) for accept order, prerequisite gate, idempotent complete.
Out of scope
- Gather/craft/encounter hooks (E7M1-07); HTTP (E7M1-08/09).
Acceptance criteria
- Accept fails when prerequisite quest not completed.
- Complete is idempotent per player+quest.
- Reason codes documented in
server/README.md.
Client counterpart: NEO-120 exposes accept POST to Godot.
E7M1-07 — Quest objective wiring (gather, craft, encounter)
Goal: Advance active quest steps when server-side gather, craft, and encounter events succeed.
In scope
QuestObjectiveWiring(or equivalent) invoked from:GatherOperations.TryGathersuccess path —gather_itemobjectives.CraftOperations.TryCraftsuccess path —craft_recipeobjectives.EncounterCompletionOperations.TryCompleteAndGrantsuccess path —encounter_completeobjectives.- Inventory snapshot read for
inventory_has_item(post-grant or on polling hook from accept).
- Counter accumulation for quantity objectives (e.g. gather 3 scrap).
- Auto
TryAdvanceStep/TryMarkCompletewhen step objectives satisfied. - Integration tests: accept quest → simulate gather/craft/encounter → progress row advances.
Out of scope
- Client-side objective tracking; item consume on turn-in; E7.M2 reward bundles.
Acceptance criteria
- Active
prototype_quest_gather_introadvances whenscrap_metal_bulkgather succeeds. - Active
prototype_quest_refine_introadvances onrefine_scrap_standardcraft success. - Active
prototype_quest_combat_introadvances whenprototype_combat_pocketcompletes. - Chain quest steps advance in order across mixed objective kinds.
Client counterpart: progress visible via NEO-122 GET HUD after mutations.
E7M1-08 — GET /game/players/{id}/quest-progress
Goal: Per-player quest progress snapshot for client HUD.
In scope
QuestProgressApi+ DTOs: all known quests with status, current step index, objective progress counters, completion timestamp when applicable.- Known-player gate (mirror encounter-progress / skill-progression patterns).
- Bruno
bruno/neon-sprawl-server/quest-progress/. - Integration tests (AAA): accept → partial progress → complete rows.
Out of scope
- Quest definition catalog (E7M1-04).
Acceptance criteria
- GET returns stable v1 envelope for
dev-local-1player. - Completed quests show
completedwith step count at terminal step.
Client counterpart: NEO-122 — quest_progress_client.gd + HUD labels.
E7M1-09 — POST /game/players/{id}/quests/{questId}/accept
Goal: Player-initiated quest accept from Godot (prototype “quest giver” is HUD button or key binding).
In scope
QuestAcceptApi: POST accept; JSON body optional v1{}; response includes updated progress row or deny payload.- Wire
QuestStateOperations.TryAccept. - Bruno + integration tests: happy accept, prerequisite deny, duplicate accept.
Out of scope
- Abandon/reset POST (defer unless capstone QA requires).
Acceptance criteria
- Successful accept transitions row
not_started→active. - Deny returns structured
reasonCodewithout mutation.
Client counterpart: NEO-122 — accept binding + feedback label.
E7M1-10 — Quest telemetry hook sites (comment-only)
Goal: Document and place comment-only hook sites for future E9.M1 catalog events quest_start, quest_step_complete, quest_complete.
In scope
- Hook sites in
QuestStateOperations/QuestObjectiveWiringat accept, step advance, and complete. - Reserved deny hook
quest_accept_denied(optional comment). server/README.mdsubsection; module doc note.
Out of scope
- E9.M1 ingest pipeline; runtime telemetry emit.
Acceptance criteria
- Hook sites exist at accept, step complete, quest complete paths.
- Epic 7 Slice 1 telemetry vocabulary documented.
Client counterpart: none (infrastructure-only).
E7M1-11 — Client quest progress + accept HUD (Godot)
Goal: Godot GET …/quest-progress client + readable quest panel; accept via POST …/accept.
In scope
quest_progress_client.gd, optionalquest_definitions_client.gdfor display names.- HUD:
QuestProgressLabel(active/completed summary),QuestAcceptFeedbackLabel(deny/success copy). - Boot hydrate + refresh after gather/craft/cast/encounter events (mirror
encounter_progress_client.gdpattern). - Prototype accept bindings: e.g. keys Q / Shift+Q for next eligible quest accept (document in manual QA).
- GdUnit: HTTP double, parse happy path + deny path.
client/README.mdsection.docs/manual-qa/NEO-122.md.
Out of scope
- Quest journal art, map pins, NPC dialog; final quest picker UI.
Acceptance criteria
- Player sees quest status change after accept without Bruno.
- Objective progress updates after gather/craft/encounter in Godot session.
- Failed GET/accept surfaces visible HUD error (match interaction client pattern).
Client counterpart: this story is the client counterpart for E7M1-08/09.
E7M1-12 — Playable onboarding quest chain capstone (Godot)
Goal: Prove Epic 7 Slice 1 acceptance in Godot: complete all four prototype quests (gather → refine → combat → operator chain) without Bruno; satisfies vision quest loop gate.
In scope
docs/manual-qa/NEO-123.md: numbered single-session capstone — fresh server restart, accept onboarding quests in order, perform gather/craft/combat actions, verify chain questprototype_quest_operator_chaincompletes whencontract_handoff_tokenheld.client/README.md: End-to-end onboarding quest loop section — flow table; cross-links NEO-112–NEO-122.- Module alignment on completion: E7_M1, documentation_and_implementation_alignment.md, module_dependency_register.md.
Out of scope
- E7.M2 reward bundles; faction reputation; quest VFX; Bruno-only verification as prototype-complete proof.
Acceptance criteria
- Human completes
docs/manual-qa/NEO-123.mdwith server + client. - Epic 7 Slice 1 AC: 3 onboarding + 1 chain complete without duplicate completion on replay.
- Re-read epic_07 Definition of Done for prototype minimums.
Client counterpart: this story is the Slice 1 client capstone.
Decomposition complete checklist
- Every player-visible AC has a
clientLinear issue (NEO-122, NEO-123) - No forbidden deferral language in backlog
- Capstone client story exists (E7M1-12 / NEO-123)
- Epic DoD updated for Godot verification
- documentation_and_implementation_alignment.md E7.M1 row updated
- module_dependency_register.md E7.M1 note updated
Related docs
- epic_07_quest_faction.md
- quest_scope_and_party.md
- E5M3-prototype-backlog.md (encounter token precursor)
- E3S5-client-prototype-backlog.md (gather/craft client HUD)
- neon_sprawl_vision.plan.md — Prototype Scope