fix(multi): resolve 16 bugs from comprehensive rules and code review
Build and Deploy / build-and-push (push) Successful in 4m12s
Build and Deploy / build-and-push (push) Successful in 4m12s
Core (solitaire_core): - fix(core): auto-complete now requires waste empty to prevent deadlock - fix(core): reject multi-card moves from waste pile (Klondike rule) - fix(core): reject foundation-to-foundation moves (score farming exploit) - fix(core): undo restores score from snapshot baseline, not live score - feat(scoring): add +5 flip bonus when face-down tableau card is exposed - feat(scoring): add recycle penalty (Draw-1: -100/pass, Draw-3: -20/pass) Engine (solitaire_engine): - fix(engine): remove TokioRuntimeResource::default() panic; degrade gracefully - fix(engine): add ModalScrim guard to handle_new_game spawn site - fix(engine): add ModalScrim guard to spawn_restore_prompt spawn site - fix(engine): add ModalScrim guard to check_no_moves spawn site Server / Web (solitaire_server): - fix(web): correct draw_mode casing in replay submission (DrawOne/DrawThree) - fix(web): correct mode casing in replay submission (Classic) for leaderboard - fix(web): trim recorded_at to YYYY-MM-DD for NaiveDate deserialization - fix(server): move /avatars route outside auth middleware (was always 401) Data / Sync (solitaire_data, solitaire_sync): - fix(data): namespace Android token file under APP_DIR_NAME with migration - fix(data): Android token store now multi-user (HashMap); no silent overwrite - fix(sync): draw_one_wins + draw_three_wins invariant preserved after merge Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -146,7 +146,6 @@ fn build_router_inner(state: AppState, rate_limit: bool) -> Router {
|
||||
.route("/api/account", delete(auth::delete_account))
|
||||
.route("/api/me", get(auth::get_me))
|
||||
.route("/api/me/avatar", put(auth::upload_avatar))
|
||||
.nest_service("/avatars", ServeDir::new("avatars"))
|
||||
.layer(axum_middleware::from_fn_with_state(
|
||||
state.clone(),
|
||||
middleware::require_auth,
|
||||
@@ -198,7 +197,8 @@ fn build_router_inner(state: AppState, rate_limit: bool) -> Router {
|
||||
.route("/api/daily-challenge", get(challenge::daily_challenge))
|
||||
.route("/api/replays/recent", get(replays::recent))
|
||||
.route("/api/replays/{id}", get(replays::get_by_id))
|
||||
.route("/health", get(health));
|
||||
.route("/health", get(health))
|
||||
.nest_service("/avatars", ServeDir::new("avatars"));
|
||||
|
||||
// Replay web UI: a single HTML page served at `/replays/:id` plus a
|
||||
// ServeDir for the static assets (`web/index.html`, `web/replay.css`,
|
||||
|
||||
@@ -431,12 +431,12 @@ async function submitReplay(s) {
|
||||
const payload = {
|
||||
schema_version: 1,
|
||||
seed: Math.round(game.seed()),
|
||||
draw_mode: drawThree ? "draw_three" : "draw_one",
|
||||
mode: "classic",
|
||||
draw_mode: drawThree ? "DrawThree" : "DrawOne",
|
||||
mode: "Classic",
|
||||
time_seconds: elapsedSecs,
|
||||
final_score: s.score,
|
||||
move_count: s.move_count,
|
||||
recorded_at: new Date().toISOString(),
|
||||
recorded_at: new Date().toISOString().slice(0, 10),
|
||||
moves: [],
|
||||
};
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user