NEO-112: add E7M1 prototype backlog and decomposition cross-links

pull/151/head
VinPropane 2026-05-31 21:13:03 -04:00
parent be1dae3872
commit 140d857398
5 changed files with 439 additions and 3 deletions

View File

@ -46,14 +46,18 @@ Provide a quest state machine and reward routing that ties gathering, crafting,
## Implementation Slices (Backlog-Ready)
<a id="epic-7-slice-1"></a>
### Slice 1 - Quest core and persistence
- Scope: E7.M1 supporting branching steps and failure/reset rules for prototype chain.
- Dependencies: E3.M2, E5.M1
- Dependencies: E3.M2, E5.M1 (gather/craft/encounter hooks on main)
- Acceptance criteria:
- 3-5 onboarding quests + one chain requiring gather/craft/combat complete without duped rewards.
- Telemetry hooks: `quest_start`, `quest_step_complete`, `quest_complete`, funnel times.
**Linear backlog:** [E7M1-prototype-backlog.md](../../plans/E7M1-prototype-backlog.md) — [NEO-112](https://linear.app/neon-sprawl/issue/NEO-112) → [NEO-123](https://linear.app/neon-sprawl/issue/NEO-123); client capstone **E7M1-12** [NEO-123](https://linear.app/neon-sprawl/issue/NEO-123). Verify in **Godot**, not Bruno-only.
### Slice 2 - Reward and unlock routing
- Scope: E7.M2 delivering XP, items, blueprint unlocks, and flags for world gates.
@ -85,7 +89,9 @@ Provide a quest state machine and reward routing that ties gathering, crafting,
- Risk: Rewards break parity or economy.
- Mitigation: Lint bundles against E6.M4 and E3.M5 policies.
<a id="epic-7-definition-of-done"></a>
## Definition of Done
- Prototype quest counts and integrated chain meet master plan minimums.
- Prototype quest counts and integrated chain meet master plan minimums **in the Godot client** (Slice 1 capstone [NEO-123](../../manual-qa/NEO-123.md)), not Bruno-only.
- Pre-production adds faction + contracts with validation.

View File

@ -7,7 +7,8 @@
| **Module ID** | E7.M1 |
| **Epic** | [Epic 7 — Quest / Faction](../epics/epic_07_quest_faction.md) |
| **Stage target** | Prototype |
| **Status** | Planned (see [dependency register](module_dependency_register.md)) |
| **Status** | Planned — Slice 1 backlog [E7M1-prototype-backlog.md](../../plans/E7M1-prototype-backlog.md): **E7M1-01** [NEO-112](https://linear.app/neon-sprawl/issue/NEO-112) → **E7M1-12** [NEO-123](https://linear.app/neon-sprawl/issue/NEO-123) (decomposed; implementation not started) |
| **Linear** | Label **`E7.M1`** · [E7M1-prototype-backlog.md](../../plans/E7M1-prototype-backlog.md) |
## Purpose
@ -45,6 +46,40 @@ Data-driven quest lifecycle: start, step progression, branching, failure/reset,
See Epic 7 **Slice 1 — Quest core and persistence**: 35 onboarding quests plus one chain across gather/craft/combat; telemetry `quest_start`, `quest_step_complete`, `quest_complete`, funnel times.
## Linear backlog (decomposed)
Full story tables, kickoff defaults, and **`blockedBy` graph:** [E7M1-prototype-backlog.md](../../plans/E7M1-prototype-backlog.md).
| Slug | Linear |
|------|--------|
| E7M1-01 | [NEO-112](https://linear.app/neon-sprawl/issue/NEO-112) |
| E7M1-02 | [NEO-113](https://linear.app/neon-sprawl/issue/NEO-113) |
| E7M1-03 | [NEO-114](https://linear.app/neon-sprawl/issue/NEO-114) |
| E7M1-04 | [NEO-115](https://linear.app/neon-sprawl/issue/NEO-115) |
| E7M1-05 | [NEO-116](https://linear.app/neon-sprawl/issue/NEO-116) |
| E7M1-06 | [NEO-117](https://linear.app/neon-sprawl/issue/NEO-117) |
| E7M1-07 | [NEO-118](https://linear.app/neon-sprawl/issue/NEO-118) |
| E7M1-08 | [NEO-119](https://linear.app/neon-sprawl/issue/NEO-119) |
| E7M1-09 | [NEO-120](https://linear.app/neon-sprawl/issue/NEO-120) |
| E7M1-10 | [NEO-121](https://linear.app/neon-sprawl/issue/NEO-121) |
| E7M1-11 | [NEO-122](https://linear.app/neon-sprawl/issue/NEO-122) |
| E7M1-12 | [NEO-123](https://linear.app/neon-sprawl/issue/NEO-123) |
## Prototype Slice 1 freeze (E7M1-01)
The **first shipped quest spine** is **frozen** for prototype tuning until a deliberate migration issue expands the roster.
| `QuestDef.id` | `displayName` | `prerequisiteQuestIds` | Steps (summary) |
|---------------|---------------|------------------------|-----------------|
| **`prototype_quest_gather_intro`** | Intro: Salvage Run | *(none)* | 1× **`gather_item`**: **`scrap_metal_bulk` ×3** |
| **`prototype_quest_refine_intro`** | Intro: Refine Stock | **`prototype_quest_gather_intro`** | 1× **`craft_recipe`**: **`refine_scrap_standard`** ×1 |
| **`prototype_quest_combat_intro`** | Intro: Clear the Pocket | *(none)* | 1× **`encounter_complete`**: **`prototype_combat_pocket`** |
| **`prototype_quest_operator_chain`** | Operator Chain | **`prototype_quest_gather_intro`**, **`prototype_quest_refine_intro`**, **`prototype_quest_combat_intro`** | 4 steps: gather **`scrap_metal_bulk` ×5** → craft **`refine_scrap_standard`** → craft **`make_field_stim_mk0`** → **`inventory_has_item`** **`contract_handoff_token` ×1** |
**CI enforcement (NEO-112):** `scripts/validate_content.py` requires **exactly** these four quest ids; objective cross-refs to frozen item/recipe/encounter catalogs; acyclic **`prerequisiteQuestIds`**; chain quest terminal step uses **`contract_handoff_token`** from [E5.M3 encounter loot](E5_M3_EncounterAndRewardTables.md#prototype-slice-3-freeze-e5m3-01).
**Reward policy (Slice 1):** Quest completion updates **`QuestStepState` only** — no duplicate item/XP grants. Gather/craft/encounter paths keep existing payouts; [E7.M2](E7_M2_RewardAndUnlockRouter.md) adds **`QuestRewardBundle`** apply in Slice 2.
## Risks and telemetry
- Scripting weight: keep `QuestDef` data-first; templates in decomposition/tooling path per epic.

File diff suppressed because one or more lines are too long

View File

@ -108,6 +108,8 @@ Gameplay **content and curves** default to **boot load** with optional **dev-onl
| Module ID | Module Name | Depends On | Contract Needed | Phase Required | Status |
|---|---|---|---|---|---|
| E7.M1 | QuestStateMachine | E3.M2, E5.M1 | QuestDef, QuestStepState, QuestStateTransition | Prototype | Planned |
**E7.M1 note:** Epic 7 **Slice 1** backlog in Linear ([Epic 7 — Questing, Narrative, and Faction Reputation](https://linear.app/neon-sprawl/project/epic-7-questing-narrative-and-faction-reputation-a9416783ceee)): [NEO-112](https://linear.app/neon-sprawl/issue/NEO-112) → [NEO-123](https://linear.app/neon-sprawl/issue/NEO-123); label **`E7.M1`**. See [E7M1-prototype-backlog.md](../../plans/E7M1-prototype-backlog.md), [E7_M1_QuestStateMachine.md](E7_M1_QuestStateMachine.md). Upstream: E3.M1 gather **Ready**, E3.M2 craft **Ready**, E5.M3 encounter **Ready**. Client capstone **E7M1-12** [NEO-123](https://linear.app/neon-sprawl/issue/NEO-123). Register row stays **Planned** until **NEO-112** lands.
| E7.M2 | RewardAndUnlockRouter | E2.M2, E3.M3, E7.M1 | QuestRewardBundle, UnlockGrant, RewardDeliveryEvent | Prototype | Planned |
| E7.M3 | FactionReputationLedger | E7.M1 | FactionStanding, ReputationDelta, FactionGateRule | Pre-production | Planned |
| E7.M4 | ContractMissionGenerator | E4.M1, E5.M3, E7.M3 | ContractTemplate, ContractSeed, ContractOutcome | Pre-production | Planned |

View File

@ -0,0 +1,392 @@
# E7.M1 — Prototype story backlog (QuestStateMachine)
Working backlog for **Epic 7 — Slice 1** ([quest core and persistence](../decomposition/epics/epic_07_quest_faction.md#slice-1---quest-core-and-persistence)). Decomposition and contracts: [E7.M1 — QuestStateMachine](../decomposition/modules/E7_M1_QuestStateMachine.md).
**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)](../plans/E5M3-prototype-backlog.md).
**Labels (Linear):** every issue **`E7.M1`**; add **`server`** or **`client`** + **`Story`** as listed.
**Precursor (do not re-scope):** [E5.M3](../decomposition/modules/E5_M3_EncounterAndRewardTables.md) encounter complete grants **`contract_handoff_token`** + records **`EncounterCompleteEvent`** ([NEO-107](https://linear.app/neon-sprawl/issue/NEO-107)) — prototype quest credit stand-in until [E7.M2](../decomposition/modules/E7_M2_RewardAndUnlockRouter.md) **`QuestRewardBundle`** router lands. **NEO-43** [`MissionRewardSkillXpGrant`](../../server/NeonSprawl.Server/Game/Skills/MissionRewardSkillXpGrant.cs) 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](../decomposition/modules/E3_M1_ResourceNodeAndGatherLoop.md) gather on interact ([NEO-63](https://linear.app/neon-sprawl/issue/NEO-63)); [E3.M2](../decomposition/modules/E3_M2_RefinementAndRecipeExecution.md) craft POST ([NEO-70](https://linear.app/neon-sprawl/issue/NEO-70)); [E5.M3](../decomposition/modules/E5_M3_EncounterAndRewardTables.md) encounter wiring + progress GET ([NEO-106](https://linear.app/neon-sprawl/issue/NEO-106), [NEO-108](https://linear.app/neon-sprawl/issue/NEO-108)). [E3S5](../plans/E3S5-client-prototype-backlog.md) client economy HUD ([NEO-72](https://linear.app/neon-sprawl/issue/NEO-72)[NEO-75](https://linear.app/neon-sprawl/issue/NEO-75)).
**Prototype quest spine (frozen in E7M1-01):** **three** solo onboarding quests + **one** multi-step chain quest (vision minimum 35 onboarding + one integrated chain). **Solo only** — no party credit ([quest_scope_and_party.md](../decomposition/modules/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](https://linear.app/neon-sprawl/issue/NEO-112) |
| E7M1-02 | server | [NEO-113](https://linear.app/neon-sprawl/issue/NEO-113) |
| E7M1-03 | server | [NEO-114](https://linear.app/neon-sprawl/issue/NEO-114) |
| E7M1-04 | server | [NEO-115](https://linear.app/neon-sprawl/issue/NEO-115) |
| E7M1-05 | server | [NEO-116](https://linear.app/neon-sprawl/issue/NEO-116) |
| E7M1-06 | server | [NEO-117](https://linear.app/neon-sprawl/issue/NEO-117) |
| E7M1-07 | server | [NEO-118](https://linear.app/neon-sprawl/issue/NEO-118) |
| E7M1-08 | server | [NEO-119](https://linear.app/neon-sprawl/issue/NEO-119) |
| E7M1-09 | server | [NEO-120](https://linear.app/neon-sprawl/issue/NEO-120) |
| E7M1-10 | server | [NEO-121](https://linear.app/neon-sprawl/issue/NEO-121) |
| E7M1-11 | client | [NEO-122](https://linear.app/neon-sprawl/issue/NEO-122) |
| E7M1-12 | client | [NEO-123](https://linear.app/neon-sprawl/issue/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 **112** 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](../decomposition/modules/E7_M2_RewardAndUnlockRouter.md) idempotent **`QuestRewardBundle`** apply + consume **`EncounterCompleteEvent`**; [E7.M3](../decomposition/modules/E7_M3_FactionReputationLedger.md) faction ledger (pre-production); [E8.M1](../decomposition/modules/E8_M1_PartyAndMatchAssembly.md) 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](../decomposition/modules/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](../decomposition/modules/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-ref **`itemId`** / **`recipeId`** / **`encounterId`** to frozen catalogs, **`prerequisiteQuestIds`** acyclic + known ids, objective kind enum.
- Designer note in [E7_M1](../decomposition/modules/E7_M1_QuestStateMachine.md) + `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 `id` fails 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.md` section.
**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; **`AddQuestDefinitionRegistry`** DI 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 in `Game/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](https://linear.app/neon-sprawl/issue/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 **`V00X`** when DB configured (mirror encounter/inventory pattern).
- **`QuestStepState`** / **`PlayerQuestProgressRow`** types 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`**, **`TryMarkComplete`** with structured **`reasonCode`** denials (`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](https://linear.app/neon-sprawl/issue/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.TryGather`** success path — **`gather_item`** objectives.
- **`CraftOperations.TryCraft`** success path — **`craft_recipe`** objectives.
- **`EncounterCompletionOperations.TryCompleteAndGrant`** success path — **`encounter_complete`** objectives.
- 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`** / **`TryMarkComplete`** when 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_intro`** advances when **`scrap_metal_bulk`** gather succeeds.
- [ ] Active **`prototype_quest_refine_intro`** advances on **`refine_scrap_standard`** craft success.
- [ ] Active **`prototype_quest_combat_intro`** advances when **`prototype_combat_pocket`** completes.
- [ ] Chain quest steps advance in order across mixed objective kinds.
**Client counterpart:** progress visible via [NEO-122](https://linear.app/neon-sprawl/issue/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-1`** player.
- [ ] Completed quests show **`completed`** with step count at terminal step.
**Client counterpart:** [NEO-122](https://linear.app/neon-sprawl/issue/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 **`reasonCode`** without mutation.
**Client counterpart:** [NEO-122](https://linear.app/neon-sprawl/issue/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`** / **`QuestObjectiveWiring`** at accept, step advance, and complete.
- Reserved deny hook **`quest_accept_denied`** (optional comment).
- `server/README.md` subsection; 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`, optional `quest_definitions_client.gd` for display names.
- HUD: **`QuestProgressLabel`** (active/completed summary), **`QuestAcceptFeedbackLabel`** (deny/success copy).
- Boot hydrate + refresh after gather/craft/cast/encounter events (mirror **`encounter_progress_client.gd`** pattern).
- 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.md` section.
- `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 quest **`prototype_quest_operator_chain`** completes when **`contract_handoff_token`** held.
- **`client/README.md`**: **End-to-end onboarding quest loop** section — flow table; cross-links NEO-112NEO-122.
- Module alignment on completion: [E7_M1](../decomposition/modules/E7_M1_QuestStateMachine.md), [documentation_and_implementation_alignment.md](../decomposition/modules/documentation_and_implementation_alignment.md), [module_dependency_register.md](../decomposition/modules/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.md`** with server + client.
- [ ] Epic 7 Slice 1 AC: 3 onboarding + 1 chain complete without duplicate completion on replay.
- [ ] Re-read [epic_07 Definition of Done](../decomposition/epics/epic_07_quest_faction.md#definition-of-done) for prototype minimums.
**Client counterpart:** this story **is** the Slice 1 client capstone.
---
## Decomposition complete checklist
- [x] Every player-visible AC has a **`client`** Linear issue (NEO-122, NEO-123)
- [x] No forbidden deferral language in backlog
- [x] Capstone client story exists (E7M1-12 / NEO-123)
- [x] Epic DoD updated for Godot verification
- [x] [documentation_and_implementation_alignment.md](../decomposition/modules/documentation_and_implementation_alignment.md) E7.M1 row updated
- [x] [module_dependency_register.md](../decomposition/modules/module_dependency_register.md) E7.M1 note updated
## Related docs
- [epic_07_quest_faction.md](../decomposition/epics/epic_07_quest_faction.md)
- [quest_scope_and_party.md](../decomposition/modules/quest_scope_and_party.md)
- [E5M3-prototype-backlog.md](E5M3-prototype-backlog.md) (encounter token precursor)
- [E3S5-client-prototype-backlog.md](E3S5-client-prototype-backlog.md) (gather/craft client HUD)
- [neon_sprawl_vision.plan.md — Prototype Scope](../../neon_sprawl_vision.plan.md)