# E1.M4 — Prototype story backlog (AbilityInputScaffold)

Working backlog for **Epic 1 — Slice 3** ([interaction, targeting, ability input](../decomposition/epics/epic_01_core_player_runtime.md#epic-1-slice-3)). Decomposition and contracts: [E1.M4 — AbilityInputScaffold](../decomposition/modules/E1_M4_AbilityInputScaffold.md).

**Labels (Linear):** every issue **`E1.M4`**; add **`Epic 1`** / Slice 3 as your team prefers.

**Precursor (do not re-scope):** selection and targeting authority live in **E1.M3** ([E1.M3 module](../decomposition/modules/E1_M3_InteractionAndTargetingLayer.md)); combat accept/deny remains authoritative in **E5.M1**.

**Linear issues (created):** attach `docs/plans/NEO-*-implementation-plan.md` on the same branch as implementation work.

| Slug | Linear |
|------|--------|
| E1M4-01 | [NEO-29](https://linear.app/neon-sprawl/issue/NEO-29/e1m4-01-hotbarloadout-v1-contract-baseline-persistence-path) |
| E1M4-02 | [NEO-31](https://linear.app/neon-sprawl/issue/NEO-31/e1m4-02-input-to-abilitycastrequest-path) |
| E1M4-03 | [NEO-28](https://linear.app/neon-sprawl/issue/NEO-28/e1m4-03-combat-acceptdeny-integration-reason-code-ux) |
| E1M4-04 | [NEO-32](https://linear.app/neon-sprawl/issue/NEO-32/e1m4-04-cooldownsnapshot-sync-slot-presentation) |
| E1M4-05 | [NEO-30](https://linear.app/neon-sprawl/issue/NEO-30/e1m4-05-slice-3-telemetry-hooks-for-cast-funnel) |

**Dependency graph in Linear:** E1M4-02 blocked by E1M4-01. E1M4-03 blocked by E1M4-02 and minimum E5.M1 accept/deny response contract. E1M4-04 blocked by E1M4-03. E1M4-05 blocked by E1M4-02 and E1M4-03.

---

## Story order (recommended)

| Order | Slug | Depends on |
|-------|------|------------|
| 1 | **E1M4-01** | E1.M3 target lock shape, existing prototype auth/session APIs |
| 2 | **E1M4-02** | E1M4-01, E1.M3 target selection flow |
| 3 | **E1M4-03** | E1M4-02, E5.M1 accept/deny contract |
| 4 | **E1M4-04** | E1M4-03 |
| 5 | **E1M4-05** | E1M4-02, E1M4-03 |

---

### E1M4-01 — `HotbarLoadout` v1 contract + baseline persistence path

**Goal:** Define a versioned `HotbarLoadout` shape and minimal server-owned read/write flow so clients can bind slots and recover the same loadout on reconnect.

**In scope**

- Server DTO/API surface (prototype JSON v1) for loadout fetch/update with explicit slot index and ability id fields.
- Client load/apply path for initial hotbar hydration.
- Validation for slot bounds and unknown ability ids with non-empty reason codes on deny.

**Out of scope**

- Final account-vs-character policy hardening beyond a documented prototype choice.
- Cooldown timing and cast execution.

**Acceptance criteria**

- [ ] A player can bind at least two slots and retrieve the same bindings after reconnect/reload.
- [ ] Invalid slot/ability input is rejected with a stable machine-readable reason code.
- [ ] Module docs and per-story implementation plan describe the chosen prototype persistence policy.

**Implementation note (NEO-29):** Prototype policy is **per-player** loadout key, persisted via **Postgres when configured** with **in-memory fallback**.

---

### E1M4-02 — Input to `AbilityCastRequest` path (hotbar activation)

**Goal:** Wire hotbar input to a real `AbilityCastRequest` payload that includes selected target context from E1.M3.

**In scope**

- Client input binding (slot key or click) to request emission.
- Request payload includes at least slot, ability id, and selected target id (if any).
- Integration tests or harness coverage for request formation and send triggers.

**Out of scope**

- Final combat resolution semantics and cooldown rendering.

**Acceptance criteria**

- [ ] Pressing a bound slot emits one cast request with expected payload fields.
- [ ] Request target context uses last acknowledged target state (not camera-only local guess).
- [ ] Unbound slots do not emit requests and expose clear local feedback/logging.

---

### E1M4-03 — Combat accept/deny integration + reason-code UX

**Goal:** Consume authoritative cast accept/deny responses from E5.M1 and reconcile client presentation when casts are rejected.

**In scope**

- Handle accept and deny responses in the cast path.
- Surface deny reasons in agreed prototype UX surface (debug panel, toast, or combat log).
- Reconcile optimistic UI state on deny.

**Out of scope**

- Rich final combat feedback package and VFX.

**Acceptance criteria**

- [ ] Denied casts render a player-visible reason path for at least `invalid_target` and `out_of_range`.
- [ ] Successful casts transition to post-request state without conflicting local rollback behavior.
- [ ] Denial contract and reason list are documented in plan/module docs to prevent drift.

---

### E1M4-04 — `CooldownSnapshot` sync + slot presentation

**Goal:** Add cooldown snapshot contract and hook slot presentation to server cooldown truth.

**In scope**

- Define `CooldownSnapshot` prototype fields and source route/update mechanism.
- Update hotbar slot state (ready, cooling down, remaining time display or indicator).
- Gate repeated cast attempts during active cooldown with synchronized behavior.

**Out of scope**

- Final polished animation treatments for cooldown visuals.

**Acceptance criteria**

- [ ] After a successful cast, affected slot shows cooling state until cooldown completes.
- [ ] Client cannot repeatedly fire a cooling ability without receiving deny/guard behavior.
- [ ] Cooldown UI recovers correctly after reconnect by rehydrating snapshot state.

---

### E1M4-05 — Slice 3 telemetry hooks for cast funnel

**Goal:** Complete Slice 3 telemetry hook sites for `ability_cast_requested` and `ability_cast_denied` with reason metadata and TODO alignment for E9.M1 schema.

**In scope**

- Add/verify hook locations in client/server cast flow for requested/denied events.
- Include payload notes for ability id, slot id, target context, and deny reason.
- Document temporary `TODO(E9.M1)` status where catalog/schema integration is pending.

**Out of scope**

- Production telemetry ingest pipelines, dashboards, and retention policy work.

**Acceptance criteria**

- [ ] Hook names match Epic 1 Slice 3 telemetry vocabulary.
- [ ] Denied cast hook carries a non-empty reason code.
- [ ] Module docs reference where hooks are emitted and what remains deferred to E9.M1.

---

## After this backlog

- Track delivery in the created Linear issues and keep dependency links synchronized if scope changes.
- For each issue kickoff, add `docs/plans/{NEO-XX}-implementation-plan.md` and keep decisions synchronized during implementation.
- Add `docs/manual-qa/{NEO-XX}.md` for user-visible stories when implementation lands.

## Related docs

- [E1_M4_AbilityInputScaffold.md](../decomposition/modules/E1_M4_AbilityInputScaffold.md)
- [E1_M3_InteractionAndTargetingLayer.md](../decomposition/modules/E1_M3_InteractionAndTargetingLayer.md)
- [E5_M1_CombatRulesEngine.md](../decomposition/modules/E5_M1_CombatRulesEngine.md)
- [epic_01_core_player_runtime.md](../decomposition/epics/epic_01_core_player_runtime.md)
