# Code review — NEO-140 (E7M3-08) **Date:** 2026-06-17 **Scope:** Branch `NEO-140-e7m3-quest-http-rep-gate-projections` — commits `367f894` … `1dedc5f` vs `main` **Base:** `main` **Follow-up:** Suggestions and actionable nits below are **done** (strikethrough + **Done.** / **Addressed.** / **Deferred**). ## Verdict **Approve with nits** ## Summary This branch lands **E7M3-08**: additive HTTP projections so clients can read faction gate rules on world quest definitions and reputation grant lines on quest-progress `completionRewardSummary`, without local standing math. Implementation mirrors **NEO-129** / **NEO-115** patterns — optional `factionGateRules` on world GET (omitted when empty), required `reputationGrants` array when a completion summary exists (sourced from `RewardDeliveryEvent.GrantedReputation` at commit time). Shared mappers mean **QuestAcceptApi** embedded rows pick up rep lines automatically. Integration tests extend existing quest API suites; Bruno adds operator-chain completion assertions and extends quest-definitions / gather-intro bru. **821** server tests pass locally. Server-only story; Godot HUD remains **NEO-142**. Low merge risk — read-only projections, schema version stays **1**. ## Documentation checked | Path | Result | |------|--------| | `docs/plans/NEO-140-implementation-plan.md` | **Matches** — kickoff decisions, AC checklist, reconciliation section, and file list align with the shipped diff. | | `docs/plans/E7M3-pre-production-backlog.md` | **Matches** — E7M3-08 server scope; client counterpart NEO-142 explicitly deferred. | | `docs/decomposition/modules/E7_M3_FactionReputationLedger.md` | **Matches** — Status line notes E7M3-08 landed (NEO-140). | | `docs/decomposition/modules/documentation_and_implementation_alignment.md` | **Matches** — E7.M3 row updated with quest HTTP projection links. | | `docs/decomposition/modules/module_dependency_register.md` | **N/A change** — no register row edit required beyond module doc / alignment table. | | `docs/manual-qa/NEO-140.md` | **Removed post-merge** — server-only story; manual QA reserved for client-facing work (story-end decision). | | `server/README.md` | **Matches** — Quest-definitions and quest-progress sections document new fields with NEO-140 cross-links. | ## Blocking issues None. ## Suggestions 1. ~~**Grid-contract completion rep (+10 Rust Collective) still untested end-to-end.** Plan §prototype targets and open-questions table adopt deferring grid-contract `reputationGrants` Bruno until a hand-in flow helper exists; operator-chain coverage is the primary AC and is solid. When grid-contract completion lands in Bruno or integration helpers, add an assertion for `[{ factionId: prototype_faction_rust_collective, amount: 10 }]` so the third freeze row in the plan table is locked like operator-chain.~~ **Done.** `QuestProgressApiTests.GetQuestProgress_ShouldReturnGridContractReputationGrant_WhenAcceptedAfterOperatorChain`; `Accept grid contract after operator chain.bru` asserts embedded `completionRewardSummary.reputationGrants`. 2. ~~**Optional wire-level omission test for `factionGateRules`.** `QuestDefinitionsWorldApiTests` asserts deserialized `FactionGateRules` is null on non-gated quests; Bruno asserts JSON `undefined` for gather intro and operator chain. A small integration test that reads raw JSON (or `JsonDocument`) and confirms the property key is absent on non-gated rows would close the loop without Bruno — low priority given Bruno coverage.~~ **Done.** `GetQuestDefinitions_ShouldOmitFactionGateRulesJsonProperty_WhenQuestHasNoGates` uses `JsonDocument` to assert key absence on gather intro and operator chain. ## Nits - ~~Nit: `docs/manual-qa/NEO-140.md` links client follow-up as `[NEO-142](NEO-142.md)` — relative path may not resolve from `docs/manual-qa/`; prefer `NEO-142.md` (same folder) or `../manual-qa/NEO-142.md` from other docs.~~ **Done.** Linear issue link until `NEO-142.md` exists. - ~~Nit: `Get quest progress after operator chain complete.bru` pre-request performs a quest-progress GET before the named GET (mirrors gather-intro bru idempotency pattern) — fine; seq **12** places it after gather-intro complete (**8**); confirm collection order if Bruno runs fail mid-spine.~~ **Addressed.** Pre-request helpers reset quest/inventory state per bru; seq order does not share mutable spine between these requests. - Nit: Client fixture JSON in `client/test/quest_progress_client_test.gd` (`_completed_with_reward_summary_json`) omits `reputationGrants` — acceptable until **NEO-142**; Godot client ignores unknown keys today. **Deferred** to NEO-142. ## Verification ```bash cd /Users/don/neon-sprawl && dotnet test NeonSprawl.sln ``` Manual (Bruno — no separate manual QA doc for this server-only story): - `cd server/NeonSprawl.Server && dotnet run` - Bruno: `quest-definitions/Get quest definitions` - Bruno: `quest-progress/Get quest progress after gather intro complete` - Bruno: `quest-progress/Get quest progress after operator chain complete` - Regression: `faction-standing/Get faction standing after operator chain`, `quest-progress/Accept grid contract after operator chain`