Merge pull request #183 from ViPro-Technologies/NEO-143-playable-faction-reputation-gate-capstone

NEO-143: Playable faction reputation + gate capstone (Godot)
pull/186/head
VinPropane 2026-06-17 22:24:10 -04:00 committed by GitHub
commit 74b6586004
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 314 additions and 7 deletions

View File

@ -266,6 +266,30 @@ Full checklist: [`docs/manual-qa/NEO-131.md`](../docs/manual-qa/NEO-131.md).
Full checklist: [`docs/manual-qa/NEO-142.md`](../docs/manual-qa/NEO-142.md).
## End-to-end faction reputation loop (NEO-143)
Epic 7 Slice 3 capstone — complete the NEO-132 four-quest reward path **in Godot**, earn Grid Operators rep from operator chain, accept faction-gated **`prototype_quest_grid_contract`** once, and verify Rust Collective rep on hand-in.
**Flow:** fresh server restart → NEO-132 four-quest path → standing **15****Shift+Q** grid contract accept/complete → Rust Collective **+10** → Godot restart idempotency.
| Step | Input / trigger | HUD / server outcome |
|------|-----------------|----------------------|
| Boot | Godot **F5** + fresh server | Five quests **`not started`**; faction standing **0** / **0** |
| Operator chain | **Shift+Q** + chain objectives (NEO-132) | Operator Chain **`completed`**; **`Grid Operators: 15`**; reward label includes **`Grid Operators +15 rep`**; bag has **`survey_drone_kit`** |
| Grid contract | **Shift+Q** after operator chain | Accept succeeds at standing **15**; quest **`completed`** immediately (kit held); **`Rust Collective: 10`**; reward label **`Rust Collective +10 rep`** |
| Idempotency | Stop Godot + **F5** (server still running) | Five quests still **`completed`**; faction standing unchanged; reward label **`—`** |
| Duplicate accept | **Shift+Q** on finished quests | **`no eligible quest`**; standing unchanged |
**Gate deny (component QA):** readable **`faction_gate_blocked`** copy on **`QuestAcceptFeedbackLabel`** is verified in [NEO-142](../docs/manual-qa/NEO-142.md) — not the main capstone success path (operator chain grants **+15** atomically on completion).
**Cross-links:** [NEO-133](../docs/plans/NEO-133-implementation-plan.md) content freeze · [NEO-136](../docs/plans/NEO-136-implementation-plan.md)[NEO-138](../docs/plans/NEO-138-implementation-plan.md) rep apply · [NEO-139](../docs/plans/NEO-139-implementation-plan.md) standing GET · [NEO-140](../docs/plans/NEO-140-implementation-plan.md) gate/rep projections · [NEO-142](../docs/manual-qa/NEO-142.md) faction HUD · [NEO-132](../docs/manual-qa/NEO-132.md) reward capstone · [NEO-123](../docs/manual-qa/NEO-123.md) onboarding flow.
**Scripts:** `faction_standing_client.gd`, `quest_hud_controller.gd`, `quest_progress_client.gd`, `quest_definitions_client.gd` — wired from `main.gd`.
**Preconditions:** **Server restart** before capstone run resets quest progress, faction standing, reward delivery, inventory, and skill progression.
Full capstone checklist: [`docs/manual-qa/NEO-143.md`](../docs/manual-qa/NEO-143.md).
## End-to-end quest reward loop (NEO-132)
Epic 7 Slice 2 capstone — complete all four prototype quests **in Godot** and verify quest completion bundles (skill XP + item grants) apply **once** with idempotent replay.

View File

@ -7,7 +7,7 @@
| **Module ID** | E7.M3 |
| **Epic** | [Epic 7 — Quest / Faction](../epics/epic_07_quest_faction.md) |
| **Stage target** | Pre-production |
| **Status** | In Progress**E7M3-01 catalog landed** ([NEO-133](https://linear.app/neon-sprawl/issue/NEO-133)); **E7M3-02 server faction catalog load landed** ([NEO-134](https://linear.app/neon-sprawl/issue/NEO-134)): fail-fast `content/factions/*_factions.json`, `IFactionDefinitionRegistry`, quest faction cross-ref + E7M3 bundle/grid gates; **E7M3-03 faction standing + reputation delta stores landed** ([NEO-135](https://linear.app/neon-sprawl/issue/NEO-135)): `IFactionStandingStore`, `IReputationDeltaStore`, in-memory + Postgres `V009`/`V010`; **E7M3-04 `ReputationOperations` landed** ([NEO-136](https://linear.app/neon-sprawl/issue/NEO-136)): auditable `TryApplyDelta` orchestration ([NEO-136 plan](../../plans/NEO-136-implementation-plan.md)); **E7M3-05 `FactionGateOperations` landed** ([NEO-137](https://linear.app/neon-sprawl/issue/NEO-137)): quest accept gate eval + `faction_gate_blocked` ([NEO-137 plan](../../plans/NEO-137-implementation-plan.md)); **E7M3-06 reward router rep grants landed** ([NEO-138](https://linear.app/neon-sprawl/issue/NEO-138)): `RewardRouterOperations` applies `reputationGrants` idempotently via `ReputationOperations` ([NEO-138 plan](../../plans/NEO-138-implementation-plan.md)); **E7M3-07 faction standing HTTP read landed** ([NEO-139](https://linear.app/neon-sprawl/issue/NEO-139)): `GET …/faction-standing` snapshot API + Bruno ([NEO-139 plan](../../plans/NEO-139-implementation-plan.md)); **E7M3-08 quest HTTP projections landed** ([NEO-140](https://linear.app/neon-sprawl/issue/NEO-140)): world GET `factionGateRules` + quest-progress `completionRewardSummary.reputationGrants` ([NEO-140 plan](../../plans/NEO-140-implementation-plan.md)); **E7M3-09 faction telemetry hooks landed** ([NEO-141](https://linear.app/neon-sprawl/issue/NEO-141)): comment-only **`reputation_delta`** + **`faction_gate_blocked`** in ops layers + README ([NEO-141 plan](../../plans/NEO-141-implementation-plan.md)); **E7M3-10 client faction HUD landed** ([NEO-142](https://linear.app/neon-sprawl/issue/NEO-142)): `faction_standing_client.gd`, `FactionStandingLabel`, readable `faction_gate_blocked` deny, `reputationGrants` reward lines ([NEO-142 plan](../../plans/NEO-142-implementation-plan.md)); [client README — Faction standing + gate feedback HUD (NEO-142)](../../../client/README.md#faction-standing--gate-feedback-hud-neo-142); [`NEO-142` manual QA](../../manual-qa/NEO-142.md). Slice 3 backlog **E7M3-11** [NEO-143](https://linear.app/neon-sprawl/issue/NEO-143). Upstream: E7.M1 **Ready**, E7.M2 **Ready**. |
| **Status** | Ready**E7M3-01 catalog landed** ([NEO-133](https://linear.app/neon-sprawl/issue/NEO-133)); **E7M3-02 server faction catalog load landed** ([NEO-134](https://linear.app/neon-sprawl/issue/NEO-134)): fail-fast `content/factions/*_factions.json`, `IFactionDefinitionRegistry`, quest faction cross-ref + E7M3 bundle/grid gates; **E7M3-03 faction standing + reputation delta stores landed** ([NEO-135](https://linear.app/neon-sprawl/issue/NEO-135)): `IFactionStandingStore`, `IReputationDeltaStore`, in-memory + Postgres `V009`/`V010`; **E7M3-04 `ReputationOperations` landed** ([NEO-136](https://linear.app/neon-sprawl/issue/NEO-136)): auditable `TryApplyDelta` orchestration ([NEO-136 plan](../../plans/NEO-136-implementation-plan.md)); **E7M3-05 `FactionGateOperations` landed** ([NEO-137](https://linear.app/neon-sprawl/issue/NEO-137)): quest accept gate eval + `faction_gate_blocked` ([NEO-137 plan](../../plans/NEO-137-implementation-plan.md)); **E7M3-06 reward router rep grants landed** ([NEO-138](https://linear.app/neon-sprawl/issue/NEO-138)): `RewardRouterOperations` applies `reputationGrants` idempotently via `ReputationOperations` ([NEO-138 plan](../../plans/NEO-138-implementation-plan.md)); **E7M3-07 faction standing HTTP read landed** ([NEO-139](https://linear.app/neon-sprawl/issue/NEO-139)): `GET …/faction-standing` snapshot API + Bruno ([NEO-139 plan](../../plans/NEO-139-implementation-plan.md)); **E7M3-08 quest HTTP projections landed** ([NEO-140](https://linear.app/neon-sprawl/issue/NEO-140)): world GET `factionGateRules` + quest-progress `completionRewardSummary.reputationGrants` ([NEO-140 plan](../../plans/NEO-140-implementation-plan.md)); **E7M3-09 faction telemetry hooks landed** ([NEO-141](https://linear.app/neon-sprawl/issue/NEO-141)): comment-only **`reputation_delta`** + **`faction_gate_blocked`** in ops layers + README ([NEO-141 plan](../../plans/NEO-141-implementation-plan.md)); **E7M3-10 client faction HUD landed** ([NEO-142](https://linear.app/neon-sprawl/issue/NEO-142)): `faction_standing_client.gd`, `FactionStandingLabel`, readable `faction_gate_blocked` deny, `reputationGrants` reward lines ([NEO-142 plan](../../plans/NEO-142-implementation-plan.md)); [client README — Faction standing + gate feedback HUD (NEO-142)](../../../client/README.md#faction-standing--gate-feedback-hud-neo-142); [`NEO-142` manual QA](../../manual-qa/NEO-142.md). **E7M3-11 client capstone landed** ([NEO-143](https://linear.app/neon-sprawl/issue/NEO-143)): playable faction reputation + gate capstone — [`NEO-143` manual QA](../../manual-qa/NEO-143.md); [client README — End-to-end faction reputation loop (NEO-143)](../../../client/README.md#end-to-end-faction-reputation-loop-neo-143); plan [NEO-143](../../plans/NEO-143-implementation-plan.md). **Epic 7 Slice 3 client capstone complete.** Upstream: E7.M1 **Ready**, E7.M2 **Ready**. |
| **Linear** | Label **`E7.M3`** · [E7M3-pre-production-backlog.md](../../plans/E7M3-pre-production-backlog.md) |
## Purpose

File diff suppressed because one or more lines are too long

View File

@ -113,9 +113,9 @@ Gameplay **content and curves** default to **boot load** with optional **dev-onl
| E7.M2 | RewardAndUnlockRouter | E2.M2, E3.M3, E7.M1 | QuestRewardBundle, UnlockGrant, RewardDeliveryEvent | Prototype | Ready |
**E7.M2 note:** Epic 7 **Slice 2** 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-124](https://linear.app/neon-sprawl/issue/NEO-124) → [NEO-132](https://linear.app/neon-sprawl/issue/NEO-132) **landed**; label **`E7.M2`**. See [E7M2-prototype-backlog.md](../../plans/E7M2-prototype-backlog.md), [E7_M2_RewardAndUnlockRouter.md](E7_M2_RewardAndUnlockRouter.md). Upstream: E7.M1 **Ready**, E2.M2 grant stack ([NEO-43](https://linear.app/neon-sprawl/issue/NEO-43)), E3.M3 inventory **Ready**. **E7M2-09 / NEO-132** client capstone landed — [`NEO-132` manual QA](../../manual-qa/NEO-132.md), [client README — End-to-end quest reward loop (NEO-132)](../../../client/README.md#end-to-end-quest-reward-loop-neo-132), plan [NEO-132](../../plans/NEO-132-implementation-plan.md). Epic 7 Slice 2 client capstone complete.
| E7.M3 | FactionReputationLedger | E7.M1, E7.M2 | FactionStanding, ReputationDelta, FactionGateRule | Pre-production | In Progress |
| E7.M3 | FactionReputationLedger | E7.M1, E7.M2 | FactionStanding, ReputationDelta, FactionGateRule | Pre-production | Ready |
**E7.M3 note:** Epic 7 **Slice 3** 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-133](https://linear.app/neon-sprawl/issue/NEO-133) → [NEO-143](https://linear.app/neon-sprawl/issue/NEO-143); label **`E7.M3`**. See [E7M3-pre-production-backlog.md](../../plans/E7M3-pre-production-backlog.md), [E7_M3_FactionReputationLedger.md](E7_M3_FactionReputationLedger.md). Upstream: E7.M1 **Ready**, E7.M2 **Ready**. Prototype spine: two frozen factions; operator chain grants **+15 Grid Operators**; **`prototype_quest_grid_contract`** gated at **minStanding 15**; rep grants via **`completionRewardBundle.reputationGrants`**; accept deny **`faction_gate_blocked`**. Client capstone **E7M3-11** [NEO-143](https://linear.app/neon-sprawl/issue/NEO-143).
**E7.M3 note:** Epic 7 **Slice 3** 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-133](https://linear.app/neon-sprawl/issue/NEO-133) → [NEO-143](https://linear.app/neon-sprawl/issue/NEO-143) **landed**; label **`E7.M3`**. See [E7M3-pre-production-backlog.md](../../plans/E7M3-pre-production-backlog.md), [E7_M3_FactionReputationLedger.md](E7_M3_FactionReputationLedger.md). Upstream: E7.M1 **Ready**, E7.M2 **Ready**. Prototype spine: two frozen factions; operator chain grants **+15 Grid Operators**; **`prototype_quest_grid_contract`** gated at **minStanding 15**; rep grants via **`completionRewardBundle.reputationGrants`**; accept deny **`faction_gate_blocked`**. **E7M3-11 / NEO-143** client capstone landed — [`NEO-143` manual QA](../../manual-qa/NEO-143.md), [client README — End-to-end faction reputation loop (NEO-143)](../../../client/README.md#end-to-end-faction-reputation-loop-neo-143), plan [NEO-143](../../plans/NEO-143-implementation-plan.md). Epic 7 Slice 3 client capstone complete.
| E7.M4 | ContractMissionGenerator | E4.M1, E5.M3, E7.M3 | ContractTemplate, ContractSeed, ContractOutcome | Pre-production | Planned |
### Epic 8 — Social / Guild

View File

@ -108,6 +108,7 @@ Follow [NEO-123](NEO-123.md) for accept order, anchors, material math, and quest
- Quest completion bundles are **idempotent** server-side (`{playerId}:quest_complete:{questId}`); Godot restart must not replay grant copy on **`QuestRewardDeliveryLabel`** (NEO-131 transition-only).
- Economy HUD auto-refreshes on quest completion transition (NEO-132); no manual **I** required for reward verification.
- Epic 7 Slice 2 AC: idempotent reward delivery; replays cannot double-claim ([epic_07 Slice 2](../decomposition/epics/epic_07_quest_faction.md#slice-2---reward-and-unlock-routing)).
- **Faction standing + grid contract:** defer to [NEO-143](NEO-143.md) Slice 3 capstone (extends this checklist through **`prototype_quest_grid_contract`**).
## Acceptance

View File

@ -40,4 +40,4 @@
- [NEO-122](NEO-122.md) — quest progress + accept HUD unchanged except new label below reward label.
- [NEO-131](NEO-131.md) — reward label transition-only behavior unchanged except rep grant lines.
**Full-flow capstone:** [NEO-143](NEO-143.md) extends this with end-to-end Slice 3 faction reputation verification.
**Full-flow capstone:** [NEO-143](NEO-143.md) extends [NEO-132](NEO-132.md) with end-to-end Slice 3 faction reputation verification (operator chain **+15** → grid contract **+10** → idempotency). Gate deny remains component QA here (step 6).

View File

@ -0,0 +1,118 @@
# NEO-143 — Manual QA checklist
| Field | Value |
|-------|-------|
| Key | NEO-143 |
| Title | E7M3-11: Playable faction reputation + gate capstone (Godot) |
| Linear | https://linear.app/neon-sprawl/issue/NEO-143/e7m3-11-playable-faction-reputation-gate-capstone-godot |
| Plan | `docs/plans/NEO-143-implementation-plan.md` |
| Branch | `NEO-143-playable-faction-reputation-gate-capstone` |
## Preconditions
- **Fresh dev player:** stop any running server, then start a new instance so in-memory quest progress, faction standing, **`IRewardDeliveryStore`**, inventory, skill progression, encounter progress, NPC HP, and resource-node depletion reset.
- **No Bruno/curl** for this checklist — accept uses **Q** / **Shift+Q**; gameplay uses **R**, **Tab** + **1**, and Economy HUD **Craft** buttons only.
- NEO-133NEO-142 faction spine and NEO-132 reward capstone landed on `main`.
- **`Economy HUD`** toggle **on** for inventory + skill verification.
## Faction + quest freeze (E7M3-01)
| Quest id | Display name | Rep grant on completion | Expected standing after |
|----------|--------------|-------------------------|-------------------------|
| `prototype_quest_operator_chain` | Operator Chain | **Grid Operators +15** (plus item/skill bundle — see NEO-132) | Grid Operators **15**, Rust Collective **0** |
| `prototype_quest_grid_contract` | Grid Contract | **Rust Collective +10** | Grid Operators **15**, Rust Collective **10** |
**Gate (component QA — not main capstone path):** accept **`prototype_quest_grid_contract`** at Grid Operators standing **0** with operator-chain prerequisite met ⇒ **`faction_gate_blocked`** with readable copy. Verified in [NEO-142](NEO-142.md) GdUnit (`client/test/faction_standing_hud_test.gd`) and optional dev fixture — organic Godot gameplay grants **+15** on operator-chain completion, so live capstone accept succeeds at standing **15**.
## Quest roster (five ids, catalog order)
| Quest id | Display name |
|----------|--------------|
| `prototype_quest_combat_intro` | Intro: Clear the Pocket |
| `prototype_quest_gather_intro` | Intro: Salvage Run |
| `prototype_quest_grid_contract` | Grid Contract |
| `prototype_quest_operator_chain` | Operator Chain |
| `prototype_quest_refine_intro` | Intro: Refine Stock |
**Shift+Q after operator chain:** only **`prototype_quest_grid_contract`** is eligible (**`not_started`**, prerequisite **`completed`**). Grid contract has one step — **`inventory_has_item`** **`survey_drone_kit` ×1** — satisfied from operator-chain reward; expect **`completed`** immediately on accept (no extra player action).
## Expected HUD progression
| Phase | `FactionStandingLabel` | `QuestProgressLabel` | `QuestRewardDeliveryLabel` |
|-------|------------------------|----------------------|----------------------------|
| Boot | Grid Operators **0**, Rust Collective **0** | Five quests **`not started`** | `Quest rewards:` / `—` |
| Operator chain **`completed`** | Grid Operators **15**, Rust Collective **0** | Four intros + operator chain **`completed`**; grid contract **`not started`** | Operator chain grants + **`Grid Operators +15 rep`** |
| Grid contract **`completed`** | Grid Operators **15**, Rust Collective **10** | All **five** **`completed`** | Grid Contract + **`Rust Collective +10 rep`** |
| Godot restart | Unchanged | All five still **`completed`** | `—` (transition-only) |
| Duplicate accepts | Unchanged | Unchanged | Unchanged |
## Checklist
Follow [NEO-132](NEO-132.md) steps **119** for the four-quest reward path (accept order, anchors, material math, reward + economy verification through operator chain). **Extend** with faction + fifth-quest steps below.
### Boot (faction baseline)
1. Start server: `cd server/NeonSprawl.Server && dotnet run`.
2. Run Godot main scene (**F5**). Confirm **`FactionStandingLabel`** shows **`Grid Operators: 0`**, **`Rust Collective: 0`**; **`QuestProgressLabel`** lists **five** quests all **`not started`**.
### Slice 2 reward path (inherit NEO-132)
3. Complete [NEO-132](NEO-132.md) steps **319** — four intros + operator chain with reward/economy checks at each completion.
### Faction standing after operator chain
4. Verify **`FactionStandingLabel`**: **`Grid Operators: 15`**, **`Rust Collective: 0`** — may lag reward label rep line by one faction-standing GET round-trip; confirm both within a moment after operator-chain completion.
5. Verify **`QuestRewardDeliveryLabel`** on operator-chain completion includes **`Grid Operators +15 rep`** (alongside item/skill lines).
6. Verify **`InventoryLabel`** still lists **`survey_drone_kit`**.
7. Verify **`QuestProgressLabel`**: grid contract **`not started`**; four prior quests **`completed`**.
### Grid contract accept + complete (faction-gated success path)
8. Press **Shift+Q** to accept **`prototype_quest_grid_contract`**. Verify **`QuestAcceptFeedbackLabel`** shows accept success (not **`faction_gate_blocked`**).
9. Verify **`Grid Contract: completed`** on **`QuestProgressLabel`** (instant complete — kit already held).
10. Verify **`QuestRewardDeliveryLabel`**: `Grid Contract` + **`Rust Collective +10 rep`**.
11. Verify **`FactionStandingLabel`**: **`Rust Collective: 10`** (Grid Operators still **15**).
### Capstone snapshot (record for idempotency)
12. **Record** final state:
- **`FactionStandingLabel`**: Grid Operators **15**, Rust Collective **10**
- **`QuestProgressLabel`**: all **five** quests **`completed`**
- **`SkillProgressionLabel`** / **`InventoryLabel`**: match NEO-132 step 20 totals
### Idempotency (no duplicate rep delivery)
13. **Godot restart:** stop Godot (**Shift+F5**); **F5** again **without** stopping the server.
14. Verify all five quests still **`completed`**; **`QuestRewardDeliveryLabel`** is **`—`** (transition-only).
15. Verify **`FactionStandingLabel`** counts **match step 12** exactly.
16. Press **Shift+Q**. Verify **`Quest accept: no eligible quest`** (or equivalent deny).
17. Verify faction standing and economy HUD counts **still match step 12** — no duplicate rep grants.
### Regression
18. Component faction HUD + gate deny: [NEO-142 manual QA](NEO-142.md) (GdUnit gate deny; optional dev fixture).
19. Slice 2 reward idempotency: [NEO-132 manual QA](NEO-132.md).
20. Slice 1 onboarding chain: [NEO-123 manual QA](NEO-123.md).
### Server AC (optional — not Godot gameplay)
```bash
cd /path/to/neon-sprawl && dotnet test NeonSprawl.sln \
--filter "FullyQualifiedName~ReputationOperationsTests|FullyQualifiedName~FactionGateOperationsTests|FullyQualifiedName~RewardRouterOperationsTests"
```
Expect all pass — auditable **`ReputationDelta`** apply + fail-closed gate evaluation. Content CI (`scripts/validate_content.py`) and server startup catalog cross-ref deny unknown faction ids at load (tamper fail-closed).
## Notes
- **Gate deny** is not exercised on the main capstone success path — see [NEO-142](NEO-142.md) step 6.
- Rep grants apply on quest **completion**, not accept; faction standing refreshes after in-session completion transition (NEO-142).
- Epic 7 Slice 3 AC: reputation deltas auditable; gates fail closed on tamper ([epic_07 Slice 3](../decomposition/epics/epic_07_quest_faction.md#slice-3---faction-ledger-pre-production)).
## Acceptance
- [ ] Steps 117 completable in one session without Bruno/curl.
- [ ] Operator chain grants Grid Operators **+15**; grid contract grants Rust Collective **+10** — visible on HUD.
- [ ] Faction-gated grid contract accept succeeds at standing **15** and completes with kit in bag.
- [ ] Godot restart + duplicate accepts leave faction standing unchanged (no double-claim).
- [ ] Epic 7 Slice 3 AC satisfied in Godot (gate deny regression via NEO-142; server AC via automated tests).

View File

@ -358,8 +358,8 @@ Accepting **`prototype_quest_grid_contract`** before operator-chain completion o
**Acceptance criteria**
- [ ] Human completes **`docs/manual-qa/NEO-143.md`** with server + client.
- [ ] Epic 7 Slice 3 AC: reputation deltas auditable; gates fail closed on tamper (unknown faction denied at startup).
- [ ] Re-read [epic_07 Slice 3 AC](../decomposition/epics/epic_07_quest_faction.md#slice-3---faction-ledger-pre-production).
- [x] Epic 7 Slice 3 AC: reputation deltas auditable; gates fail closed on tamper (unknown faction denied at startup) — server automated tests pass; gate deny component QA in [NEO-142](../manual-qa/NEO-142.md).
- [x] Re-read [epic_07 Slice 3 AC](../decomposition/epics/epic_07_quest_faction.md#slice-3---faction-ledger-pre-production).
**Client counterpart:** this story **is** the Slice 3 client capstone.

View File

@ -0,0 +1,164 @@
# NEO-143 — E7M3-11: Playable faction reputation + gate capstone (Godot)
**Linear:** [NEO-143](https://linear.app/neon-sprawl/issue/NEO-143)
**Branch:** `NEO-143-playable-faction-reputation-gate-capstone`
**Backlog:** [E7M3-pre-production-backlog.md](E7M3-pre-production-backlog.md) — **E7M3-11**
**Module:** [E7_M3_FactionReputationLedger.md](../decomposition/modules/E7_M3_FactionReputationLedger.md)
**Pattern:** [NEO-132](NEO-132-implementation-plan.md) — Slice 2 capstone docs-primary; [NEO-142](NEO-142-implementation-plan.md) — faction HUD component landed
**Precursors:** [NEO-142](https://linear.app/neon-sprawl/issue/NEO-142) **`Done`** — standing label, gate deny copy, rep reward lines; [NEO-133](https://linear.app/neon-sprawl/issue/NEO-133)[NEO-141](https://linear.app/neon-sprawl/issue/NEO-141) server faction spine **landed**
**Server counterpart:** NEO-133NEO-141 authoritative faction/rep/gate stack; Bruno is **not** prototype-complete per [full-stack epic decomposition](../../.cursor/rules/full-stack-epic-decomposition.md)
## Goal
Prove Epic 7 Slice 3 acceptance **in Godot**: earn rep from operator chain, accept gated faction quest once; gates fail closed.
## Kickoff clarifications
| Topic | Question | Agent recommendation | Answer |
|-------|----------|----------------------|--------|
| **Implementation scope** | Docs-only vs client code? | **Docs + manual QA primary**; fix integration gaps only if capstone QA fails on `main` — [NEO-132](NEO-132-implementation-plan.md) precedent; NEO-142 HUD already landed. | **Adopted** — docs-primary |
| **Capstone path** | Extend NEO-132 full flow vs shorter faction-only path? | **Extend full [NEO-132](NEO-132-implementation-plan.md) four-quest flow** + fifth quest **`prototype_quest_grid_contract`** accept/complete — backlog E7M3-11 explicit session; grid contract auto-completes on accept when **`survey_drone_kit`** is already in bag. | **Adopted** — extend NEO-132 |
| **Gate deny verification** | How to satisfy backlog “retry accept gate before rep (denied)” when operator chain grants **+15** atomically? | **Regression to [NEO-142](NEO-142-implementation-plan.md)** (GdUnit + optional dev fixture); capstone main checklist is Godot **success path** only. | **Adopted** — NEO-142 regression |
**Additional defaults (no kickoff question — settled by backlog / landed code):**
- **Session baseline:** fresh **server restart** before Godot **F5** — resets quest progress, faction standing, reward delivery, inventory, skills, encounter state, and resource nodes; **no Bruno/curl** in main capstone checklist.
- **Economy HUD:** toggle **on** for inventory + skill verification (NEO-132 precedent).
- **Five-quest HUD:** **`QuestProgressLabel`** iterates cached quest-definitions catalog — **`prototype_quest_grid_contract`** appears automatically (no hardcoded four-quest list).
- **Shift+Q after operator chain:** catalog order (`id` ordinal) picks **`prototype_quest_grid_contract`** first among eligible **`not_started`** quests (prerequisite operator chain **`completed`**).
- **Server Slice 3 AC** (auditable deltas; tamper fail-closed): verified via **automated regression** reference in manual QA — **`ReputationOperationsTests`**, content CI gates, startup catalog cross-ref — not re-proven in Godot gameplay (NEO-130 / NEO-135 precedent).
## Scope and out-of-scope
**In scope (from Linear + [E7M3-11](E7M3-pre-production-backlog.md#e7m3-11--playable-faction-reputation--gate-capstone-godot)):**
- **`docs/manual-qa/NEO-143.md`**: numbered **single-session** capstone — extend [NEO-132](../manual-qa/NEO-132.md) four-quest + reward flow through faction standing **15****`prototype_quest_grid_contract`** accept/complete → Rust Collective **+10**; Godot restart idempotency for faction standing + quest statuses.
- **`client/README.md`**: **End-to-end faction reputation loop (NEO-143)** section — integration flow table; cross-links NEO-133NEO-142 and Slice 1/2 capstones.
- **Module alignment** (on story completion): [E7_M3](../decomposition/modules/E7_M3_FactionReputationLedger.md) status **Ready**, [documentation_and_implementation_alignment.md](../decomposition/modules/documentation_and_implementation_alignment.md) E7.M3 row, [module_dependency_register.md](../decomposition/modules/module_dependency_register.md) E7.M3 note, [E7M3-pre-production-backlog.md](E7M3-pre-production-backlog.md) E7M3-11 checkboxes; Epic 7 Slice 3 client capstone complete.
- **Integration fixes only if capstone QA fails** on current `main` wiring (faction standing stale, grid-contract accept/complete gap, reward label missing Rust Collective line, etc.).
**Out of scope (from Linear + backlog):**
- Contract generator (E7.M4); zone travel gates (E4.M1); Bruno-only proof.
- New server HTTP routes, store logic, or gate evaluation changes.
- Live gate-deny step in main capstone checklist (deferred to NEO-142 component QA).
- **`GET /game/world/faction-definitions`** client.
**Client counterpart:** this story **is** the Slice 3 client capstone.
## Acceptance criteria checklist
- [ ] Human completes **`docs/manual-qa/NEO-143.md`** with server + client.
- [x] Epic 7 Slice 3 AC: reputation deltas auditable; gates fail closed on tamper (server tests + NEO-142 gate deny regression).
- [x] Re-read [epic_07 Slice 3 AC](../decomposition/epics/epic_07_quest_faction.md#slice-3---faction-ledger-pre-production).
## Implementation reconciliation (shipped)
- **`docs/manual-qa/NEO-143.md`** — five-quest capstone extending NEO-132 with faction standing **15** → grid contract **+10** → idempotency.
- **`client/README.md`** — **End-to-end faction reputation loop (NEO-143)** section with flow table and cross-links.
- **`docs/manual-qa/NEO-142.md`**, **`docs/manual-qa/NEO-132.md`** — cross-links to NEO-143 Slice 3 capstone.
- **`docs/plans/E7M3-pre-production-backlog.md`**, **`E7_M3_FactionReputationLedger.md`**, **`documentation_and_implementation_alignment.md`**, **`module_dependency_register.md`** — E7M3-11 landed; E7.M3 **Ready**; Epic 7 Slice 3 client capstone complete.
- **Server regression:** 36 faction-related tests pass (`ReputationOperationsTests`, `FactionGateOperationsTests`, `RewardRouterOperationsTests`).
- **No client code changes** — capstone docs-primary; NEO-142 HUD wiring sufficient on `main`.
## Technical approach
### 1. Faction + quest freeze reference (E7M3-01)
| Milestone | Quest / action | Rep / standing outcome | Expected HUD |
|-----------|----------------|------------------------|--------------|
| Operator chain **`completed`** | `completionRewardBundle.reputationGrants` | **Grid Operators +15** | **`FactionStandingLabel`**: `Grid Operators: 15`; **`QuestRewardDeliveryLabel`**: `Grid Operators +15 rep` |
| Grid contract accept + complete | `inventory_has_item` **`survey_drone_kit` ×1** (held from operator chain reward) | **Rust Collective +10** on completion | **`FactionStandingLabel`**: `Rust Collective: 10`; **`QuestRewardDeliveryLabel`**: `Rust Collective +10 rep` |
| Gate (component) | Accept grid contract at standing **0** with operator-chain prereq met | **`faction_gate_blocked`** | Readable deny on **`QuestAcceptFeedbackLabel`** — verified in [NEO-142](../manual-qa/NEO-142.md) GdUnit, not main capstone |
**Final standing snapshot (one session):** Grid Operators **15**, Rust Collective **10**.
**Quest roster (five ids, catalog order):** combat intro → gather intro → **grid contract** → operator chain → refine intro.
### 2. Capstone manual QA script (`docs/manual-qa/NEO-143.md`)
**Preconditions:** Same as [NEO-132](../manual-qa/NEO-132.md) — fresh server restart, Godot gameplay only, **`Economy HUD`** on, NEO-142 faction HUD landed on `main`.
**Flow:** Inherit [NEO-132](../manual-qa/NEO-132.md) steps **119** (four quests + reward/economy verification through operator chain). **Extend** with faction + fifth-quest steps:
1. After operator chain completion — assert **`FactionStandingLabel`** **`Grid Operators: 15`**, **`Rust Collective: 0`**; reward label includes **`Grid Operators +15 rep`**.
2. **`Shift+Q`** accept **`prototype_quest_grid_contract`** — accept succeeds; quest **`completed`** (kit already in bag; **`inventory_has_item`** wiring on accept).
3. Assert **`QuestRewardDeliveryLabel`**: `Grid Contract` + **`Rust Collective +10 rep`**.
4. Assert **`FactionStandingLabel`**: **`Rust Collective: 10`** (Grid Operators still **15**).
5. Assert **`QuestProgressLabel`**: all **five** quests **`completed`**.
6. **Capstone snapshot** — record final faction standing + five-quest statuses.
7. **Idempotency** — Godot restart (server still running): five quests still **`completed`**; faction standing unchanged; reward label **`—`**; duplicate **Shift+Q** deny; standing counts unchanged.
8. **Regression pointers:** [NEO-142](../manual-qa/NEO-142.md) gate deny (GdUnit + optional fixture); [NEO-132](../manual-qa/NEO-132.md) reward idempotency; [NEO-123](../manual-qa/NEO-123.md) onboarding chain.
**Server AC (non-Godot):** optional regression subsection — `dotnet test` filters for **`ReputationOperationsTests`**, **`FactionGateOperationsTests`**, **`RewardRouterOperationsTests`** (rep grants); content **`validate_content.py`** / startup fail-fast for unknown faction cross-refs.
### 3. Client README (`client/README.md`)
Add **End-to-end faction reputation loop (NEO-143)** section after [Faction standing + gate feedback HUD (NEO-142)](../../client/README.md#faction-standing--gate-feedback-hud-neo-142), mirroring NEO-132 capstone section:
- One-line Epic 7 Slice 3 capstone goal.
- Flow table: fresh restart → NEO-132 four-quest path → standing **15** → grid contract accept/complete → Rust Collective **+10** → idempotency.
- Cross-links: NEO-133 content freeze, NEO-136NEO-138 rep apply, NEO-139 standing GET, NEO-140 gate/rep projections, NEO-142 HUD, NEO-132 reward capstone.
- Pointer to **`docs/manual-qa/NEO-143.md`**.
- Note: gate deny proof lives in NEO-142 component QA.
### 4. Module / backlog alignment (on land)
When capstone QA passes:
- [E7M3-pre-production-backlog.md](E7M3-pre-production-backlog.md) — E7M3-11 AC checkboxes.
- [E7_M3_FactionReputationLedger.md](../decomposition/modules/E7_M3_FactionReputationLedger.md) — module status **Ready**; Slice 3 client capstone note.
- [documentation_and_implementation_alignment.md](../decomposition/modules/documentation_and_implementation_alignment.md) — E7.M3 row: NEO-143 landed; Epic 7 Slice 3 client capstone complete.
- [module_dependency_register.md](../decomposition/modules/module_dependency_register.md) — E7.M3 **Ready** when capstone lands.
### 5. Integration fixes (conditional)
Run capstone QA on `main` (NEO-142 merged). If any step fails:
| Symptom | Likely fix surface |
|---------|-------------------|
| **`FactionStandingLabel`** stale after grid contract complete | `quest_hud_controller.gd` — faction GET on completion transition (NEO-142 wiring) |
| Grid contract accept denied at standing **15** | Server gate/standing bug — unlikely if NEO-138 Bruno + tests pass |
| Grid contract stays **`active`** after accept with kit in bag | Server **`inventory_has_item`** wiring on accept — file server issue |
| **`QuestRewardDeliveryLabel`** missing Rust Collective line | `quest_hud_controller.gd` rep formatter — unlikely if NEO-142 GdUnit passes |
| Five-quest progress label missing grid contract row | `quest_definitions_client.gd` cache / boot GET — unlikely |
Document any fix in **Implementation reconciliation** and add regression note to manual QA.
## Files to add
| Path | Purpose |
|------|---------|
| `docs/plans/NEO-143-implementation-plan.md` | This plan. |
| `docs/manual-qa/NEO-143.md` | Single-session five-quest faction capstone extending NEO-132 with standing + grid-contract verification + idempotency. |
## Files to modify
| Path | Rationale |
|------|-----------|
| `client/README.md` | **End-to-end faction reputation loop (NEO-143)** section with flow table and cross-links. |
| `docs/manual-qa/NEO-142.md` | Cross-link to NEO-143 as full-flow Slice 3 capstone superset. |
| `docs/manual-qa/NEO-132.md` | Cross-link to NEO-143 faction extension; note standing/grid-contract verification defers to NEO-143. |
| `docs/plans/E7M3-pre-production-backlog.md` | E7M3-11 AC checkboxes when capstone lands. |
| `docs/decomposition/modules/E7_M3_FactionReputationLedger.md` | Module status **Ready** + capstone landed note on completion. |
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | E7.M3 row: NEO-143 landed; Slice 3 complete. |
| `docs/decomposition/modules/module_dependency_register.md` | E7.M3 **Ready** when capstone lands. |
**Conditional (only if capstone QA fails):** `client/scripts/quest_hud_controller.gd` or `client/scripts/main.gd` — minimal gap-fill; document paths in reconciliation.
## Tests
| Test file | What it covers |
|-----------|----------------|
| *(client unchanged expected)* | NEO-142 GdUnit (`faction_standing_client_test.gd`, `faction_standing_hud_test.gd`) covers standing parse, gate deny copy, rep reward lines. Capstone proof is **`docs/manual-qa/NEO-143.md`** human QA. |
| *(server unchanged)* | Faction spine covered by **`ReputationOperationsTests`**, **`FactionGateOperationsTests`**, **`RewardRouterOperationsTests`**, **`QuestAcceptApiTests`**, content **`validate_content.py`**. Optional regression command in manual QA notes. |
## Open questions / risks
| Question / risk | Agent recommendation | Status |
|-----------------|----------------------|--------|
| **Gate deny in capstone vs NEO-142** | Main NEO-143 checklist is success path; deny proof stays NEO-142 component QA (user adopted). | **adopted** |
| **Grid contract instant complete on accept** | Document expected behavior — **`inventory_has_item`** evaluated on **`TryAccept`**; no extra player action after **Shift+Q**. | **adopted** |
| **E7.M3 Ready timing** | Bump module + register to **Ready** only when NEO-143 capstone QA passes (NEO-141 deferred this). | **adopted** |
| **Five-quest Shift+Q ordering** | After operator chain, only grid contract is eligible **`not_started`** — no catalog-order ambiguity. | **adopted** |
| **Integration gaps on `main`** | Run capstone QA before doc-only land; expect pass with NEO-142 merged. | **adopted** — no client code changes; server tests pass |