Skip to content

Roadmap

The journey from “useful module surface” to “stable API” happens incrementally, driven by real consumers.

  • Collection layer (src/games.js) — gameIdentity, isPrefix, mergeGames, attributed-annotation merging. Built when the multi-source merge demand emerged.
  • Replay engine (src/replay.js) — ReplayEngine, position hashing, FEN diffing primitives.
  • UCI engine client (src/engine/) — generic UCI protocol client + curated registry. Engine binaries are the consumer’s responsibility.
  • FEN parser (src/fen.js) — grammar + semantic validation (king count, pawn placement, castling backing, ep consistency), normalization, FenParseError.
  • Slice-based lifecycle (SPEC-lifecycle.md) — reactive system over cursor/tree/header/view/engine slices. Construction callbacks (onCursor/onTree/onHeader/onView/ onEngine) + game.subscribe(slice, fn). Mutators declare slice impact statically; views are fresh per fire; re-entrant mutation throws. Replaces the prior onPositionChange / onChange / start() shape.
  • Engine integration bridge (SPEC-engine-integration.md) — publishEngineInfo(id, snapshot) on Game; attachEngine (wires UCI events → slice with leading+trailing throttling at 500ms, opt-in analyze: true to drive analysis from cursor); autoAnalyze standalone for headless/shared-engine cases; UCI translation primitives (parseInfoLine, uciPvReplay, sanToUci, uciToSan, buildPositionCommand).
StepWhatDriven by
1API audit — list every method on Game, decide which are publicfirst stable external consumer
2Strip legacy fields — remove tournamentSlug from FIELD_SCHEMA and any other carry-over from earlier shapesstep 1 reveals these
3Editor state primitives — refine the setComment / toggleNag / promoteVariation surface so Motif’s editor UIs have clean state operations to callfirst consumer that wants editing UI
4NAG kit as a stand-alone module under src/nag/clean extraction; KeySquares already wants this
5Variant support — parameterize the position engine so non-standard chess works without forking the runtime. Pluggable legality engine (chess.js default; chessops / fairy-stockfish-rules / custom for variants) lives behind Motif’s GAME contract — consumers see no API change.Rabbit’s variant work
6PGN header editing — apps that load a PGN need to edit headers (Event, Date, players, result, etc.). PGN headers are chess data, same category as comments, so this belongs in Tabia. A prior setRecord(r) (whole-record replace) was removed because it had no consumers and silently allowed startFen mutation, which corrupts the tree. The right shape is targeted — e.g., setHeader(key, value) + removeHeader(key), with startFen excluded from settable keys since it’s structural and fixed at construction. Design the right surface when the first consumer needs it.first app that wants header editing
7Save-state tracking — “has this game been modified since last save?” Apps need it for unsaved-changes indicators, prompt-before-close, autosave triggers. A prior dirty flag + isDirty() was removed because it had no consumers, no markClean() to complete the round-trip, and every new mutation method had to remember to flip the flag (maintenance burden). The right shape includes both isDirty() AND markClean() (or markSaved()), possibly a dirty-changed event so consumers don’t poll, and consideration of whether to distinguish data mutations from preference toggles. Until then, apps can derive it themselves by flipping a local flag in their onChange callback.first app that wants unsaved-changes UX

The order is “extract when there’s a consumer,” not “extract on schedule.” Many steps can be partially deferred.

Keyboard binding is not Tabia’s concern — the canonical keymap and the binder both live in Motif, since they cross subsystems (Tabia actions + Rabbit board + Motif components) and involve DOM event handling that’s foreign to a data-only library.

Tabia does not render boards or own UI components — that was walked back when board.js was removed. Today:

  • Board renderingRabbit (SVG, framework-free)
  • UI components (MoveList, BranchPopover, Toolbar, FenArea, etc.) → Motif (Lit web components)
  • Integration examplePROMOTE/dev/ (composes all three)

If something feels like it belongs in Tabia but it’s really UI, it belongs in Motif.

  • A new app imports { createGame, ReplayEngine } from Tabia, pairs with Rabbit for rendering and Motif for UI chrome, and gets a working interactive viewer in <100 lines of app code.
  • A prep-reader app uses Motif’s <motif-move-list> wired to Tabia’s Game, getting a polished move list for free.
  • A future opening trainer imports { createGame } only (no UI), uses it for predict-the-move logic, and builds its own UI.
  • A future game-archive app imports the collection layer and gets duplicate detection + attributed-annotation merging without writing any of it.
  • Rendering. Tabia is data only. Renderers are sibling libraries.
  • UI components. Motif’s territory.
  • App shell. Modal scaffolding, tab systems, route management — consumer responsibilities.
  • A general framework. Tabia provides building blocks. Each app provides its own scaffolding.