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 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 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.

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):

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)

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