---
scope: Wordle Clone v1 — web application, solver engine, word bank
purpose: build-substrate
altitude: feature
updated: 2026-07-07T20:38
---

- (decision) Language: TypeScript everywhere — frontend, backend, solver. Single language, shared types.
- (decision) Frontend: React + Vite SPA. Backend: Node.js + Express REST API. Solver: standalone TypeScript script.
- (decision) Server is stateless. No database. Word bank is static JSON. Per-word player stats are NOT v1.
- (decision) API: GET /api/daily → {wordId}. POST /api/guess → {valid, colors?: [g/y/x], correct?: bool}. POST /api/play-again → {wordId}. Colors as single chars g/y/x.
- (decision) Guess request includes tryNo. Loss reveal: word returned in guess response when tryNo=6 and not correct. tryNo field future-proofs for post-v1 hints on attempts 5-6.
- (decision) Backend: Node.js + Express + TypeScript. Rust argument deferred — 3 endpoints, minimal concurrency, TS shared types win. Rewrite trivial if ever needed.
- (decision) Deployment: home Linux server, internet-exposed. Single Node.js process serving both API and static SPA.
- (decision) Monorepo: client/ (React+Vite), server/ (Express), solver/ (pre-compute script), shared/ (TS types), data/ (word bank JSON, solver output)
- (decision) Daily word: ID computed deterministically from date (days since epoch mod target count, EST/EDT). No server-side storage needed — same date always maps to same word.
- (decision) Play Again: Gaussian sample around skill metric to pick word difficulty. Center at player skill, variance TBD. No repeat-word prevention — target set is large enough.
- (event) spine finalized
