Repository navigation
Conversation
…r data on sign-out The daily challenge and own text get their ids in @typing-race/curriculum (dailyId/isDailyId, ownTextId/isOwnTextId, isFreePracticeId). The server does not know these ids and answers unknown_scale, which the outbox keeps and re-sends forever, so sync stayed "pending" and sign-out stopped. - submitAttempt stores free practice locally but never queues it - free practice an older build queued is dropped from the outbox - wipeLocalIfSynced/forgetLocal also remove typing-race:daily* and typing-race:sprint-best:* (the guide-seen flag stays: it is a per-browser UI hint with no learner data) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Own text records under ownTextId (yq.owntext), not the stock real-text id, and is labelled "Свій текст"/"Own text" on the result screen - Free practice (daily and own text) is ignored by mastery/progress, key confidence and weak spots (derive), the coach's last attempt, XP, the streak and daily-goal minutes (gameStats), and the result reward - /own hides the practice/test toggle (always practice); "Again" on an own-text result re-runs the same text from memory, or opens /own when it is gone - The "cut to N characters" notice shows on the pre-start card - Daily done-today and the day count are kept per language Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t, pause on blur - Score from packages/metrics (computeMetrics) over the keystroke log, so accuracy is over all keystrokes and a wrong letter left in the line counts. The engine gains stop(): a time-boxed run ends as completed and finish() returns the log so far. startSprint gates the input at 60 s of typing time, so no key after the minute is logged; the time is capped at the minute, or the time used if the line ends sooner (stated on /formulas in uk and en). - A pre-start card with the exercise layout check (shared useLayoutCheck and LayoutStatus extracted from PreStart); Start stays disabled on a mismatch. Play Mode during the run; the typed-layout notice too. - Leaving the window pauses the clock; Escape pauses and resumes; the exercise pause dialog resumes. - The line comes from seededRandom with a fresh seed per sprint (ADR 0005). - The countdown starts from SPRINT_SECONDS, derived from SPRINT_MS. - The locked line uses "ви". The line uses the text-size setting, hides its count-up time and its ninety word stations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… closes every tooltip Testers could not find the new modes: Sprint sat behind /review, Own text behind a Map key, Daily was a small link. Home now has a sixth, quiet row of four cards (glyph, name, one line). Sprint is dimmed until Stage 2 opens and Mistakes until there are weak spots; their Tip says how they open. Daily shows a "готово" chip once cleared today. The row is a lazy chunk so the initial JS stays under 150 KB, and a coach-mark step points at it. On screens up to 900 px tall the session steps become one line of chips and the vertical rhythm tightens, so Continue's Start button is never clipped (it already was at 1024x768). The status bar's stats, the layout switch and Home's weak-spot chips now use the shared Tip, so Esc hides their bubble. Daily's "Днів поспіль" becomes "Викликів поспіль" so it no longer reads as the Streak. CoachMarks' keydown listener joins the seam exemptions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ground note Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CoachMarks listened for keydown on the document directly, which turned tools/architecture.test.ts red on main. The capture-phase listener now lives in apps/web/src/seams/keys.ts (takeKey), imported from its own file so it stays in the lazy CoachMarks chunk. Behaviour is unchanged: Esc closes the guide wherever focus is and the screen never sees it; Enter still steps on from the bubble. A component test pins both down. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The results' "race again" always called quickMatch, so friends from a
private room were thrown into public quick match. Now a private room's
rematch opens a new private room and broadcasts its code on the old
room's channel ('rematch' event, allowed by the existing realtime
policy); whoever is still on the results sees "<name> calls everyone to
another race" and the button becomes «Приєднатися», which joins by code.
If that room cannot be joined, a new one is opened instead. Quick-match
results behave as before.
The decision and its execution are pure (features/race/rematch.ts) and
unit-tested, along with a Results component test.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After delete-account the server has no session left, so signOut's /auth/v1/logout answered 403 and the browser logged it. For a deleted account the stored session is dropped first; signOut then finds no token, makes no request, and still clears this browser and emits SIGNED_OUT. Ordinary sign-out still ends the session on the server. Tested with a real supabase-js client against an in-test fake fetch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…egrate/post-submission # Conflicts: # apps/web/src/features/exercise/PreStart.tsx
…grate/post-submission
…the keys seam Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
…, and the audit's copy gaps
Accessibility (D2):
- The status bar's Level, Streak and Race Rating, the rail's Season card, Home's weak chips and
Profile's badges are named groups/items whose name says the value; the stage's scroll area is a
named region ("Вміст екрана").
- e2e: expectFocusablesNamed reads Chromium's accessibility tree and fails on any focusable node
without a role or a name; viewport-1024 and the new viewport-390 run it on every screen.
Phone, 390x844 (D3):
- /about gets a phone layout: one column, type sized to the screen, the hero card and lede no
longer cut (scrollWidth 462 -> 390). The method cells no longer stack on top of each other:
the shell's .stage grid placement leaked onto them at every width.
- The bottom dock spans the full width with five even tabs; the rail's side tooltips are hidden
on a phone.
- Sprint, Daily and Own text ask for a physical keyboard on a phone like every training route
(Sprint's header overlapped itself at 390).
- e2e/viewport-390.spec.ts: 14 routes, no sideways scroll, nothing cut, axe, named focus stops,
and the dock's even spacing.
Copy and logic:
- First run: picking a language switches the status bar's УКР/ENG at once, and the switch picks
in the flow (past step 1 it returns to the level step for the new layout).
- The attempt-type buttons keep one pair of names in both states: «Тренувальна спроба» /
«Залікова спроба».
- Academy: "mechanics" is decided per exercise, from whether its lines hold letter groups that
are not words; «the · you» and other word drills no longer say «Рядки не є словами».
- Result: after a cleared practice attempt the headline asks for the test only when «Далі»
opens it (practiceAdvice); otherwise it says the floor is cleared. Unit-tested.
- Profile badges: locked is a dashed edge, a lock glyph and muted text at >= 4.5:1, never
opacity, so axe passes in light, dark and low vision.
- e2e: the coach-line assertions follow the shortened result copy; the weak-transition line is
checked to name both fingers in full and not to overflow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… characters A jury-style audit found that one beforeinput carrying the whole line was graded as a passing test (9 949 SPM, "exercise mastered"), and that an automation tool's 4 264 SPM became the personal best. It also found that SPM counted wrong keystrokes, so 7 errors on a 59-character line read 12% above 60·N/T, outside the jury's ±10%. - SPM = 60 000 × min(correct character keystrokes, text length) / elapsed ms, which is 60·N/T on a finished attempt. Accuracy is unchanged. - The input seam takes an insertText of several characters as ignored (reason "burst"), like a paste. Compositions are still judged per char. - packages/metrics gives each attempt a verdict, metrics.implausible: "burst" when the median interval between character keystrokes is under 25 ms, "tooFast" above 1 500 SPM. Such an attempt is kept and shown as "Не зараховано: набір не схожий на ручний", but counts toward nothing: the progress fold, Academy progress, XP and level, streak and daily minutes, personal best, speed trend and session sizing all skip it through curriculum's typedByHand. finish-race reads the same verdict. DERIVED_VERSION is 2. - The Formulas page (uk, en), ADR 0003 (amendment), CONTEXT.md and docs/security.md state both rules. - The e2e harness types at 45 ms a key (about 1 300 SPM) and the CDP harness waits the same by default; the product has no test escape. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…te/post-submission # Conflicts: # apps/web/src/app/state/gameStats.ts # apps/web/src/features/result/reward.ts
…only) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… best The plausibility verdict from #43 now reaches the two free-practice modes that keep their own records: a daily attempt flagged implausible clears no day, and a sprint score carrying a verdict sets no personal best. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
race-real.spec.ts «a Ukrainian race with the system on US explains itself» failed whenever the private room drew the «Київ стоїть…» text (one of the four Ukrainian race texts, picked by `order by random()`): 1 try in 4, both tries 1 run in 16. Cause: the real-key harness looks up what the active layout prints on a physical key in the curriculum's QWERTY, which lists only the keys an English exercise uses. It has no BracketLeft, BracketRight, Backslash or Slash, and for those the harness fell back to the intended character. So «ї» pressed "on US" arrived as «ї», not «]». «ї» is a ЙЦУКЕН letter, and the race's foreign-letter watch rightly reset on it: R b ї d ␠ c is a streak of 2, never FOREIGN_STREAK (3), so the notice never came. The trace agrees: the engine counted all six keys (errors 1..6), the race never re-mounted, focus held, no page error, the notice slot stayed empty. The product is right: a real US layout prints «]» there. Fix: the harness knows what a real US layout prints on the four keys the curriculum leaves out, and throws on any other missing key instead of typing a character the active layout cannot produce. The key mapping is a pure `keystrokeOf`, pinned by tools/real-keyboard.test.ts: no ЙЦУКЕН key pressed on US types a Cyrillic letter, «Київ с» types R b ] d ␠ c, and those six keys reach FOREIGN_STREAK. All three failed before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Owner
Author
This branch was successfully deployed
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.
Draft. Do not merge before the judging results are published — the submitted version is the tag
submission-2026-10-05(80d8375); commits after the deadline do not count.One branch merging every post-submission draft so CI runs once on the combination:
insertTextignored; SPM > 1500 or median key gap < 25 ms → not counted); SPM = right keystrokes / time/aboutand the tab bar fit 390 px (new viewport-390 spec), first-run language switch, mode labels, Academy mechanics marker, practice advice matches «Далі», profile badges contrast, result e2eIntegration fixes on this branch:
PreStart.tsximport conflict (fix: free practice (daily, own text) stays local and counts toward nothing #38 × fix(sprint): published formulas, layout check before start, pause on blur #39)tools/architecture.test.ts— fix: private-room rematch, coach-marks seam, clean sign-out after deletion #41 routes the listener through the seamgameStats.ts/reward.ts: free practice AND hand-typing both gate XP, streak, daily goal and passes (fix: free practice (daily, own text) stays local and counts toward nothing #38 × fix(metrics): count only hand-typed attempts; SPM over the text's characters #43)Local: typecheck green, lint 0 errors (1 old warning), unit 903 passed / 3 skipped, initial JS 149.8 KB gzip of 150.
After merging:
pnpm supabase:functionssofinish-raceapplies the same plausibility verdict.🤖 Generated with Claude Code