13 KiB
NEO-152 — E7M4-09: Contract telemetry hook sites (comment-only)
Linear: NEO-152
Branch: NEO-152-e7m4-09-contract-telemetry-hook-sites-comment-only
Backlog: E7M4-pre-production-backlog.md — E7M4-09
Module: E7_M4_ContractMissionGenerator.md
Pattern: NEO-121 / NEO-130 / NEO-141 — comment-only engine anchors + TODO(E9.M1); engine-only (no duplicate API-layer hooks)
Precursors: NEO-147 Done — ContractGeneratorOperations.TryIssue; NEO-149 Done — ContractCompletionOperations + partial contract_complete hook comment; NEO-150 Done — ContractEconomyValidation at issue (deferred reward_anomaly to this story); NEO-151 Done — contract HTTP (on main)
Blocks: NEO-153 (Godot contract HUD — telemetry vocabulary complete for server path)
Client counterpart: none (infrastructure-only per E7M4-09)
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, 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 |
Private CompensatingRevertAll in RewardRouterOperations (single method-level stub — all TryDeliverBundle rollback call sites); E9.M1 filters by SourceKind |
Shared router core (NEO-148); DRY vs per-call-site duplicates (NEO-121 central Deny precedent) |
| 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_issuedhook inContractGeneratorOperations.TryIssue(successful issue commit). - Complete comment-only
contract_completehook inContractCompletionOperations(verify/extend NEO-149 partial block). - Comment-only
reward_anomalystubs 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, orContractEconomyValidation(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.
Implementation reconciliation (shipped)
ContractGeneratorOperations.TryIssue:contract_issuedhook after successfulTryCreateActive;reward_anomalystub on economy validation deny.ContractCompletionOperations.TryCompleteOnEncounterClear:contract_completehook extended to NEO-121 block style (NEO-149 partial comment completed).RewardRouterOperations.TryDeliverBundle/CompensatingRevertAll:reward_anomalystub for partial-apply rollback (single block on privateCompensatingRevertAll— covers all fourTryDeliverBundlecall sites; DRY vs per-call-site duplicates); NEO-130reward_deliverycomment updated for contractSourceKind.- Docs:
server/README.mdContract telemetry hooks (NEO-152) subsection; E7.M4 module + alignment register + E7M4 backlog updated. - Tests: no new tests — comments-only;
dotnet testregression green.
Technical approach
1. Shipped state (post-NEO-152)
| Location | Shipped state |
|---|---|
ContractGeneratorOperations.TryIssue |
contract_issued + economy-deny reward_anomaly comment blocks |
ContractCompletionOperations.TryCompleteOnEncounterClear |
Full NEO-121-style contract_complete hook after deliver-then-mark |
RewardRouterOperations.CompensatingRevertAll |
Single reward_anomaly stub (private method — all rollback call sites) |
server/README.md |
Contract telemetry hooks (NEO-152) subsection + NEO-130 cross-link |
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_anomalywith opsreasonCode(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: private CompensatingRevertAll in RewardRouterOperations (invoked from four TryDeliverBundle rollback paths after partial grant apply — skill XP / reputation / TryRecord race loser). Single method-level stub covers all call sites (NEO-121 central Deny precedent).
- Stub comment names
reward_anomaly; note E9.M1 filters byDeliveryTarget.SourceKind— contract completions usecontract_completion. - Not on: early denies before any grant apply (e.g.
inventory_fullsimulation fail), idempotentTryGetreplay, or successfulTryRecord.
5. README consolidation
Add ### Contract telemetry hooks (NEO-152) after 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.CompensatingRevertAll |
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)
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 on private CompensatingRevertAll (covers all rollback call sites 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 |