No description
T3 follow-up addressing 4 of the 6 e2e fixmes (T29). Ships the engine
wiring that was deferred in T3's original scope per the implementation
retrospective.
New triggers.ts exports four dispatchers + an HP snapshot helper:
- fireOnTurnStartHooks(engine, whoseTurn): walks every piece of the
given color, runs each OnTurnStartHooks entry as a primitive list.
- fireOnCaptureHooks(engine, attackerId): runs OnCaptureHooks for the
attacker piece. attackerId is captured in onBeforeMove (engine.moveLog
isn't yet populated when onAfterMove fires, so we can't read from it).
- fireOnDamagedHooks(engine, preMoveHp): compares post-move Hp facts
against a pre-move snapshot; pieces whose Hp dropped (or whose Hp
fact was retracted = died) get their OnDamagedHooks fired.
- fireConditionalHooks(engine): re-evaluates every ConditionalHook's
condition against current piece state; runs the matching then/else
branch.
- snapshotHp(session): freezes Hp facts at the supplied phase for the
on-damaged dispatcher to compare against.
Conditions supported: attr-lt, attr-gt, attr-eq, always, never (matches
the ConditionSpec union from T14).
Each dispatcher recurses through nested primitives via the same
PRIMITIVE_REGISTRY lookup the descriptor applier uses, with the runtime
depth cap (8) as a backstop. Trigger evaluation has no parent
descriptor — synthesised __trigger__ ref fills the contract.
Integration in apply.ts (__modifier-profile-integration__ preset):
- onBeforeMove: snapshots Hp + the attacker pieceId (when isCapture).
Both stored in WeakMaps keyed by engine so concurrent engines
(server-authoritative + client-predicted) keep independent state.
- onDamage: NEW absorb-damage-with-attribute branch BEFORE the existing
DamageResistance branch. When AbsorbDamageAttr+AbsorbDamageRate are
set on the target, incoming damage spends the attribute first;
full absorption short-circuits with consume:true died:false.
Partial absorbs fall through (same documented limitation as
partial DamageResistance).
- onAfterMove: now runs computeAuraFacts (T28, unchanged) + four
trigger dispatchers in order:
fireOnDamagedHooks → fireOnCaptureHooks → fireConditionalHooks
→ fireOnTurnStartHooks (for the next-mover's color)
7 vitest scenarios in triggers.test.ts cover each dispatcher
including the absorb-damage shield (3 charges → 0 → fall through).
|
||
|---|---|---|
| .github/workflows | ||
| .sisyphus | ||
| docs | ||
| packages | ||
| scripts | ||
| .gitignore | ||
| eslint.config.js | ||
| lefthook.yml | ||
| LICENSE | ||
| package.json | ||
| playwright.config.ts | ||
| README.md | ||
| tsconfig.base.json | ||
| tsconfig.json | ||
| vitest.workspace.ts | ||
@paratype
A Doorenbos-style Rete II rules engine for TypeScript games, with an authoritative WebSocket chess demo.
Packages
packages/rete— Rete II engine corepackages/chess— Browser chess demo (React + Vite)packages/server— Authoritative Bun WebSocket server
Docs
- SPEC.md — Engine specification
- PHASES.md — Development phases & perf budgets
- RULES.md — Chess rule presets
- PROTOCOL.md — WebSocket message protocol
Getting Started
bun install && bun run check