Repository navigation
fix(ws-server): score submit-answer from the server clock and only for the open question - #233
Merged
TheCodeHeist-Coder merged 1 commit intoNov 3, 2026
Conversation
…r the open question submit-answer used the timeMs the client sent (only clamped) and never checked that the question was still open. Now it reads quiz:deadline and quiz:current-q from Redis, drops the answer if the question is not the live one or the deadline has passed, and computes elapsedMs from the deadline instead of using the payload value. The measured value is also what gets stored as the answer's timeMs. quiz:end and the "no more questions" path now clear the deadline keys, so answers arriving after the quiz ended are not buffered again. Adds handleMessages.submitAnswer.test.ts (fake clock) and updates the auth test mock for the new timeManager exports. ARCHITECTURE.md scoring notes updated to match.
TheCodeHeist-Coder
approved these changes
Nov 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
quiz:submit-answerinapps/ws-server/src/utils/handleMessages.tsnow measures the time itself and only accepts an answer for the question that is open right now. #157 added the clamp and the rescale, but the number still came from the client and nothing checked that the question was still live.quiz:deadline:<sid>andquiz:current-q:<sid>from Redis (the keystimeManager.tsalready writes). If there is no deadline, the question index doesn't match the one in the message, ornow > deadline, the answer is dropped.elapsedMs = limitMs - (deadline - now), clamped to[0, limitMs]. ThetimeMsin the payload is not read anymore. The measured value is also what gets stored as the answer'stimeMs.questionIdis dropped too (before it was buffered with 0 points).quiz:endand the "no more questions" branch ofquiz:next-questionnow call a newcloseQuestionWindow()intimeManager.ts, which deletes the deadline and current-q keys. Without that, when the host ends the quiz mid-question the deadline key lives on for up totimeLimit + 60sand answers would still get buffered after the final board. It runs beforedrainSession.ARCHITECTURE.md: the scoring notes said the client sendstimeMsand still showed the old1000 - timeMsformula, so I updated both.Related issue
Closes #232
Type of change
Which services does this touch?
apps/frontendapps/http-serverapps/ws-serverapps/genAIpackages/db(schema or Prisma changes)How did you test this?
Run in
apps/ws-server, baseline is cleanmain(976d5d8):pnpm test(vitest)pnpm check-typespnpm build(tsc -b)No pre-existing failures. (
ws-serverhas no lint script, so there was nothing to run there. I generated the Prisma client first withpnpm db:generate, otherwisecheck-typesfails on main as well.)New
handleMessages.submitAnswer.test.tsuses a fake clock (vi.useFakeTimersonDate) and a mocked Redisget:timeMs: 0,-500,19999and"nope"all give the same server-measured elapsed time and pointsquestionId, missing current-q marker and a garbage deadline value are rejectedI also added an assertion to the existing
quiz:endtest thatcloseQuestionWindowruns beforedrainSession. As a sanity check I put the oldhandleMessages.tsback with the new tests and 10 of them fail, with the new handler all 55 pass.I did not run the full stack with Docker and a real Redis or play a quiz with several participants. The Redis reads are covered with mocks only.
Notes
quiz:next-questionbroadcasts the question beforestartQuestionTimerwrites the deadline keys. An answer landing in that few-ms gap would be dropped. A human can't click that fast, but flagging it. I did not reorder those calls in this PR.if (!participantId) break), the client gets no error message. I kept it that way since the issue says "ignore".timeMs; it is just ignored now, so I leftLiveQuiz.tsxalone.participantIdin the message is still taken from the payload rather than from the socket. That is a separate problem from this issue and I left it out.