Skip to content

V0.3.0 - #3

Open
alexanderilyin wants to merge 127 commits into
mainfrom
v0.3.0
Open

alexanderilyin wants to merge 127 commits into
mainfrom
v0.3.0

Conversation

@alexanderilyin

Copy link
Copy Markdown
Member

No description provided.

alexanderilyin and others added 30 commits September 23, 2026 11:53
- Main menu: More games opens prototypes/
- CPU wording: a/an before 8, 11, 18, 80s
- Master dice: mastered facts rarer (weak facts ~70% of rolls)
- Tidy pickPracticeFacts
- CLAUDE.md for the project; docs/v0.3.0-plan.md

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Legend: half teen x 2-9, half Tricky facts; needs 12x12+
- Pair dice: big rectangles rarer on small boards; near the end fall
  back to small facts that fit instead of ending half empty
- Test: Tricky/Master/Legend game length; v0.3.0 log started

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ents, schedules

- server/store.js: node:sqlite storage (users, schools, classes, rosters,
  progress, homework, parent links, scheduled tournaments)
- server/auth.js: dev provider (HMAC JWT) and Keycloak provider (RS256 via
  JWKS); identity keeps first name, last initial, role, email domain only
- server/accounts.js: roles and rules; Cadence Park (@iusd.org) school
- HTTP API; hosting a class needs a teacher; signed-in students join live
  classes under their account name without the OpenAI name check
- progress.js: activity log, merge, fact summary, homework goals

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- js/auth.js: dev sign-in, Keycloak PKCE ready,
  per-account save, first-use merge of the device's guest save, sync
- account screens: sign-in dialog (guests always welcome), student
  homework and classes, teacher roster/stats/homework/tournaments,
  parent view with extra homework, live tournament banner
- hosting a class needs a teacher; activity log for stats and homework
- docs/privacy.md

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- apps: multiplication, seven other games, more-games hub, server (ESM)
- packages: engine, progress, ui, game-kit, classroom, auth
- game.js split into section modules with an ordered run() phase
  (pixel-identical before and after on 11 screens)
- homework only offers what the child has unlocked; Play starts it
- RFC 9457 problem details and security headers on the server
- CI (lint, test, build, strict docs build; pinned actions), Dependabot
- MkDocs Material docs site with screenshots, ADRs, CHANGELOG

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The other games get the main game's feel: a wallet with answer, streak,
win and practice bonuses; a shop with unlock chains (Medium then Hard,
20-question then Expert practice, Home multiplayer); achievements with
the shared banners (@blockout/ui/rewards); stats per question.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
E: Version '2.40.0-*' for '1password-cli' was not found
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hing

decodeURIComponent ran outside the request handler's try, so one request
like GET /%E0 threw an unhandled rejection and stopped the server. Decoding
now answers 400, route() runs inside the try, readJson rejects on a cut-off
body or after 10 s (408), and the entry point logs and exits on anything
unhandled so the process manager restarts it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-in and HTTP

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… LAN addresses in production

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The theme script moves from an inline <script> into theme.js (emitted by
blockoutPage(), served from memory in vite dev), the icon sprite uses the
display presentation attribute, and the two style= strings in innerHTML
are set from script instead. A Playwright pass over all 11 apps found no
violations or page errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Room codes came from Math.random (predictable) and class and parent codes
from a byte modulo 31 (slightly biased). Both now use crypto.randomInt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Token buckets per address and per account (apps/server/src/ratelimit.js,
in memory for now). Code guessing is limited by counting wrong codes, not
tries, because a whole school often shares one address. Live-update
streams answer 404 for a wrong key, like a missing class, so they can't be
used to find codes. TRUST_PROXY sets how many proxies to trust for
X-Forwarded-For.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…TERM

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
JSON lines with OpenTelemetry field names in production, short text lines
at a terminal. One record per request (route pattern, status, time) and an
audit record per account change, by id and role only. Names, codes, keys
and tokens are never logged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…or once a minute

Every API request used to update the users row (role, name, last seen).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ccess tokens

The browser renews an access token a minute before it expires, and once
after a 401, before giving up and signing out. The auth config is fetched
without the token so renewing doesn't recurse. Dev tokens last 12 hours.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A live room's dice bag held closures, so a room couldn't be saved as JSON.
diceState(size, sides) is the bag's state and diceFromState(state, rng)
rolls from it (using up rare faces in the state itself); createDice is
both together. Rooms keep the state and roll with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every account rule is async and awaits its store calls (the sqlite store
still answers synchronously), so a store over the network can replace it.
Schools are cached for a minute for sign-ins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/store/ holds the stores behind one async interface: memory (tests, a
run without a database) and the sqlite file store. test/store.contract.js
checks any store the same way. markOpened now opens a schedule only once.
BLOCKOUT_STORE picks the store.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
deploy/data/install.sh installs a one-member MongoDB replica set and Valkey
with random passwords in Secrets (namespace blockout-data, or blockout-dev
for development); forward.sh forwards both and prints the URLs. The
data/ ignore rule now only covers the repo root's data/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/store/mongo.js passes the same store contract as the memory and sqlite
stores; numbered migrations (indexes, the first school) run once, with a
lock so servers starting together wait for each other. Production requires
BLOCKOUT_MONGO_URL. CI runs the store tests against MongoDB and Valkey.
ADR-0006 supersedes ADR-0003.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An event stream answered with any error (a restarting or busy server, not
just a missing class) ended the class for the student. Now the page falls
back to polling, which retries on errors and stops only on a 404. First
tests for @blockout/classroom.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
alexanderilyin and others added 10 commits October 17, 2026 05:55
🏫 Class beside Play (or a #class= link): join with the code, wait for the
teacher, then play the whole class's rolls, pairs and tournaments, with
your partner's moves shown as they make them. Results stay up until the
next game; the app rejoins after it was closed; scheduled tournaments
show on the menu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@blockout/progress/grownups: how activity, homework, codes and planned
tournaments read, the homework form's choices and goal, whose homework
comes off. @blockout/progress/hosting: the host's choices and settings,
its buttons, roster, pairs cards, podium and report. The web game's
account and host screens and the admin page use them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Teachers: My classes (classes and their join code with a QR code, roster,
class homework, a student's page, their grown-ups, planned tournaments,
teacher codes) and hosting a live class from the phone. Parents: My kids
(link a child, their page, delete their account). Admins: accounts,
teacher codes and schools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One small task a day on the menu, picked from the player's own Times
Table: facts that need practice, then the next table to master, then a
taste of the next (still locked) table, else a game that stretches a
little. Worth 30 points, 10 more for each day in a row, with two new
achievements. Shared rules in @blockout/progress/daily; in the web game
and the phone app, for guests too. Homework shows beside it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bles

A round of every table could tick it off without asking about them. The
phone's card also says what was done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Homework (📚, now for guests too) starts with today's practice, then the
homework from a teacher or grown-up; its badge counts today's task until
it's done. In the web game and the phone app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
During a game or practice round, bonuses, achievements and stickers are
held and listed on the results screen, the same kind stacked into one;
urgent pop-ups (a skipped roll, a failed send) still show. Pop-ups
elsewhere stack repeats too, and the game's results scroll on a small
phone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A helped answer was still "first try" when help came before any wrong
answer, so it earned the first-try, speedy, streak and timer bonuses.
Match.firstTry (no wrong answer, no help) now decides, for games,
practice and class games alike, and Speedy, Big block and Twelve times
need the answer right first time. The phone app's backspace is red.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Single player climbs levels instead of buying boards and difficulties:
level n is an n×n board with an n-sided die (1–12), then challenge levels
(16×16, Tricky, 20×20, Master, Legend, 24×24). Beating the CPU with 80 %
of answers right first try moves you up and unlocks the level's board and
difficulty ids, so everything keyed on them keeps working. Free play (pick
difficulty and board) opens at Level 6. The menu shows a Journey card and
"Play Level N", on the web and in the phone app.

The engine plays any die from 1 to 12 sides; small dice roll evenly. Old
saves start on the level their unlocks and wins reach (journeyV1). ADR-0010.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 59c99cb8-cb1c-467a-a8df-2e04f2da9335
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

alexanderilyin and others added 19 commits October 17, 2026 07:49
… instead of emojis

Practice is free and goes ×1 to ×12: level n is the ×n table, with about
one question in five reviewing the tables before. A round with 80 % right
first try moves you up and unlocks the next table (recordPracticeLevel, in
Practice.finish). Free practice opens at Practice level 2. Tables are no
longer sold; old saves start on the highest table they own
(practiceJourneyV1). On the web and in the phone app. ADR-0011.

The menu's Journey, Free play, Learn, Expert and Play buttons and the Sign
in chip use Lucide icons (mountain, sprout and brain added to the sprite).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…0 and 20

Each board from 1×1 to 12×12 has three stages: win with 80 % of answers
right first try, then also without "Help me count", then also claiming more
than half the board (42 levels with the six challenges). Each times table in
Practice has three rounds of 5, 10 and 20 questions (36 levels); reaching the
longer rounds unlocks them for Free practice, so they leave the Shop. Saves
from 0.6–0.7 move to the first stage of their board or table (journeyV2,
practiceJourneyV2). ADR-0012.

Practice's Journey asks facts up to the table's dice again (6 for ×1–×6, 8
for ×7–×8, 12 from ×9), not every table up to × 12.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and its timers are reached, not bought

Each times table has five rounds: 5, 10 and 20 questions in Learn, then 10
in Expert, then 10 in Expert against a timer that gets shorter along each
difficulty's tables (30 s, 10 s, 5 s). The round sets the length, Learn or
Expert and the timer. Expert practice and its timers unlock for Free practice
with the first round that brings them, so they leave the Shop. Saves from 0.8
keep their table and round (practiceJourneyV3). ADR-0013.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…answer right first try

1×1, 2×2 and 3×3 have one stage each (three stages start on 4×4, 36 levels),
and a board's stage 3 asks for every answer right first try instead of more
than half the board, which was mostly luck: players won and still got stuck
on "So close! Now claim more than half the board". Saves keep their board
and stage (journeyV3). ADR-0014.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
rollForBoard rolls again for a roll with no 1 that hasn't come up yet this
game (3 × 4 and 4 × 3 are the same rectangle), then for one with no 1, then
takes whatever fits, so the last 1-wide gaps still get their 1 × N. Bags
remember their rolls (bag.seen, saved with a live class's bag); rejected
rolls don't use up rare faces. Games are about a fifth shorter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…at fits" and click to place

Practice's second mode is Auto instead of Free practice: no tables or
difficulty to pick; Practice.autoQueue asks your facts to practice (missed,
not mastered since), then the facts that need practice, then Learning and
not seen yet facts of the reached tables mixed, repeating weak facts rather
than asking mastered ones (those only once everything reached is mastered).

Four settings are gone, in the web game, the phone app and live classes:
every game rolls in the app, only rolls what fits, lets the first rectangle
go anywhere, and places by drawing or Auto. Click to place owners get Auto
(or their points back if they had it); Real dice and the "No room" options
are refunded (settingsGoneV1). Settings.enforce drops the gone settings and
choices but keeps everything else (the Wardrobe's cosmetics). Old invite
links still open. The engine loses the corner rule and tap-to-place.

Fix: homework for a times table started a Journey round, and homework for a
game played the Journey's level; table homework now starts its round, and
game homework plays in Free play.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… ones for points

Like the cards of a level-up: candidates() lists the tasks today's practice
could deal (tables that need practice, a table to master, a taste of the
next, your Journey's level, from 6×6 a board or a challenge), the most
useful first; pickChoices deals it and two at random. Doing any one does
today's practice. reroll() deals three others for 10 points, then 20, 40…
that day. A save with one task keeps it as one of the three.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Once the mechanics are learned the game gets out of the way: fast CPU
explanations on 4×4, auto roll on 5×5, auto placing on 6×6, instant CPU
turns on 7×7 (with "Press a button after CPU turns" unlocked). They're
Journey items now, not bought; recordLevelGame returns the settings to turn
on (switches) and the clients do, with a toast, and they can be switched
back in Settings, where an unreached one shows its lock. Owners keep them;
an old save's start level counts only boards and difficulties.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The twelve browser apps become apps/web, a Vite multi-page app:
index.html is the multiplication game at /, each <name>/index.html a
page at /<name>/, with its code in src/<name>/. The seven operation
games share src/operation.js, which reads the game from
<body data-variant>. Three apps remain: web, server and mobile.

npm run dev now starts one Vite server and the server (it had 13
persistent tasks, more than Turborepo's default concurrency of 10).
The server serves apps/web/dist as plain static files, and the image
copies that one folder. URLs don't change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Development on deepspacecartel.com signs in through one Keycloak for the
cluster (rts-terraform, realm dev) instead of the chart's own, which
values-workspace.yaml turns off.

- The dev container's keycloak-clients Feature creates Blockout's
  clients (.devcontainer/keycloak-clients.json) and, on every start,
  adds this workspace's Coder URLs for ports labelled [sign-in],
  dropping those of deleted workspaces.
- Admins are the blockout client's admin role: the realm is shared, so
  realm roles no longer count.
- An unverified email address counts as none: anyone can register any
  address, and the domain picks a school.
- .env.1password no longer needs the Google secret (it's the platform's).
- WIP.md: where this stopped, and the plan to replace the sync script
  with Terraform.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant