diff --git a/server/NeonSprawl.Server/Game/Factions/FactionGateOperations.cs b/server/NeonSprawl.Server/Game/Factions/FactionGateOperations.cs index 5d28dfa..9752814 100644 --- a/server/NeonSprawl.Server/Game/Factions/FactionGateOperations.cs +++ b/server/NeonSprawl.Server/Game/Factions/FactionGateOperations.cs @@ -30,13 +30,16 @@ public static class FactionGateOperations var read = standingStore.TryGetStanding(normalizedPlayerId, factionId); if (!read.Success) { + // --- Telemetry hook site (NEO-141): future E9.M1 catalog event `faction_gate_blocked` --- + // TODO(E9.M1): catalog emit — ops reasonCode (e.g. unknown_faction), playerId, factionId, minStanding, currentStanding (null). + // No ingest or ILogger here (comments-only). return Deny(read.ReasonCode, factionId, rule.MinStanding, null); } if (read.Standing < rule.MinStanding) { // --- Telemetry hook site (NEO-141): future E9.M1 catalog event `faction_gate_blocked` --- - // TODO(E9.M1): catalog emit — playerId, factionId, minStanding, currentStanding, gate context. + // TODO(E9.M1): catalog emit — ops reasonCode (gate_blocked), playerId, factionId, minStanding, currentStanding. // No ingest or ILogger here (comments-only). return Deny( FactionGateEvaluateReasonCodes.GateBlocked, diff --git a/server/NeonSprawl.Server/Game/Factions/ReputationOperations.cs b/server/NeonSprawl.Server/Game/Factions/ReputationOperations.cs index 0343fa5..aaa42ba 100644 --- a/server/NeonSprawl.Server/Game/Factions/ReputationOperations.cs +++ b/server/NeonSprawl.Server/Game/Factions/ReputationOperations.cs @@ -67,6 +67,10 @@ public static class ReputationOperations if (auditStore.TryAppend(row)) { // --- Telemetry hook site (NEO-141): future E9.M1 catalog event `reputation_delta` --- + // TODO(E9.M1): catalog emit — once per successful auditable apply (standing + audit committed). + // Not on deny paths, audit append failure + compensating revert, or caller rollback paths. + // Planned payload fields: playerId, factionId, appliedDelta, newStanding, sourceKind, sourceId, deltaId, appliedAt. + // No ingest or ILogger here (comments-only). return new ReputationApplyOutcome( true, null, diff --git a/server/NeonSprawl.Server/Game/Rewards/RewardRouterOperations.cs b/server/NeonSprawl.Server/Game/Rewards/RewardRouterOperations.cs index e77a339..cb99ad3 100644 --- a/server/NeonSprawl.Server/Game/Rewards/RewardRouterOperations.cs +++ b/server/NeonSprawl.Server/Game/Rewards/RewardRouterOperations.cs @@ -220,9 +220,6 @@ public static class RewardRouterOperations var appliedAmount = repOutcome.NewStanding - repOutcome.PreviousStanding; appliedReputation.Add(new RewardReputationGrantApplied(grant.FactionId, appliedAmount)); - - // --- Telemetry hook site (NEO-141): future E9.M1 catalog event `reputation_delta` --- - // TODO(E9.M1): catalog emit — once per applied rep row on TryRecord success only (not rollback paths). } var deliveredAt = timeProvider.GetUtcNow(); diff --git a/server/README.md b/server/README.md index 3d64a37..efd1920 100644 --- a/server/README.md +++ b/server/README.md @@ -134,6 +134,17 @@ Quest accept evaluates **`FactionGateRule`** rows through **`FactionGateOperatio Plan: [NEO-137 implementation plan](../../docs/plans/NEO-137-implementation-plan.md). +### Faction telemetry hooks (NEO-141) + +Comment-only hook sites for future E9.M1 catalog events ([Epic 7 Slice 3](../../docs/decomposition/modules/E7_M3_FactionReputationLedger.md#related-implementation-slices)). **`TODO(E9.M1)`** — no production ingest. Quest accept and reward router delegate to these ops layers only (no duplicate API-layer or router-layer hooks for these names). Quest rep grants emit **`reputation_delta`** via **`ReputationOperations`** — see [Reward telemetry hooks (NEO-130)](#reward-telemetry-hooks-neo-130) cross-link. + +| Event | Anchor | When | +|-------|--------|------| +| **`reputation_delta`** | **`ReputationOperations.TryApplyDelta`** | After successful audit append (auditable apply committed). | +| **`faction_gate_blocked`** | **`FactionGateOperations.TryEvaluate`** | On any rule deny (threshold or standing read failure). | + +Manual QA: [`docs/manual-qa/NEO-141.md`](../../docs/manual-qa/NEO-141.md); plan: [NEO-141 implementation plan](../../docs/plans/NEO-141-implementation-plan.md). + **Dev quest fixture (NEO-137, Bruno/manual QA):** When `Game:EnableQuestFixtureApi` is **true** (default in **Development** via `appsettings.Development.json`, **Testing**, or CI Bruno step) or the host environment is **Development** / **Testing**, **`POST /game/players/{id}/__dev/quest-fixture`** accepts `schemaVersion` **1** with optional (resets run before **`completedQuestIds`** when combined): - **`resetQuestIds`** — deletes each quest's progress row for the player and clears matching **`IRewardDeliveryStore`** + quest-completion **`IReputationDeltaStore`** audit rows (idempotent when absent). - **`resetFactionIds`** — clears each faction's standing row for the player (missing row reads as **0**; idempotent when absent). @@ -408,9 +419,10 @@ Comment-only hook sites in **`RewardRouterOperations.TryDeliverQuestCompletion`* | Event | Anchor | When | |-------|--------|------| | **`reward_delivery`** | **`TryDeliverQuestCompletion`** | After **`TryRecord`** returns **`true`** on first-time delivery (not idempotent **`TryGet`** replay or race-loser paths). | -| **`reputation_delta`** | **`TryDeliverQuestCompletion`** (stub) | After each successful reputation row apply (NEO-138); consolidated in NEO-141. | | **`unlock_granted`** | **`TryDeliverQuestCompletion`** (stub) | Future **`UnlockGrant`** apply from bundle rows — prototype bundles have item + skill XP only; no runtime unlock apply in Slice 2. | +**`reputation_delta`** from quest bundle rep rows emits via **`ReputationOperations.TryApplyDelta`** ([NEO-141](#faction-telemetry-hooks-neo-141)) — not duplicated in the router. + **`perk_unlock`** side effects from skill XP grants use **`PerkUnlockEngine`** ([NEO-49](#perk-unlock-engine-and-telemetry-hooks-neo-47-neo-49)) — distinct from content **`UnlockGrant`** rows. Manual QA: [`docs/manual-qa/NEO-130.md`](../../docs/manual-qa/NEO-130.md); plan: [NEO-130 implementation plan](../../docs/plans/NEO-130-implementation-plan.md). ## Encounter definitions (NEO-103)