Fix AAA layout in depletion tests, add ResourceNodeInstanceIds.Normalize,
throw on failed lazy-init after catalog hit, and update dependency register
footnote plus review doc strikethroughs.
Shared inventory-api-helper seeds stock and records baselines in
pre-request scripts; assertions use exact deltas instead of cumulative
at.least checks. Harden C# remove round-trip add assertion.
Thin adapter over ItemDefinitionCatalog mirroring NEO-35 skill registry;
registers singleton in AddItemDefinitionCatalog with TryGetDefinition,
GetDefinitionsInIdOrder, and AAA unit + host DI tests.
Batch skillXp then resetPerkState with XP rollback; 400 for invalid skillXp;
MasteryFixtureApi gate tests; README fixture vs grant semantics; Bruno GET playerId.
Replace fixture player ids with POST …/__dev/mastery-fixture (resetPerkState,
absolute skillXp). Bruno deny tests set state in pre-request on dev-local-1.
Gated to Development/Testing or Game:EnableMasteryFixtureApi.
Seed neo48-perk-fresh and neo48-perk-branch via Game:FixturePlayerIds;
level_too_low uses isolated fresh player, branch_already_chosen sets up
neo48-perk-branch in pre-request instead of skipping on shared state.
Add self-contained branch_already_chosen Bruno request, HTTP deny smoke
for unknown track/tier/branch and null-body 400, folder docs for level_too_low.
GET/POST /game/players/{id}/perk-state with versioned JSON, engine-backed
branch selection, PerkStateApiTests, Bruno collection, README, and E2.M3 alignment.
Mirror validate_content.py gates in MasteryCatalogLoader, expose IMasteryCatalogRegistry, cross-check skillIds against the skill catalog, and enforce tierIndex 1..N per track.
Replace placeholder XP level math with content-backed thresholds validated in CI and at server boot, and update docs/manual QA/review artifacts to reflect NEO-39 delivery and follow-up fixes.
Register SkillDefinitionCatalog from content/skills with JsonSchema.Net validation
mirroring validate_content.py and the prototype Slice 1 gate. Resolve paths via
Content:SkillsDirectory or discovery from AppContext.BaseDirectory. Eager-load
after WebApplication build so bad catalogs block startup. Add unit and host
tests, pin test factories to repo skills, document config in server/README, add
manual QA checklist, and extend Bruno GET /health smoke tests.
Split HTTP verification reads into Assert in C# API and integration tests.
Add # Arrange/# Act/# Assert to GdUnit suites and keep Act free of assertions
where casts and busy guards are under test.
Fix the hotbar client test transport body construction so the suite parses and runs again, and normalize AAA marker ordering in the flagged C# tests to restore readability consistency with the test template.
Audit all test files and bring them in line with formatting/style expectations by adding explicit AAA markers across server test methods and fixing GDScript test lint/format issues.
Add explicit Arrange/Act/Assert sections to each hotbar API test method to align with the C# test style guide while preserving existing coverage and behavior.
Set E1.M4 documentation status to In Progress across decomposition tracking docs and add lower-bound slot validation coverage. Mark the corresponding review suggestions and nit as resolved in the saved review file.
Add server/client tests for duplicate-slot, schema, missing-player, normalization, and busy/error handling paths, and include Bruno requests for the new hotbar routes with happy and representative failure checks.
Ship a server-owned hotbar loadout API with Postgres-or-memory persistence so slot bindings survive reconnects in configured environments. Wire client boot hydration and add tests/docs/manual QA to keep the prototype contract and policy explicit.
Followup #5: after locking alpha, walking into beta's ring, stopping and
pressing Tab could still return `out_of_range` even when client and server
agreed on position (Δ=0). The freshness-kick move-stream POST and the
target/select POST race on separate HTTP connections so the select can be
validated against a stale stored snap.
Extend TargetSelectRequest v1 with an optional positionHint {x,y,z}.
Server uses the hint for the radius check and for the echoed
targetState.validity; the hint is advisory and does not write to the
position store (move-stream stays the sole write path). Client attaches
the hint automatically whenever set_freshness_kick(...) is wired, so
headless unit tests that skip the wiring fall through to the no-hint
behavior. Freshness kick is kept in place to refresh the stored snap for
later validity GETs but is no longer load-bearing for denial correctness.
Server: 3 new tests (hint accepts when stored is stale, hint-far denies,
hint never writes to position store). Client: 1 new test asserting the
hint is in the POST body when wired. All 62 server + 96 client tests
pass.
Plan Decision 8, review follow-up #5, manual QA section 6, and NEO-23
contract notes updated. New bruno request exercises the hint path.
All prototype targets now share `PrototypeTargetRegistry.SharedLockRadius`
(= 6 m). Anchors moved to alpha `(-3, 0.5, -3)` and beta `(3, 0, 3)` so
the two 6 m rings overlap ~3.5 m wide centered at origin, letting Tab
flip between targets without locomotion. Spawn `(-5, -5)` keeps alpha
in range (~2.83 m) and beta out (~11.31 m) so the existing soft-lock
denial QA path at spawn still holds.
`prototype_target_constants.gd` mirrors the shared radius + new anchors.
The range-aware Tab picker now **skips the currently-locked id** so Tab
always expresses a swap intent — if the only in-range target is already
locked, the fallback picks the next cycle id and the server denies
(preserves NEO-23's "Tab = visible denial when no swap possible" rule).
Tests:
- Server: new `PlayerTargetStateReaderTests.ComputeValidity_AtOrigin_…`
asserts both targets report `Ok` at origin. Existing boundary test
rewritten for the new alpha anchor.
- Client: new `test_tab_from_locked_alpha_at_origin_swaps_to_beta_when_both_in_range`
and `test_tab_when_only_current_lock_is_in_range_falls_back_to_cycle_for_denial`.
Existing in-range/out-of-range positions updated for the new layout.
Server 59/59 and client 95/95 suites pass.
Plan Decision 7, review follow-up #4, manual QA section 5 (new "Tab-flip
at origin" overlap sanity), and README updated. Per-target radii will
return with real combat design (E5.M1) — this is a prototype stub.
Introduce POST /game/players/{id}/move-stream with ordered targets, full-chain
validation before apply, and PositionStateResponse. Godot client uses camera-
relative WASD, queues samples, and snaps to stream responses; debug builds keep
click-to-move via ground_pick. Update READMEs, E1.M1 snapshot, and tests.
Expanded 45×45 district made the 18 m MaxHorizontalStep too restrictive
(floor diagonal ~63 m). Add HorizontalStepEnabled to MovementValidationOptions
(default false, matching DistrictBoundsEnabled pattern) so the check is
skipped unless explicitly opted in. Add test for disabled case (9 pass).
Update README and plan with the decision.
Use C# primary constructors and camelCase private fields per csharp-style.
Refactor InMemoryPositionStateStore seeding into CreateInitialMap; align
Postgres integration tests and PositionStateApiTests with factory/httpClient
naming.
Wire IPositionStateStore to Npgsql when ConnectionStrings:NeonSprawl is set,
with versioned DDL under server/db/migrations and a shared PostgresPositionBootstrap
for schema plus dev-player seeding at host startup. In-memory tests override Postgres
registrations when CI sets the connection string globally.
Add GitHub Actions Postgres service for dotnet test, server README updates,
decomposition and implementation-plan docs, code review write-up, and C# brace
conventions in csharp-style rule.
Implement E1.M1 intent → authority: POST /game/players/{id}/move with v1 JSON
MoveCommandRequest (snap + sequence), extend in-memory store and
PositionStateApi. Godot client splits ground_pick and position_authority_client
with thin main.gd; snap_to_server on player.
Tests live under NeonSprawl.Server.Tests/Game/PositionState/ with AAA layout
and MethodName_ShouldExpectedOutcome_WhenScenario naming. Debug builds use
UseAppHost=false so Linux IDEs resolve .NET 10 from a user-local SDK.
Add NS-16 implementation plan, code review doc, godot-client-script-organization
rule, csharp test layout/naming rules, and refresh E1.M1 / alignment docs.
Server and client README updates include move API, prerequisites, and Rider
notes.
Made-with: Cursor