This repository holds the Grand Cross rules engine and word list, published to
public npm as @grand-cross/engine and @grand-cross/words, plus an unpublished
offline tuning harness.
These rules apply to every repository in the fells-code org. Repo-specific guidance may extend them but must not contradict them.
- Commit and open PRs solely under the repository owner's identity. Never commit under an agent or assistant identity.
- Never attribute work to an AI assistant: no
Co-Authored-By: Claude(or any assistant) trailers, no "Generated with" / "Created with Claude" notes, and no assistant branding or emoji anywhere in commit messages, PR or issue titles and descriptions, changesets, code comments, or docs.
- Comment only when the code genuinely needs explaining: a non-obvious reason, a gotcha, or an invariant. Never narrate what the code plainly does.
- Every
TODO/FIXMEmust reference a ticket, e.g.// TODO(#123): .... Do not leave a bare TODO. If no ticket exists, create one first.
- Conventional Commits (
feat:,fix:,chore:,docs:,ci:,test:). - Descriptive branch names (
feat/...,fix/...); never aclaude/or other tool-generated prefix.
- No em dashes in commit messages, code comments, PR or issue text, changesets, or docs. Use a comma, parentheses, or a separate sentence.
- All code quality checks must pass before you open a PR or call the work done: tests, type checks, and build. Run them and report the real output; do not open a PR while any check is failing.
- Match the surrounding code's style, naming, and comment density.
The engine is pure. No Math.random, no Date, no I/O, no ambient state.
Every transition is a function of (state, move).
This is not a style preference. grand-cross-api verifies every submitted score
by replaying the player's move log through this engine against a server-issued
seed. If the engine can observe anything outside its arguments, that replay stops
being reproducible and the leaderboard stops being trustworthy.
If a change seems to need the clock or a random number inside packages/engine,
pass it in as an argument or thread it through the seeded RNG instead.
The same reasoning is why this code lives in one repository rather than being copied into the client and the server. Two copies drift, and the drift shows up as honest games being rejected.
Bump RULES_VERSION in packages/engine/src/version.ts for anything that
changes how a move log plays out: the piece distribution, the escalation curves,
the scoring table, or move legality. Stored runs carry the version they were
played under so a mismatch is detectable rather than silent.
Rendering, wording, tooling and comment changes are not rules changes.
packages/grand-cross-tuning is a measurement instrument, not shipped code. Its
job is to tell you whether a rules change made the game better, and its numbers
are only meaningful if the bot stays a stable yardstick. Changing the bot and the
rules in the same commit makes both unmeasurable.
Search depth is capped at the three pieces a real player can see. Raising it would let the bot plan against letters a human cannot know, which would make every measurement optimistic.
After making code changes in this repository, verify them by running:
npm run typechecknpm testnpm run build
Notes:
- The tuning suite plays real games and takes around ten seconds. That is expected, not a hang.
- Releases go through Changesets. Run
npm run changesetas part of any change that affects a published package, and let CI publish. Never publish from a developer machine. - The engine and word list are versioned together (
fixedin the changeset config), so consumers cannot install a mismatched pair.