neon-sprawl/docs/plans/NEO-152-implementation-plan.md

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.mdE7M4-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 DoneContractGeneratorOperations.TryIssue; NEO-149 DoneContractCompletionOperations + partial contract_complete hook comment; NEO-150 DoneContractEconomyValidation 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 NoneContractIssueApi / 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.

Implementation reconciliation (shipped)

  • ContractGeneratorOperations.TryIssue: contract_issued hook after successful TryCreateActive; reward_anomaly stub on economy validation deny.
  • ContractCompletionOperations.TryCompleteOnEncounterClear: contract_complete hook extended to NEO-121 block style (NEO-149 partial comment completed).
  • RewardRouterOperations.TryDeliverBundle / CompensatingRevertAll: reward_anomaly stub for partial-apply rollback (single block on private CompensatingRevertAll — covers all four TryDeliverBundle call sites; DRY vs per-call-site duplicates); NEO-130 reward_delivery comment updated for contract SourceKind.
  • Docs: server/README.md Contract telemetry hooks (NEO-152) subsection; E7.M4 module + alignment register + E7M4 backlog updated.
  • Tests: no new tests — comments-only; dotnet test regression 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_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: 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 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):

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