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

12 KiB
Raw Blame History

NEO-86 — Implementation plan

Story reference

Field Value
Key NEO-86
Title E5M1-12: Playable tab-target combat capstone (Godot)
Linear https://linear.app/neon-sprawl/issue/NEO-86/e5m1-12-playable-tab-target-combat-capstone-godot
Module E5.M1 — CombatRulesEngine · Epic 5 Slice 1 · backlog E5M1-12
Branch NEO-86-playable-combat-capstone
Server deps NEO-82 cast + combatResolution (Done); NEO-83 combat-targets GET (Done); NEO-44 gig XP on defeat + GET (Done)
Client deps NEO-85 combat feedback + target HP HUD (Done); E1.M4 cast funnel (NEO-28/31/32) (Done)
Pattern Capstone integration — NEO-75 docs + manual QA primary; minimal client gap-fill for gig visibility
Server counterpart NEO-44 gig progression GET; Bruno-only verification is not prototype-complete per full-stack epic decomposition

Kickoff clarifications

Topic Question Agent recommendation Answer
Gig XP visibility Include gig XP in the Godot capstone? GigProgressionClient + GigXpLabel in economy HUD; refresh after defeat cast — NEO-44 landed; NEO-75 capstone is Godot-only (no Bruno/curl). User: Godot gig label + client.
Implementation scope Docs-only vs client code? Docs + manual QA primary; add gig client wiring; fix integration gaps only if capstone QA fails on main. User: docs-first; code for gig row or QA failures.
Session baseline Fresh server vs fixture POST only? Fresh server restart before Godot F5 — resets in-memory dummy HP + gig XP baseline (NEO-75 / NEO-44 manual QA precedent). Adopted (kickoff default).

No other blocking decisions — combat loop HUD, hotbar pulse bootstrap, and event-driven combat-target refresh are landed in NEO-85; field names follow NEO-82 / NEO-44 server vocabulary.

Goal, scope, and out-of-scope

Goal: Prove Epic 5 Slice 1 acceptance in Godot: tab-target lock, cast abilities, readable deny reasons, defeat prototype_target_alpha, and visible gig XP after defeat — without Bruno.

In scope (from Linear + E5M1-12):

  • docs/manual-qa/NEO-86.md: numbered single-session capstone script — fresh dev player, Tab lock alpha, digit cast prototype_pulse until defeat, verify combat HUD + breach gig XP — no Bruno/curl steps.
  • client/README.md: End-to-end combat loop section — integration checklist (boot → Tab lock → cast → defeat → gig XP refresh chain); cross-links NEO-23/28/31/85.
  • GigProgressionClient + GigXpLabel — mirror SkillProgressionClient (NEO-37); boot hydrate + refresh when cast accept carries targetDefeated: true.
  • Module alignment (on story completion): update documentation_and_implementation_alignment.md E5.M1 row + E5M1-12 backlog checkboxes; note Epic 5 Slice 1 client capstone complete.
  • Integration fixes only if capstone QA fails on current main wiring (unexpected deny, refresh gap, etc.).

Out of scope (from Linear + backlog):

  • NPC AI (E5.M2), encounters/loot (E5.M3), group scaling.
  • Final combat VFX, floating damage numbers, player HP bar.
  • Full nine-gig catalog, hub swap, gig level curves.
  • Bruno-only verification as prototype-complete proof.

Acceptance criteria checklist

  • Human completes docs/manual-qa/NEO-86.md with server + client; prototype_target_alpha shows defeated state in HUD.
  • Epic 5 Slice 1 AC: combat readable in fixed isometric camera; no silent cast failures on deny.
  • breach gig XP visible in Godot after defeat (≥ 25 from fresh-server baseline).
  • Re-read epic_05 Definition of Done for prototype minimums.

Implementation reconciliation (shipped)

  • Client: gig_progression_client.gd GET snapshot + gig_row; GigXpLabel under economy HUD; defeat-triggered refresh in _on_cast_result_received.
  • Tests: gig_progression_client_test.gd, gig_feedback_refresh_test.gd (5 cases pass).
  • Docs: NEO-86 manual QA; client/README.md end-to-end combat loop section; E5M1 backlog + module alignment rows updated.
  • Integration: No capstone QA gaps on main beyond gig client wiring.

Technical approach

1. Capstone manual QA script (docs/manual-qa/NEO-86.md)

Preconditions: Stop any running server; start fresh (dotnet run) so in-memory dev-local-1 dummy HP and gig XP reset. Godot F5 with no prior curl/Bruno seeding.

Combat math (from NEO-85 / NEO-82):

Cast # Expected cast feedback Target HP label
1 Cast: 25 dmg → 75 HP 100/10075/100
2 → 50 HP 50/100
3 → 25 HP 25/100
4 → 0 HP — defeated! 0/100 (defeated)
5 ability_cast_denied: target_defeated unchanged defeated

Checklist outline:

  1. Start server + client; confirm Gig XP: row shows breach at 0 xp (boot hydrate).
  2. Walk to orange Dummy α near spawn; Tab lock prototype_target_alpha — dummy brightens; CombatTargetHpLabel shows 100/100.
  3. Press 1 four times (respect ~3s pulse cooldown); verify CastFeedbackLabel + HP label step as above.
  4. Verify GigXpLabel shows breach xp 25 after lethal hit (event-driven GET refresh).
  5. Press 1 again — deny feedback; gig XP unchanged.
  6. Esc clear lock — HP label — (no lock); combat feedback labels remain readable.
  7. Regression spot-check: cast without lock → invalid_target deny on CastFeedbackLabel.

Document optional POST /game/__dev/combat-targets-fixture for mid-session dummy-only reset (NEO-85) — capstone baseline still prefers server restart.

2. Gig progression client (gig_progression_client.gd)

Mirror skill_progression_client.gd:

  • GET {base}/game/players/{id}/gig-progression — injectable HTTP, _busy guard.
  • parse_progression_json(text) static — require schemaVersion 1, mainGigId, gigs array.
  • Signals: progression_received(snapshot: Dictionary), progression_sync_failed(reason: String).
  • gig_row(gig_id: String, snapshot: Dictionary = {}) -> Dictionary — lookup by id; uses cached snapshot when second arg omitted.

3. Gig XP HUD (main.gd + main.tscn)

  • Add GigProgressionClient node (scene or runtime like CombatTargetsClient).
  • Add GigXpLabel under UICanvas/EconomyHudSection/Body/ below SkillProgressionLabel, above CraftRecipePanel — respects NEO-75 collapse toggle.
  • _render_gig_xp_label() — format Gig XP:\nbreach: L1 · 25 xp (prototype fixed level 1 from server); Loading… / error lines match skill label pattern.
  • Refresh triggers (event-driven):
    • Boot: request_sync_from_server() in setup (parallel to skill progression).
    • cast_result_received when accepted and resolution.targetDefeated === true → gig progression sync (NEO-44 grant is server-side on that accept; GET reconciles HUD).
  • Do not refresh gig XP on every cast accept — only boot + defeat transition (minimize HTTP chatter).

4. README integration checklist

New ## End-to-end combat loop (NEO-86) section after NEO-85 combat HUD subsection:

  • Flow in prose: boot hotbar sync (auto pulse bind) → Tab lock dummy → digit cast → cast feedback + HP label refresh → defeat → gig XP refresh.
  • Cross-links: NEO-23 (targeting), NEO-28/31 (cast), NEO-85 (combat HUD), NEO-44 (server gig grant).
  • Pointer to docs/manual-qa/NEO-86.md as authoritative capstone script.
  • Note Economy HUD toggle hides gig row with other economy labels.

5. Module alignment (implementation batch or story end)

When acceptance criteria pass:

  • documentation_and_implementation_alignment.md: E5.M1 — note NEO-86 landed, Epic 5 Slice 1 client capstone complete.
  • E5M1-prototype-backlog.md: E5M1-12 checkboxes + landed note.
  • E5_M1_CombatRulesEngine.md: E5M1-12 row if not already marked.

6. Integration fixes (conditional)

Run capstone QA on main wiring before gig client work; if any step fails, fix minimal main.gd / client bug in this branch and note in plan Decisions. Expected path: gig client + docs only.

Files to add

Path Purpose
docs/plans/NEO-86-implementation-plan.md This plan.
docs/manual-qa/NEO-86.md Capstone single-session manual QA (zero Bruno/curl).
client/scripts/gig_progression_client.gd GET gig-progression snapshot; parse v1; gig_row helper.
client/scripts/gig_progression_client.gd.uid Godot uid companion (tracked).
client/test/gig_progression_client_test.gd GdUnit: parse snapshot; gig_row("breach"); HTTP error path. AAA layout.
client/test/gig_feedback_refresh_test.gd GdUnit: cast accept with targetDefeated triggers gig sync (spy harness). AAA layout.

Files to modify

Path Rationale
client/scripts/main.gd Wire GigProgressionClient; _render_gig_xp_label; defeat-triggered gig refresh in _on_cast_result_received.
client/scenes/main.tscn Add GigProgressionClient node + GigXpLabel under economy HUD body.
client/README.md End-to-end combat loop section; gig XP row docs; capstone manual QA link.
docs/decomposition/modules/documentation_and_implementation_alignment.md E5.M1 row — NEO-86 landed + Slice 1 client capstone complete (on story completion).
docs/plans/E5M1-prototype-backlog.md E5M1-12 acceptance checkboxes + landed note (on story completion).

Tests

Path Change
client/test/gig_progression_client_test.gd Add: GET parse v1 with breach row; invalid schema → failure signal; 404 path.
client/test/gig_feedback_refresh_test.gd Add: cast accept with targetDefeated: true triggers GigProgressionClient.request_sync_from_server; deny / non-defeat accept does not.

Manual verification: docs/manual-qa/NEO-86.md — server + Godot; full defeat spine + gig XP visibility without curl.

Open questions / risks

Question or risk Agent recommendation Status
Gig refresh on every accept Refresh only on targetDefeated + boot — avoids redundant GETs on non-lethal hits. adopted
Gig label inside economy collapse Place under EconomyHudSection/Body — hidden when economy HUD collapsed (NEO-75); combat labels stay visible. adopted
Overlap with NEO-85 manual QA NEO-86 supersedes as capstone script; keep NEO-85 as component-level regression reference. adopted
Postgres dev DB retains gig XP Capstone preconditions require server restart; document that shared Postgres may show prior totals. adopted
Integration gap on main Run capstone dry-run first; fix only if QA fails. adopted — no logic gaps beyond gig client