57 lines
4.2 KiB
Markdown
57 lines
4.2 KiB
Markdown
# Code review — NEO-148 contract completion reward router
|
||
|
||
**Date:** 2026-06-27
|
||
**Scope:** Branch `NEO-148-e7m4-05-reward-router-contract-completion` — commits `b20d7d0`..`76ce194` vs `origin/main`
|
||
**Base:** `origin/main`
|
||
|
||
## Verdict
|
||
|
||
**Approve with nits**
|
||
|
||
## Summary
|
||
|
||
NEO-148 extends the E7.M2 reward delivery stack for contract completion: `RewardDeliverySourceKinds`, source-kind-aware `IRewardDeliveryStore` / `InMemoryRewardDeliveryStore` keys, `RewardDeliveryEvent.SourceKind` + `ContractInstanceId`, `ReputationDeltaSourceKinds.ContractCompletion`, and `RewardRouterOperations.TryDeliverContractCompletion` with a shared private `TryDeliverBundle` core extracted from the quest path. Grant order, compensating rollback, and `TryRecord` race handling mirror NEO-127/NEO-138. Five contract router tests plus one store isolation test cover happy path, idempotent replay, faction deny, inventory deny, and invalid instance id. Implementation plan reconciliation, `server/README.md`, and E7.M4 module anchor are updated. Server-only slice; no client work expected. Full suite: **897** tests passed locally.
|
||
|
||
## Documentation checked
|
||
|
||
| Path | Result |
|
||
|------|--------|
|
||
| `docs/plans/NEO-148-implementation-plan.md` | **Matches** — AC checklist marked complete; shipped reconciliation aligns with diff |
|
||
| `docs/plans/E7M4-pre-production-backlog.md` (E7M4-05) | **Matches** — router + store extension, orchestrator/outcome store out of scope |
|
||
| `docs/decomposition/modules/E7_M4_ContractMissionGenerator.md` | **Matches** — NEO-148 router anchor added |
|
||
| `docs/decomposition/modules/E7_M2_RewardAndUnlockRouter.md` | **Matches** — NEO-148 contract reuse anchor added |
|
||
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | **Matches** — E7.M4 row includes NEO-148 |
|
||
| `server/README.md` | **Matches** — delivery store fields, `TryGet` overloads, contract router + reason codes |
|
||
| Client counterpart (NEO-153) | **N/A** — explicitly out of scope; no prototype-complete claim |
|
||
|
||
Register/tracking table updated for E7.M4 (NEO-148).
|
||
|
||
## Blocking issues
|
||
|
||
None.
|
||
|
||
## Suggestions
|
||
|
||
1. ~~**Update implementation alignment register** — Add **E7M4-05 / NEO-148 landed** to the E7.M4 row in `docs/decomposition/modules/documentation_and_implementation_alignment.md` (router + source-kind store extension), consistent with NEO-144–NEO-147 entries.~~ **Done.**
|
||
|
||
2. ~~**Optional E7.M2 module cross-ref** — A one-line anchor on `E7_M2_RewardAndUnlockRouter.md` noting contract completion reuse via `TryDeliverContractCompletion` (NEO-148) would help readers who land on E7.M2 first.~~ **Done.**
|
||
|
||
3. ~~**Store key format note for future Postgres** — Quest store keys changed from `{playerId}\0{questId}` to `{playerId}\0{sourceKind}\0{sourceId}`. Harmless for in-memory prototype (process restart clears state). When Postgres delivery persistence lands, document or migrate old rows explicitly so idempotent replay cannot double-grant after upgrade.~~ **Done.** (`server/README.md` + plan open questions)
|
||
|
||
4. ~~**Contract fixture reset (follow-on)** — `QuestFixtureOperations` still clears quest rows via `TryClear(playerId, questId)` only. When E7M4-06+ adds Bruno contract loops, contract delivery rows will need a parallel reset path — track on NEO-149 or fixture story, not blocking here.~~ **Done.** (deferred to NEO-149 in plan open questions)
|
||
|
||
## Nits
|
||
|
||
- Nit: Plan test table lists “unknown item / full bag deny”; `TryDeliverContractCompletion_ShouldDenyInventoryFull_WithoutStoreRecord` covers full bag only. Shared `TryDeliverBundle` item path is quest-tested for inventory deny; an explicit `invalid_item` contract test would lock naming to AC but is low value given shared core.
|
||
- Nit: NEO-130 telemetry hook comments in `TryDeliverBundle` still reference `questId` in payload notes; fine until contract telemetry wiring — consider generalizing when E9.M1 touches contract completion.
|
||
- Nit: Plan mentions `MakeContractStoreKey`; implementation uses `MakeStoreKey(playerId, sourceKind, sourceId)` instead — equivalent, no action needed.
|
||
|
||
## Verification
|
||
|
||
```bash
|
||
dotnet test NeonSprawl.sln
|
||
dotnet test NeonSprawl.sln --filter "FullyQualifiedName~RewardRouterOperationsTests|FullyQualifiedName~InMemoryRewardDeliveryStoreTests"
|
||
```
|
||
|
||
Manual Godot QA: none (server engine per plan).
|