Commit Graph

169 Commits

Author SHA1 Message Date
claude-checkpoint-tooling aeccc78143 Fix login form fields reappearing after Play -> Back (real playtest bug)
_show_authenticated() (the session-restore path, used when returning to
main_menu.tscn -- e.g. after clicking Back from world_list) only hid
EmailEdit/Over18Check, unlike _on_auth_complete() (the fresh-login path)
which hides the full form: UsernameEdit, PasswordEdit, EmailEdit,
Over18Check, AuthButton, AuthModeLabel. Play was correctly enabled and
the welcome message was correctly visible, so a check that stopped at
those two passed clean -- but the actual login fields were still on
screen, which is what a real user sees as "it's making me log in again."
Confirmed via live PC1 playtest today.

Reported by: real playtest, 2026-08-20 (yesterday's automated regression
test for this exact Play -> Back flow only asserted Play/welcome/auth
buttons row -- strengthened smoke_test_main_menu_back.gd to also check
the individual form fields, and confirmed it fails exactly this way with
the bug reintroduced before restoring the fix).

Verified against a live backend: 61/61 pytest (unaffected), 8/8 Godot
smoke tests (including 20/20 on the strengthened back-nav test).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M6rUEynmv2QmweocAQYARN
2026-08-20 19:21:40 +00:00
claude-checkpoint-tooling 1c64dd4967 Fix broken main_menu.gd on main + land Play->Back session persistence
Two things, found together while investigating a stuck blocker task:

1. URGENT: main_menu.gd currently fails to parse at all on main
   ("Identifier '_dob_edit' not declared in the current scope"). PR #44
   (hide-all-fields-on-auth-success) and PR #45 (DOBEdit -> Over18Check
   checkbox) each merged cleanly on their own -- Gitea's merge reported
   mergeable:true for both -- but together they left a reference to the
   old _dob_edit var on a line neither PR's diff touched, so no conflict
   was ever flagged. Same failure mode in smoke_test_main_menu_full_flow.gd
   (dob_edit, would have failed to even parse in the checkpoint suite).
   Fixed both to reference _over_18_check / over_18_check.

2. Lands the Play -> Back session-persistence fix (PR #43, task
   28357bca192d) that was stuck behind review the whole time main didn't
   have it yet: signal auth_state_ready, _show_authenticated() /
   _reset_to_logged_out() helpers, and _ready() restoring the
   authenticated UI (not resetting it) when ApiClient already holds a
   valid token. Plus the real fix a review round caught on top of that
   (task 9186405a6373): _show_authenticated() was hiding AuthPanel while
   claiming the welcome label inside it was visible -- a no-op UI-wise,
   since WelcomeLabel.visible=true does nothing once its parent is
   hidden. AuthPanel now stays visible; the regression test
   (smoke_test_main_menu_back.gd, new) asserts is_visible_in_tree(), not
   just .visible, so this class of bug can't pass silently again.

Verified against a live backend: 61/61 pytest, 7/7 Godot smoke tests
(including the new smoke_test_main_menu_back.gd, 12/12).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M6rUEynmv2QmweocAQYARN
2026-08-20 18:15:32 +00:00
reviewer-bot 4dd6c7c8af Merge pull request 'Main menu: hide the login/register form once authenticated, keep only the welcome message' (#44) from agent/470f875e0416 into main 2026-08-20 17:08:45 +00:00
claude-checkpoint-tooling 33b0c3f1fb Merge main into agent/470f875e0416 to resolve PR #44 conflicts
# Conflicts:
#	scripts/main_menu.gd
2026-08-20 16:47:44 +00:00
claude-checkpoint-tooling 0ecc16d963 Registration: replace date-of-birth field with a simple 18+ confirmation
The register form collected and validated an actual date of birth just to
throw it away (backend never stored it beyond a nullable column). Per the
roadmap note: don't collect/store DOB right now, just get a client-side
"I am 18 or older" confirmation. RegisterRequest.date_of_birth (str) is now
over_18 (bool); the endpoint rejects over_18=false with 400 same as before.
calculate_age() is kept (unused by /register now) since real DOB collection
is coming back in a future pass and deleting it broke test_auth_age_gate.py's
import for no benefit.

Frontend: DOBEdit LineEdit replaced with an Over18Check CheckBox; api_client.gd's
register() now takes a bool instead of a date string. All Godot callers
(smoke_test_auth/e2e/world_list/main_menu_ui/main_menu_full_flow) and backend
test files updated to match. Full suite verified against a live backend:
61/61 pytest, 6/6 Godot smoke tests (33 individual assertions), all passing.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M6rUEynmv2QmweocAQYARN
2026-08-20 16:25:27 +00:00
coder-bot 8f1a1110e2 WIP checkpoint: Main menu: hide the login/register form once authenticated, keep only the welcome message 2026-08-20 15:01:02 +00:00
coder-bot 12fef51020 WIP checkpoint: Main menu: fix Register-mode UI trapping the user (remove the confusing mode toggle) 2026-08-20 14:03:57 +00:00
coder-bot af50819a16 WIP checkpoint: Main menu: fix Register-mode UI trapping the user (remove the confusing mode toggle) 2026-08-20 14:01:20 +00:00
coder-bot 16bb87c6b0 WIP checkpoint: Main menu: fix Register-mode UI trapping the user (remove the confusing mode toggle) 2026-08-20 13:56:44 +00:00
claude-checkpoint-tooling 8fc9d378e4 Fix: world cards render at ~1px wide once real data exists
Reported directly by the user after seeding a test world through the
browse-worlds screen: "screen is blank now". Two compounding layout
bugs, both real:

1. world_list.tscn's root VBoxContainer had no anchors at all, only
   size_flags -- but its parent (the scene's root node) is a plain
   Control, not a Container, so size_flags on a Control's direct child
   are meaningless there. Without anchors it never actually filled the
   screen, so it (and everything under it) collapsed toward zero size.

2. size_flags_horizontal/vertical used the raw integer 4 everywhere
   expand-fill was clearly intended (ScrollContainer, its WorldList
   child, HeaderHBox, TitleLabel) -- but 4 is Control.SIZE_SHRINK_CENTER
   in Godot 4, not SIZE_EXPAND_FILL (which is 3). ScrollContainer with
   SHRINK_CENTER takes only its own minimum size instead of expanding,
   cascading a ~1px width down through every card and every label.
   Confirmed the actual enum values by asking Godot directly rather
   than assuming.

Both are exactly the kind of bug a headless smoke test checking only
"did cards get added to the tree" can't catch -- child_count was
correctly non-zero, nothing crashed, the content was just rendered
invisibly thin. Verified the fix is real by deliberately reintroducing
bug #2 and confirming the test now correctly fails (card width 25px,
below the 200px sanity threshold) before restoring it.

Also replaced the card background: it was a ColorRect manually moved to
child index 0 of a VBoxContainer, which doesn't work as an overlay --
a VBoxContainer lays out ALL its children in sequence, so the "moved to
front" ColorRect rendered as an empty dark bar ABOVE the text, not
behind it. Now a proper PanelContainer with a StyleBoxFlat background,
the idiomatic way to get a colored panel behind auto-laid-out content
in Godot.

smoke_test_world_list.gd strengthened to actually catch this class of
bug going forward: seeds a real world via POST /api/worlds/ first (the
checkpoint runner's fresh DB meant the populated-card path never ran in
CI at all before this, only empty-list did), and asserts each card's
real computed width, not just that cards exist.

Full checkpoint suite re-verified clean on this branch: migrations,
68/68 pytest, all 6 Godot smoke tests, both platform exports.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 13:35:46 +00:00
claude-checkpoint-tooling 3679e4dbc4 Fix: Login/Register unreachable from the main menu -- no button opened it
Reported directly by the user playtesting the exported exe: Play was
permanently disabled and there was no way to log in or register. Root
cause: main_menu.tscn wired Play and Quit, but nothing was ever
connected to _on_login_button_pressed()/_on_register_button_pressed() --
the AuthPanel, its form, and all its backend-calling logic were fully
correct, just completely unreachable. The script's own top comment even
said "(the existing Play/Quit buttons)" as if Play/Quit somehow doubled
as login triggers, which they never did -- a sign nobody ever actually
clicked through the running scene, only checked things at the API level.

Fix: added a real AuthButtonsRow (LoginButton, RegisterButton) wired to
the existing handlers, which now hide themselves once auth succeeds.
Also fixed grab_focus() targeting the AuthPanel (which can't accept
focus, just a background warning) to focus the username field instead.

Verified via two new smoke tests (smoke_test_main_menu_ui.gd,
smoke_test_main_menu_full_flow.gd) using a *real signal emission* on
the buttons, not a direct method call -- that distinction is exactly
what would have caught the original bug. Full click-through confirmed
against a real backend: open register form, fill it, submit, Play
actually becomes enabled, welcome message shows, auth buttons hide.

Also documents a real headless-testing gotcha found while verifying
this: a synthetic Button.pressed.emit() on an await-using handler
doesn't reliably get driven forward in --script/SceneTree mode the way
real GUI input dispatch does -- confirmed by comparing direct-call vs
emit() behavior side by side. Not a bug in the actual click-driven flow,
a test-harness quirk to route around (call the handler directly,
awaited, instead of emitting the signal).

Full checkpoint suite re-verified clean on this branch: migrations,
68/68 pytest, all 6 Godot smoke tests (including the 2 new ones, picked
up automatically by run_checkpoint_tests.py's glob).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 13:13:38 +00:00
tester-bot fcfb767c49 End-to-end headless smoke test: register -> login -> fetch worlds
Tester had already written a complete, correct test (4/4 steps pass
against a real backend) but stalled investigating DB/migration setup
after its own verification run silently exited with no result -- the
task spec's suggested invocation included --quit, which kills a --script
SceneTree test before its awaited HTTP calls finish (same Godot
--script-mode gotcha documented elsewhere this session).

Renamed smoke_test.gd -> smoke_test_e2e.gd: the task spec named it
without the checkpoint runner's expected smoke_test_*.gd suffix, so
scripts/run_checkpoint_tests.py's glob would have silently never picked
it up. Also fixed the --quit flag in the file's own usage comments.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 23:30:52 +00:00
tester-bot e2e771d2f8 WIP checkpoint: End-to-end headless smoke test + manual verify 2026-08-19 23:15:22 +00:00
coder-bot 3dd5416e81 Merge origin/main into agent/1de295a0da42
Resolves conflict in main_menu.gd's play button: kept this branch's real
world_list.tscn transition over main's stale TODO placeholder (main's
version predates this fix; nothing else has touched the play button
since). Brings in PR #37's /auth/refresh endpoint and its tests cleanly,
no other conflicts.
2026-08-19 23:11:05 +00:00
coder-bot b3227f5b78 Wire browse-worlds screen to GET /api/worlds
- main_menu.gd: Play button now navigates to world_list.tscn
- world_list.gd: fix invalid theme_override_constants property-path syntax
  (use add_theme_constant_override), fix @onready node paths missing
  /HeaderHBox/, cast Variant dict values to String to avoid type-inference
  errors when cards actually render
- world_list.tscn: wire up Back/Refresh button signal connections (scene
  had zero [connection] blocks, buttons were dead)
- backend: fix real bug where World.visibility enum sent member names
  ("PUBLIC") to Postgres instead of lowercase values, breaking both GET
  and POST /api/worlds/ for any real query
- backend: fix WorldResponse.created_at typed as str instead of datetime,
  causing a second 500 on GET once the enum bug was fixed
- add smoke_test_world_list.gd: headless scene-level smoke test that
  registers a user, instantiates the real scene, and confirms world cards
  render from live backend data (not just an API-level check)
2026-08-19 21:57:39 +00:00
coder-bot 2ba6bc6b9f WIP checkpoint: Implementation: Browse worlds screen wired to GET /api/worlds 2026-08-19 21:27:24 +00:00
coder-bot 03da22aa9e WIP checkpoint: Implementation: Browse worlds screen wired to GET /api/worlds 2026-08-19 21:20:52 +00:00
coder-bot 55a65bb7b1 WIP checkpoint: Implementation: Browse worlds screen wired to GET /api/worlds 2026-08-19 21:20:52 +00:00
coder-bot 62e15d0c87 WIP checkpoint: Implementation: Browse worlds screen wired to GET /api/worlds 2026-08-19 21:20:52 +00:00
coder-bot 6d5051bbb2 WIP checkpoint: Implementation: Login/Register UI wired to /api/auth 2026-08-19 20:23:02 +00:00
coder-bot 69b6f40eb9 WIP checkpoint: Implementation: Login/Register UI wired to /api/auth 2026-08-19 19:51:31 +00:00
coder-bot 4d467c0c9e WIP checkpoint: Implementation: Login/Register UI wired to /api/auth 2026-08-19 19:51:31 +00:00
reviewer-bot f64ffd6422 Merge pull request 'Write headless Godot export CI script' (#32) from agent/a7d462fe1322 into main 2026-08-19 17:16:46 +00:00
reviewer-bot 7e28e2b525 Merge pull request 'Add test inventory + checkpoint test runner' (#31) from docs/test-inventory-and-runner into main 2026-08-19 17:01:42 +00:00
coder-bot 99be8f1dab Write headless Godot export CI script
Adds export_presets.cfg (Linux/X11 + Windows Desktop, x86_64) -- didn't
exist at all before this, which is why the task got stuck: you can't run
godot --export-release without presets already defined, and Godot only
auto-generates this file interactively through the editor. Hand-wrote a
minimal valid one and verified both platforms actually export and produce
real binaries (not just that the command exits 0):
- export/linux/agp-game.x86_64 -- confirmed real ELF 64-bit executable via `file`
- export/windows/agp-game.exe -- 104MB PE binary

scripts/build.sh wraps both exports, documents the exact Godot version +
export template requirement, and was run end-to-end (not just written) to
confirm it works as a real CI entrypoint would invoke it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 15:54:50 +00:00
claude-checkpoint-tooling 634b2e39c0 Add test inventory + checkpoint test runner
docs/testing/test_inventory.md: honest, current list of what automated
coverage actually exists (38 backend pytest tests across 2 files, 0 Godot
tests merged yet) and what doesn't (no integration/endpoint tests at all,
no coverage yet for the in-review RS256/moderation work) -- verified
against a genuinely fresh checkout + fresh Postgres, not just read off the
file tree.

scripts/run_checkpoint_tests.py: runs the whole inventory in one shot --
throwaway Postgres, migrations, pytest, then starts the backend server and
runs every scripts/smoke_test_*.gd against it headlessly. Verified working
both with and without a smoke test present (temporarily tested against
PC1's not-yet-merged scripts/api_client.gd + smoke_test_api.gd, removed
before this commit -- that PR carries its own files).

docs/testing/README.md: what running this at a Package Checkpoint review
does and doesn't prove, and how to grow the inventory as real coverage
gets added.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 15:18:49 +00:00
architect-bot ef21bf9d69 Fix smoke test: resolve ApiClient via get_node, defer one frame before first request
Two Godot --script-mode-only quirks, neither a bug in ApiClient itself
(verified working against a real backend, health_check returns 200):
1. Bare autoload identifiers don't resolve at compile time in --script/
   SceneTree runs (the node exists at runtime, root.has_node() is true,
   but referencing it as a plain global name fails to compile) -- use
   get_node() instead.
2. A freshly add_child()'d HTTPRequest isn't reliably "inside tree" yet
   at _initialize() time, since --script mode hasn't processed a frame
   yet -- one process_frame await fixes it.

Verified: full smoke test now passes end-to-end against a live backend
(docker compose postgres + alembic migrations + uvicorn), confirming
ApiClient.health_check() genuinely round-trips.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-19 14:40:51 +00:00
architect-bot e06cd2a23d WIP checkpoint: Architecture: ApiClient autoload (HTTP + JWT + error contract) 2026-08-19 14:31:35 +00:00
architect-bot 77f9b74286 WIP checkpoint: Architecture: ApiClient autoload (HTTP + JWT + error contract) 2026-08-19 14:12:56 +00:00
architect-bot 6108c63c46 WIP checkpoint: Architecture: ApiClient autoload (HTTP + JWT + error contract) 2026-08-19 14:11:48 +00:00
architect-bot 109c6ef1a0 WIP checkpoint: Architecture: ApiClient autoload (HTTP + JWT + error contract) 2026-08-19 14:11:48 +00:00
architect-bot 33c5236a2d WIP checkpoint: Architecture: ApiClient autoload (HTTP + JWT + error contract) 2026-08-19 14:11:48 +00:00
architect-bot 4bb4e9fb8a WIP checkpoint: Architecture: ApiClient autoload (HTTP + JWT + error contract) 2026-08-19 14:11:48 +00:00
architect-bot ffbc4849a7 Recovered orphaned WIP before switching to a new task 2026-08-19 14:11:47 +00:00
coder-bot cf8fd5a6fd WIP checkpoint: Implement Godot dedicated server scene + player spawning 2026-08-19 13:45:39 +00:00
coder-bot fc9a39de93 WIP checkpoint: Implement Godot dedicated server scene + player spawning 2026-08-19 13:38:29 +00:00
coder-bot dcaaf96d81 WIP checkpoint: fix static registry, uint32 type, GLTF API calls, ARRAY_BONES, missing slot handler
- avatar_slot_registry.gd: _slot_data/_slot_definitions made static (every
  caller uses AvatarSlotRegistry.method() on the class, never an instance,
  so the old instance _init() populating them was never invoked); moved
  registry build to static func _static_init()
- avatar_catalog.gd: compute_loadout_hash() return type uint32 -> int
  (GDScript has no fixed-width uint type)
- avatar_loader.gd: GLTFDocument.parse() (doesn't exist) -> the real
  append_from_buffer()+generate_scene() API; Error.parse_error() (doesn't
  exist) -> error_string(); Mesh.ARRAY_JOINTS (doesn't exist) ->
  Mesh.ARRAY_BONES; removed a dead 'mesh is MultiMesh' check that can
  never be true (MeshInstance3D.mesh is always Mesh, never MultiMesh);
  fixed _merge_node_into_avatar to recurse into a Skeleton3D's children
  (skinned meshes are commonly children of the skeleton in a GLB export,
  the old code returned before ever reaching them)
- avatar_customization_ui.gd: added the missing _on_slot_button_pressed
  handler (referenced by a button connect, never defined -- just calls
  the existing _select_slot())

Verified: godot --headless --import + --quit is fully clean, and the
avatar_customization.tscn scene now instantiates successfully.

Remaining, scoped separately: avatar_visualizer.gd calls a
AvatarLoader.load_slot() static async API and SlotItem/SlotResult types
that don't exist -- AvatarLoader's real API (matching the project's own
avatar.md spec: one canonical skeleton, attachments retargeted onto it)
is instance-based load_base_body()/load_attachment(). AvatarVisualizer
needs rewriting to use that real API; it's the one out of step with spec,
not AvatarLoader.
2026-08-18 14:47:08 +00:00
reviewer-bot dbdc01f8d6 Merge remote-tracking branch 'origin/main' into merge-fix
# Conflicts:
#	.gitignore
#	project.godot
2026-08-18 14:40:41 +00:00
coder-bot 803405aba4 WIP checkpoint: Implement avatar loader and customization UI in Godot 2026-08-18 14:03:07 +00:00
coder-bot ea6958c11e Recovered orphaned WIP before switching to a new task 2026-08-18 13:50:01 +00:00
coder-bot 61ee81a974 WIP checkpoint: fix script-attach typo, missing class_name, var name mismatch
- scenes/avatar_customization.tscn: script_extense -> script (typo meant
  the customization UI script was never actually attached to the scene)
- scripts/avatar/avatar_loadout.gd: add missing class_name AvatarLoadout
  (avatar_system.gd references the type everywhere; only this file among
  the avatar scripts lacked its class_name)
- scripts/main_menu.gd: _ready() referenced _customize_button/_settings_button/
  _quit_button but the @onready vars are named without the leading
  underscore
2026-08-18 13:44:41 +00:00
coder-bot 93d83a28fc WIP checkpoint: Address review feedback: Implement chunk streaming system in GDScript 2026-08-18 13:11:54 +00:00
coder-bot 62f12e55db WIP checkpoint: Implement avatar loader and customization UI in Godot 2026-08-18 13:09:57 +00:00
coder-bot 4c0af03573 WIP checkpoint: Implement avatar loader and customization UI in Godot 2026-08-18 13:01:14 +00:00
coder-bot 25f2dffc53 WIP checkpoint: Address review feedback: Implement chunk streaming system in GDScript 2026-08-18 02:45:44 +00:00
coder-bot 929c69e5da WIP checkpoint: Address review feedback: Implement chunk streaming system in GDScript 2026-08-18 02:40:34 +00:00
coder-bot 689c51e179 WIP checkpoint: Implement avatar loader and customization UI in Godot 2026-08-18 00:47:59 +00:00
coder-bot 5cfbcd8efd WIP checkpoint: Implement avatar loader and customization UI in Godot 2026-08-18 00:36:39 +00:00
coder-bot 27461d9b5e WIP checkpoint: Implement avatar loader and customization UI in Godot 2026-08-18 00:25:17 +00:00
coder-bot 1c5c0f8c8e WIP checkpoint: Implement chunk streaming system in GDScript 2026-08-18 00:15:16 +00:00