Constitution

3 min read

Constitution

These rules apply to the whole system. If an article appears to contradict one of them, the constitution wins and the article is to be treated as defective.

1. Architecture

1.1. The codebase is split into three layers with a one-way dependency direction: engine → ai → ui. The engine must not import from ai/ or ui/. The AI must not import from ui/. No layer may reach backwards.

1.2. The engine and the AI are pure: no DOM access, no window, no localStorage, no timers, no randomness except through an injected RNG function. They must be runnable in Node with no shims.

1.3. All game state is immutable. Functions return new state objects; they never mutate their arguments. Board arrays are typed readonly.

1.4. There is no global mutable singleton. The application owns exactly one state variable, held in the composition root (src/main.ts), and passes it down explicitly.

2. Dependencies

2.1. Zero runtime dependencies. The shipped bundle contains only first-party code. No UI framework, no utility library, no icon package, no web fonts fetched at runtime.

2.2. Dev dependencies are limited to the build and test toolchain: Vite, TypeScript, Vitest, jsdom, ESLint and Prettier. Adding anything else requires it to be justified in the article that introduces it.

2.3. The application makes no network requests at runtime. It must work fully offline after the first load, opened from file:// or from a static host.

3. Code quality

3.1. TypeScript runs in strict mode. any, @ts-ignore and non-null assertions (!) are forbidden; narrow types properly instead.

3.2. Every exported function and type carries a TSDoc comment stating its contract, including what it does on invalid input.

3.3. No file exceeds 300 lines. No function exceeds 50 lines or a cyclomatic complexity of 10.

3.4. Naming: PascalCase for types, camelCase for values and functions, SCREAMING_SNAKE_CASE for module-level constants, kebab-case for file names and CSS classes.

3.5. Errors are handled explicitly. Invalid engine input throws a typed InvalidMoveError; the UI never lets an invalid call happen in the first place, and corrupt persisted data is discarded rather than trusted.

4. Determinism and correctness

4.1. Given the same inputs, the engine always returns the same output. Any randomness in the AI is supplied by an injected rng: () => number so tests can seed it.

4.2. The Hard AI must never lose. This is not an aspiration but a tested invariant: an exhaustive search over every legal opponent line must end in a win or a draw for the AI, from both seats.

4.3. Cell indices are 0..8 in row-major order. Index 0 is top-left, index 4 is centre, index 8 is bottom-right. This numbering is used everywhere — engine, AI, DOM data-index, tests.

5. Accessibility

5.1. The game must be fully playable using the keyboard alone, with no mouse or touch.

5.2. Every interactive element is a real focusable control with an accessible name. Visible focus indication is never removed.

5.3. Colour is never the only signal. A win is conveyed by text in a live region and by a shape or outline change, not by colour alone.

5.4. Text and interactive elements meet WCAG 2.1 AA contrast (4.5:1 for body text, 3:1 for large text and UI boundaries) in both light and dark themes.

5.5. All animation is suppressed when prefers-reduced-motion: reduce is set.

6. Privacy and licensing

6.1. No analytics, no telemetry, no cookies, no fingerprinting, no third-party scripts of any kind.

6.2. Persisted data is limited to game settings and aggregate scores. Nothing that identifies a person is stored.

6.3. The project ships under the MIT licence, with a LICENSE file at the repository root.

7. Scope boundaries

7.1. Out of scope for this specification: online multiplayer, accounts, boards larger than 3x3, server-side anything, mobile app packaging, sound effects, internationalisation beyond English strings kept in one module.

7.2. Anything not specified is decided in favour of the simplest implementation that satisfies these rules.

Discussion

0 comments

No comments yet. Start the discussion.