Bare HTTP + WebSocket bootstrap (health check route, ws upgrade, connection logging) with matchmaking/ and rooms/ reserved but unimplemented. No matchmaking logic yet. Also adds root README.md (project overview) and AGENTS.md (architecture map, code style, git workflow).
70 lines
3.4 KiB
Markdown
70 lines
3.4 KiB
Markdown
# AGENTS.md
|
|
|
|
Context and working agreements for anyone (human or agent) changing this
|
|
repo. See `README.md` for what the product is; this file is about how we
|
|
build it.
|
|
|
|
## Architecture
|
|
|
|
`frontend/` (Vite + TypeScript, **no UI framework** — plain DOM) is
|
|
layered bottom-up; each layer only imports from the ones below it:
|
|
|
|
```
|
|
types.ts shared domain types (Token, CategoryState, ThemeDefinition, ...)
|
|
data/ static content: theme palettes, code samples
|
|
engine/ pure, DOM-free utilities: tokenizer, color math, particles, sound
|
|
game/ canvas rendering, scoring, panel/result DOM views, ThemeGuessGame
|
|
preview/ the pre-round "flash the real theme" renderer
|
|
ui/ composable DOM widgets (theme grid, preview countdown flow)
|
|
main.ts composition root — wires the above to the page, nothing else
|
|
```
|
|
|
|
New game logic goes in `engine/` if it doesn't touch the DOM, `game/` if
|
|
it does. Don't reach for React/Vue/etc.: the canvas rendering and the
|
|
handful of DOM widgets don't need a framework, and adding one is a
|
|
bigger discussion than a drive-by PR.
|
|
|
|
`backend/` is currently a bare HTTP+WebSocket bootstrap
|
|
(`src/index.js` + `src/ws/connectionHandler.js`) with `src/matchmaking/`
|
|
and `src/rooms/` reserved but empty (see the `README.md` in each). Plain
|
|
Node with ESM (`"type": "module"`), no framework, no TypeScript — keep it
|
|
that way until there's an actual reason to add one.
|
|
|
|
## Code style
|
|
|
|
- Frontend TypeScript runs under `strict`. Don't widen types to work
|
|
around an error without understanding why it's there.
|
|
- Small static string-keyed lookup tables are `Record<K, V>`, not `Set`/
|
|
`Map` — reach for `Set`/`Map` only for dynamic membership, non-string
|
|
keys, or when you need `.size`/iteration order/`.clear()`.
|
|
- Don't wrap a single expression in a named function just to name it,
|
|
unless the name is a stable public API, a callback whose identity
|
|
matters, or documents a non-obvious formula. Inline the rest.
|
|
- A property literally named `constructor` in an object literal typed as
|
|
`Record<string, V>` gets its value type silently widened by TS (see
|
|
`engine/tokenizer.ts`'s `toLookup`) — build such tables from an array
|
|
via `Object.fromEntries` instead of a literal when a key might collide.
|
|
|
|
## Git
|
|
|
|
- Trunk is `master`; keep it green (typechecks, builds, game loads).
|
|
- One short-lived branch per change, prefixed by intent:
|
|
`feat/…`, `fix/…`, `refactor/…`, `chore/…`, `docs/…`.
|
|
- Commit messages follow Conventional Commits:
|
|
`type(scope): summary` — e.g. `refactor(frontend): split game into modules`,
|
|
`feat(backend): add matchmaking queue`. Scope is usually `frontend`,
|
|
`backend`, or a package-relative area (`engine`, `ui`, `ws`, ...).
|
|
- Squash-merge feature branches into `master`; delete the branch after.
|
|
Keep history readable — no "wip", "fix typo", "asdf" commits on `master`.
|
|
- Frontend and backend evolve independently; a PR touching only one
|
|
should only bump/discuss that package's version and deps.
|
|
|
|
## Verification expectations
|
|
|
|
- Frontend change: `npm run typecheck` (or `build`) in `frontend/`, plus
|
|
an actual smoke test (open the dev server, play a round) for anything
|
|
touching rendering, input, or scoring — type-checking alone doesn't
|
|
catch a canvas drawn one line too high.
|
|
- Backend change: whatever's implemented must actually boot
|
|
(`npm start`) and the touched transport (HTTP route / WS message) must
|
|
be exercised manually or with a script; there's no test suite yet.
|