12 KiB
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 castprototype_pulseuntil defeat, verify combat HUD +breachgig 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— mirrorSkillProgressionClient(NEO-37); boot hydrate + refresh when cast accept carriestargetDefeated: true.- Module alignment (on story completion): update
documentation_and_implementation_alignment.mdE5.M1 row + E5M1-12 backlog checkboxes; note Epic 5 Slice 1 client capstone complete. - Integration fixes only if capstone QA fails on current
mainwiring (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.mdwith server + client;prototype_target_alphashows defeated state in HUD. - Epic 5 Slice 1 AC: combat readable in fixed isometric camera; no silent cast failures on deny.
breachgig 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.gdGET snapshot +gig_row;GigXpLabelunder 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-86manual QA;client/README.mdend-to-end combat loop section; E5M1 backlog + module alignment rows updated. - Integration: No capstone QA gaps on
mainbeyond 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/100 → 75/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:
- Start server + client; confirm
Gig XP:row showsbreachat 0 xp (boot hydrate). - Walk to orange Dummy α near spawn; Tab lock
prototype_target_alpha— dummy brightens;CombatTargetHpLabelshows100/100. - Press 1 four times (respect ~3s pulse cooldown); verify
CastFeedbackLabel+ HP label step as above. - Verify
GigXpLabelshowsbreachxp25 after lethal hit (event-driven GET refresh). - Press 1 again — deny feedback; gig XP unchanged.
- Esc clear lock — HP label
— (no lock); combat feedback labels remain readable. - Regression spot-check: cast without lock →
invalid_targetdeny onCastFeedbackLabel.
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,_busyguard.parse_progression_json(text)static — requireschemaVersion1,mainGigId,gigsarray.- Signals:
progression_received(snapshot: Dictionary),progression_sync_failed(reason: String). gig_row(gig_id: String, snapshot: Dictionary = {}) -> Dictionary— lookup byid; uses cached snapshot when second arg omitted.
3. Gig XP HUD (main.gd + main.tscn)
- Add
GigProgressionClientnode (scene or runtime likeCombatTargetsClient). - Add
GigXpLabelunderUICanvas/EconomyHudSection/Body/belowSkillProgressionLabel, aboveCraftRecipePanel— respects NEO-75 collapse toggle. _render_gig_xp_label()— formatGig 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_receivedwhenacceptedandresolution.targetDefeated === true→ gig progression sync (NEO-44 grant is server-side on that accept; GET reconciles HUD).
- Boot:
- 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.mdas authoritative capstone script. - Note
Economy HUDtoggle 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 |