No description
Find a file
Joey Yakimowich-Payne 8220f1507e
feat(engine): PlayerAction + transferable-royalty preset (solo)
Feature 4a of post-epic-deferrals. Introduces the PlayerAction
surface — a turn-consuming event orthogonal to LegalMove — and one
preset that uses it to transfer royalty between friendly pieces
once per game per color. Solo-only for v1 (F4b will add the WS
protocol; F4c will add the UI).

Engine surface:
  - New module packages/chess/src/actions.ts exports PlayerAction
    (discriminated union; starts with 'transfer-royalty' kind),
    PlayerActionKind, and ActionResult {ok,error,reason}.
  - ChessEngine.performAction(action): ActionResult runs a parallel
    pipeline to applyMove: terminal-state guard → poll every
    active preset's performAction hook (first non-undefined wins)
    → handler returns ok=false => no turn consumption → handler
    returns ok=true => advanceTurnAfterMutation shared helper
    (factored out of applyMove) which handles HalfMovesThisTurn
    increment, shouldAdvanceTurn poll, onTurnStart fire, etc.
  - ActionResult error codes: NO_HANDLER, REJECTED, INVALID_TARGET,
    NOT_YOUR_TURN, GAME_OVER. Stable for future UI / protocol.

PresetDef additions:
  - performAction(ctx): ActionResult | undefined — first non-
    undefined wins. Handlers validate + mutate state + return.
  - transformRoyalPieces(ctx, current): EntityId[] — a POST-union
    transform on the accumulated royal set, letting a preset
    reassign rather than append. Used by transferable-royalty to
    swap transferredFrom -> transferredTo.

Preset: transferable-royalty
  - category 'king'; incompat with suicide-chess + capture-all
    (both empty the royal set).
  - State: transferredFrom/To keyed by color — one-shot per color.
  - performAction validates: fromPiece alive, currently royal,
    toPiece alive, same color, not already royal, not already
    transferred. Returns INVALID_TARGET / REJECTED on failure,
    ok:true on success.
  - transformRoyalPieces swaps old royal for new in the engine's
    royal resolution; defensively drops dead ids so a transferred
    royal that later died doesn't linger.

Tests:
  - transferable-royalty.test.ts: 20 tests covering registration,
    happy path, once-per-game cap, all INVALID_TARGET paths,
    turn consumption, composition with knightmate-rules and
    piece-hp.
  - engine.performAction.test.ts: 7 tests covering NO_HANDLER,
    GAME_OVER guard, first-match-wins, shouldAdvanceTurn veto,
    performAction + applyMove interleaving.
  - presets.test.ts: EXPECTED_IDS bumped; symmetry + dangling-ref
    audits still green.
  - capture-all.ts: reciprocated incompat with transferable-royalty
    (symmetry audit).

Verification: 1699 tests passing (was 1671, +28). Typecheck + lint
clean. No regressions.

Plan: .sisyphus/plans/post-epic-deferrals.md F4a complete.
F4b (WS protocol) and F4c (UI) remain.

(Agent hit 200-tool-cap near the end; orchestrator reconciled
a missing defaultKingRoyals helper + 2 test setups that triggered
insufficient-material draws + symmetric incompat declaration.)
2026-04-21 11:30:24 -06:00
.github/workflows chore(root): scaffold monorepo — Phase 0 complete 2026-04-16 13:32:21 -06:00
.sisyphus plan(sisyphus): lock 7 design decisions for post-epic deferrals 2026-04-21 10:33:14 -06:00
docs docs(adr): T4 scripted modifiers forward-design 2026-04-19 21:09:52 -06:00
packages feat(engine): PlayerAction + transferable-royalty preset (solo) 2026-04-21 11:30:24 -06:00
scripts feat(rete): add replay engine + state-hash determinism verifier (P3.3) 2026-04-16 15:25:13 -06:00
.gitignore chore(sisyphus): Rule Variants Final Verification Wave — all reviewers APPROVE 2026-04-21 10:15:18 -06:00
eslint.config.js feat(engine): damage-resistance modifier descriptor 2026-04-18 22:22:31 -06:00
lefthook.yml chore(root): scaffold monorepo — Phase 0 complete 2026-04-16 13:32:21 -06:00
LICENSE chore(root): scaffold monorepo — Phase 0 complete 2026-04-16 13:32:21 -06:00
package.json feat(rete): add replay engine + state-hash determinism verifier (P3.3) 2026-04-16 15:25:13 -06:00
playwright.config.ts fix(rete): inject clock into EventLog; use tsc for DTS; fix cycle.test.ts private access; add Playwright worker limit 2026-04-16 18:25:49 -06:00
README.md chore(root): scaffold monorepo — Phase 0 complete 2026-04-16 13:32:21 -06:00
tsconfig.base.json chore(root): scaffold monorepo — Phase 0 complete 2026-04-16 13:32:21 -06:00
tsconfig.json chore(root): scaffold monorepo — Phase 0 complete 2026-04-16 13:32:21 -06:00
vitest.workspace.ts chore(root): scaffold monorepo — Phase 0 complete 2026-04-16 13:32:21 -06:00

@paratype

A Doorenbos-style Rete II rules engine for TypeScript games, with an authoritative WebSocket chess demo.

Packages

Docs

Getting Started

bun install && bun run check