181 lines
13 KiB
Markdown
181 lines
13 KiB
Markdown
# NEO-152 — E7M4-09: Contract telemetry hook sites (comment-only)
|
|
|
|
**Linear:** [NEO-152](https://linear.app/neon-sprawl/issue/NEO-152)
|
|
**Branch:** `NEO-152-e7m4-09-contract-telemetry-hook-sites-comment-only`
|
|
**Backlog:** [E7M4-pre-production-backlog.md](E7M4-pre-production-backlog.md) — **E7M4-09**
|
|
**Module:** [E7_M4_ContractMissionGenerator.md](../decomposition/modules/E7_M4_ContractMissionGenerator.md)
|
|
**Pattern:** [NEO-121](NEO-121-implementation-plan.md) / [NEO-130](NEO-130-implementation-plan.md) / [NEO-141](NEO-141-implementation-plan.md) — comment-only engine anchors + `TODO(E9.M1)`; engine-only (no duplicate API-layer hooks)
|
|
**Precursors:** [NEO-147](https://linear.app/neon-sprawl/issue/NEO-147) **`Done`** — `ContractGeneratorOperations.TryIssue`; [NEO-149](https://linear.app/neon-sprawl/issue/NEO-149) **`Done`** — `ContractCompletionOperations` + partial `contract_complete` hook comment; [NEO-150](https://linear.app/neon-sprawl/issue/NEO-150) **`Done`** — `ContractEconomyValidation` at issue (deferred `reward_anomaly` to this story); [NEO-151](https://linear.app/neon-sprawl/issue/NEO-151) **`Done`** — contract HTTP (**on `main`**)
|
|
**Blocks:** [NEO-153](https://linear.app/neon-sprawl/issue/NEO-153) (Godot contract HUD — telemetry vocabulary complete for server path)
|
|
**Client counterpart:** none (infrastructure-only per [E7M4-09](E7M4-pre-production-backlog.md#e7m4-09--contract-telemetry-hook-sites-comment-only))
|
|
|
|
## Goal
|
|
|
|
Anchor future E9.M1 catalog events **`contract_issued`**, **`contract_complete`**, and **`reward_anomaly`** on the server-authoritative contract path without ingest or behavior change.
|
|
|
|
## Kickoff clarifications
|
|
|
|
**No clarifications needed.** Linear AC, [E7M4-09 backlog scope](E7M4-pre-production-backlog.md#e7m4-09--contract-telemetry-hook-sites-comment-only), landed contract ops/HTTP (NEO-147/149/150/151), and NEO-121/130/141 comment-only telemetry precedents settle scope.
|
|
|
|
| Topic | Decision | Evidence |
|
|
|-------|----------|----------|
|
|
| **Runtime behavior** | Comments-only + `TODO(E9.M1)` — no ingest, no `ILogger` | NEO-121/130/141 precedent |
|
|
| **`contract_issued` anchor** | **`ContractGeneratorOperations.TryIssue`** — after successful **`TryCreateActive`** (first-time active row persisted) | Backlog E7M4-09; NEO-147 deferred hook |
|
|
| **`contract_complete` anchor** | **`ContractCompletionOperations.TryCompleteOnEncounterClear`** — after successful **`TryMarkComplete`** + outcome append (delivery already succeeded) | Backlog E7M4-09; partial comment landed in NEO-149 — complete to NEO-121 block style |
|
|
| **`reward_anomaly` — economy deny** | **`ContractGeneratorOperations.TryIssue`** when **`ContractEconomyValidation.TryValidateTemplate`** returns false (`economy_cap_exceeded`, `invalid_reward_bundle`) | Backlog E7M4-09; NEO-150 deferred |
|
|
| **`reward_anomaly` — delivery rollback** | **`RewardRouterOperations.TryDeliverBundle`** at **`CompensatingRevertAll`** invocations (partial grant apply then revert); E9.M1 filters by **`SourceKind`** (`contract_completion` vs `quest_completion`) | Shared router core (NEO-148); rollback site is private bundle path, not HTTP |
|
|
| **API-layer hooks** | **None** — **`ContractIssueApi`** / **`ContractListApi`** delegate to ops only | NEO-121/141 engine-only precedent |
|
|
| **`reward_delivery` for contracts** | **No duplicate** — reuse NEO-130 **`reward_delivery`** hook at **`TryRecord`** success inside shared **`TryDeliverBundle`**; README cross-links contract **`SourceKind`** | NEO-130 already documents payload; contract uses same hook site |
|
|
| **Manual QA doc** | **None** — comment-only server story; `dotnet test` covers regression | NEO-141 precedent; no user-visible behavior |
|
|
|
|
## Scope and out-of-scope
|
|
|
|
**In scope (from Linear + backlog):**
|
|
|
|
- Comment-only **`contract_issued`** hook in **`ContractGeneratorOperations.TryIssue`** (successful issue commit).
|
|
- Complete comment-only **`contract_complete`** hook in **`ContractCompletionOperations`** (verify/extend NEO-149 partial block).
|
|
- Comment-only **`reward_anomaly`** stubs at economy validation deny (issue path) and delivery rollback paths (shared router bundle).
|
|
- **`server/README.md`** — dedicated **Contract telemetry hooks (NEO-152)** subsection with event table.
|
|
- **`E7_M4_ContractMissionGenerator.md`**, alignment register, E7M4 backlog updates when implementation completes.
|
|
|
|
**Out of scope:**
|
|
|
|
- E9.M1 ingest pipeline; runtime telemetry emit.
|
|
- `ILogger` / metrics / dev-only log lines.
|
|
- Duplicate hooks in **`ContractIssueApi`**, **`ContractListApi`**, **`EncounterCompletionOperations`**, or **`ContractEconomyValidation`** (orchestrator/router anchor only).
|
|
- Godot client (**NEO-153** / E7M4-10).
|
|
|
|
**Client counterpart:** none.
|
|
|
|
## Acceptance criteria checklist
|
|
|
|
- [ ] Hook sites documented in README; no behavior change beyond comments.
|
|
|
|
## Technical approach
|
|
|
|
### 1. Current state (precursor partial work)
|
|
|
|
| Location | State today | NEO-152 action |
|
|
|----------|-------------|----------------|
|
|
| **`ContractGeneratorOperations.TryIssue`** | Class doc references NEO-152; **no** hook at success or economy deny | **Add** `contract_issued` + `reward_anomaly` comment blocks |
|
|
| **`ContractCompletionOperations.TryCompleteOnEncounterClear`** | Partial **`contract_complete`** hook after **`TryMarkComplete`** (NEO-149) | **Verify/extend** to full NEO-121-style block (`TODO(E9.M1)` + payload fields) |
|
|
| **`RewardRouterOperations.TryDeliverBundle`** | NEO-130 **`reward_delivery`** at **`TryRecord`** success; **`CompensatingRevertAll`** with no anomaly stub | **Add** `reward_anomaly` stub comment at rollback invocations (shared quest + contract path) |
|
|
| **`server/README.md`** | Contract ops documented; **no** telemetry subsection | **Add** NEO-152 hook table; cross-link NEO-130 reward delivery for contract completions |
|
|
|
|
### 2. Hook site A — `contract_issued`
|
|
|
|
**Anchor:** **`ContractGeneratorOperations.TryIssue`**, immediately after **`instanceStore.TryCreateActive`** returns **`true`**.
|
|
|
|
- Comment block names future E9.M1 event **`contract_issued`**.
|
|
- **`TODO(E9.M1): catalog emit`** — once per first-time active instance persist.
|
|
- **Not** on: deny paths, idempotent create failure, or economy validation failure.
|
|
- Planned payload fields: `playerId`, `contractInstanceId`, `templateId`, `seedBucket`, `encounterTemplateId`, `issuedAt`.
|
|
|
|
### 3. Hook site B — `contract_complete`
|
|
|
|
**Anchor:** **`ContractCompletionOperations.TryCompleteOnEncounterClear`**, after successful **`TryMarkComplete`** + outcome append (delivery already committed via router).
|
|
|
|
- Extend existing NEO-149 partial comment to full block style.
|
|
- **`TODO(E9.M1): catalog emit`** — first-time completion only; idempotent replay / already-completed short-circuit excluded.
|
|
- **Not** on: no-op paths (`no_active_contract`, `encounter_mismatch`), delivery deny (contract stays active), or mark-complete failure after successful delivery.
|
|
- Planned payload fields: `playerId`, `contractInstanceId`, `templateId`, `encounterId`, `completedAt`, idempotencyKey (`{playerId}:contract_complete:{contractInstanceId}`).
|
|
|
|
### 4. Hook site C — `reward_anomaly` (stub)
|
|
|
|
**C1 — economy validation deny**
|
|
|
|
**Anchor:** **`ContractGeneratorOperations.TryIssue`**, when **`ContractEconomyValidation.TryValidateTemplate`** returns **`false`** (before persist).
|
|
|
|
- Stub comment names **`reward_anomaly`** with ops **`reasonCode`** (`economy_cap_exceeded`, `invalid_reward_bundle`).
|
|
- Defense-in-depth path — startup catalog load should prevent most cases; issue-time lint is explicit Slice 4 AC.
|
|
|
|
**C2 — delivery rollback**
|
|
|
|
**Anchor:** **`RewardRouterOperations.TryDeliverBundle`**, at **`CompensatingRevertAll`** call sites after partial grant apply (skill XP / reputation / TryRecord race loser).
|
|
|
|
- Stub comment names **`reward_anomaly`**; note E9.M1 filters by **`DeliveryTarget.SourceKind`** — contract completions use **`contract_completion`**.
|
|
- **Not** on: early denies before any grant apply (e.g. **`inventory_full`** simulation fail), idempotent **`TryGet`** replay, or successful **`TryRecord`**.
|
|
|
|
### 5. README consolidation
|
|
|
|
Add **`### Contract telemetry hooks (NEO-152)`** after [Contract completion operations (NEO-149)](../../server/README.md#contract-completion-operations-neo-149):
|
|
|
|
| Event | Anchor | When |
|
|
|-------|--------|------|
|
|
| **`contract_issued`** | **`ContractGeneratorOperations.TryIssue`** | After successful **`TryCreateActive`** (active instance persisted). |
|
|
| **`contract_complete`** | **`ContractCompletionOperations.TryCompleteOnEncounterClear`** | After successful deliver-then-mark (instance marked complete + outcome appended). |
|
|
| **`reward_anomaly`** | **`ContractGeneratorOperations.TryIssue`** (economy deny); **`RewardRouterOperations.TryDeliverBundle`** (partial-apply rollback) | Economy cap / bundle cross-ref deny at issue; compensating revert after partial delivery apply. |
|
|
| **`reward_delivery`** *(cross-link)* | **`RewardRouterOperations.TryDeliverBundle`** | NEO-130 — first-time **`TryRecord`** success; contract path uses **`SourceKind = contract_completion`**. |
|
|
|
|
### 6. Relationship to adjacent hooks
|
|
|
|
| Event | Layer | Story |
|
|
|-------|-------|-------|
|
|
| **`contract_issued`** | **`ContractGeneratorOperations`** | NEO-152 |
|
|
| **`contract_complete`** | **`ContractCompletionOperations`** | NEO-152 |
|
|
| **`reward_anomaly`** | Generator (economy deny) + router (rollback) | NEO-152 |
|
|
| **`reward_delivery`** | **`RewardRouterOperations`** | NEO-130 (shared; contract via NEO-148) |
|
|
| **`reputation_delta`** | **`ReputationOperations`** | NEO-141 (contract rep grants funnel here) |
|
|
| **`encounter_complete`** | Encounter ops | NEO-109 (encounter clear triggers contract completion wiring) |
|
|
|
|
### 7. Flow (comment anchors only)
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant Issue as ContractGeneratorOperations.TryIssue
|
|
participant Econ as ContractEconomyValidation
|
|
participant Store as IContractInstanceStore
|
|
participant Enc as EncounterCompletionOperations
|
|
participant Complete as ContractCompletionOperations
|
|
participant Router as RewardRouterOperations
|
|
|
|
Issue->>Econ: TryValidateTemplate
|
|
alt economy deny
|
|
Econ-->>Issue: fail (hook: reward_anomaly)
|
|
else pass
|
|
Issue->>Store: TryCreateActive
|
|
Store-->>Issue: success (hook: contract_issued)
|
|
end
|
|
|
|
Enc->>Complete: TryCompleteOnEncounterClear
|
|
Complete->>Router: TryDeliverContractCompletion
|
|
alt partial apply then revert
|
|
Router-->>Complete: deny (hook: reward_anomaly in bundle)
|
|
else delivery ok
|
|
Router-->>Complete: success (hook: reward_delivery NEO-130)
|
|
Complete->>Store: TryMarkComplete
|
|
Complete-->>Complete: append outcome (hook: contract_complete)
|
|
end
|
|
```
|
|
|
|
## Files to add
|
|
|
|
None — comment-only changes to existing ops files and docs; no new source or test files.
|
|
|
|
## Files to modify
|
|
|
|
| Path | Rationale |
|
|
|------|-----------|
|
|
| `server/NeonSprawl.Server/Game/Contracts/ContractGeneratorOperations.cs` | Add `contract_issued` hook after successful create; `reward_anomaly` stub on economy validation deny |
|
|
| `server/NeonSprawl.Server/Game/Contracts/ContractCompletionOperations.cs` | Verify/extend `contract_complete` hook block to NEO-121 style |
|
|
| `server/NeonSprawl.Server/Game/Rewards/RewardRouterOperations.cs` | Add `reward_anomaly` stub comments at `CompensatingRevertAll` invocations in `TryDeliverBundle` |
|
|
| `server/README.md` | Add **Contract telemetry hooks (NEO-152)** subsection with event table + NEO-130 cross-link |
|
|
| `docs/decomposition/modules/E7_M4_ContractMissionGenerator.md` | Status line: E7M4-09 NEO-152 telemetry hooks landed |
|
|
| `docs/decomposition/modules/documentation_and_implementation_alignment.md` | E7.M4 row: E7M4-09 hook sites |
|
|
| `docs/plans/E7M4-pre-production-backlog.md` | E7M4-09 checkboxes + landed note |
|
|
|
|
## Tests
|
|
|
|
| File | Coverage |
|
|
|------|----------|
|
|
| *(none added or changed)* | Comments-only change; no behavior or wire contract change. Regression: `dotnet test NeonSprawl.sln` (existing contract generator/completion/API tests unchanged). |
|
|
|
|
## Open questions / risks
|
|
|
|
| Question / risk | Agent recommendation | Status |
|
|
|-----------------|----------------------|--------|
|
|
| `reward_anomaly` in shared `TryDeliverBundle` affects quest path docs | **Single stub** at rollback sites with **`SourceKind`** filter note — avoid duplicate contract-only copies | `adopted` |
|
|
| Partial `contract_complete` comment from NEO-149 | **Extend in place** — do not relocate hook to router or encounter layer | `adopted` |
|
|
| Economy deny hook vs validator module | **Anchor at orchestrator** (`TryIssue`) when validation fails — not inside `ContractEconomyValidation` | `adopted` |
|
|
| Duplicate `reward_delivery` for contracts | **No** — NEO-130 hook at `TryRecord` already covers contract path via `SourceKind` | `adopted` |
|