refactor(ui): use asEntityId helper at piece-id boundaries

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
Joey Yakimowich-Payne 2026-04-19 13:53:45 -06:00
commit 9960ea96cf
No known key found for this signature in database
6 changed files with 79 additions and 7 deletions

View file

@ -1,6 +1,6 @@
import { MODIFIER_REGISTRY } from '../modifiers/registry';
import type { ChessEngine } from '../engine';
import type { EntityId } from '@paratype/rete';
import { asEntityId } from '@paratype/rete';
interface Props {
pieceId: number;
@ -8,8 +8,9 @@ interface Props {
}
export function ModifiedPieceIndicator({ pieceId, engine }: Props) {
const id = asEntityId(pieceId);
const isModified = MODIFIER_REGISTRY.list().some((descriptor) => {
return engine.session.get(pieceId as unknown as EntityId, descriptor.attrName) !== undefined;
return engine.session.get(id, descriptor.attrName) !== undefined;
});
if (!isModified) {

View file

@ -11,7 +11,7 @@
* state on each render so it stays reactive to fact changes (moves, rule
* changes, etc.) without any additional subscription setup.
*/
import type { EntityId } from '@paratype/rete';
import { asEntityId } from '@paratype/rete';
import { MODIFIER_REGISTRY } from '../modifiers/index.js';
import { getModifierSource } from "../modifiers/source.js";
import type { ChessEngine } from '../engine.js';
@ -26,7 +26,7 @@ export function ModifierPinnedPanel({ pieceId, engine, onClose }: Props) {
if (pieceId === null || engine === null) return null;
const { session } = engine;
const id = pieceId as unknown as EntityId;
const id = asEntityId(pieceId);
// Basic piece facts — bail if the entity doesn't exist in session.
const pieceType = session.get(id, 'PieceType') as string | undefined;

View file

@ -9,7 +9,7 @@
* MODIFIER_REGISTRY attribute is shown without distinguishing per-type /
* per-instance / preset origin (T26 can refine this).
*/
import type { EntityId } from '@paratype/rete';
import { asEntityId } from '@paratype/rete';
import { MODIFIER_REGISTRY } from '../modifiers/index.js';
import type { ChessEngine } from '../engine.js';
@ -20,7 +20,7 @@ interface Props {
export function ModifierTooltip({ pieceId, engine }: Props) {
const { session } = engine;
const id = pieceId as unknown as EntityId;
const id = asEntityId(pieceId);
// Basic piece facts
const pieceType = session.get(id, 'PieceType') as string | undefined;

View file

@ -6,7 +6,7 @@ export type {
SchemaKind,
DefineSchemaOptions,
} from "./schema.js";
export { defineSchema, fact } from "./schema.js";
export { asEntityId, defineSchema, fact } from "./schema.js";
export type { AttrKey, FactValue } from "./wm.js";
export { WorkingMemory } from "./wm.js";

View file

@ -24,6 +24,16 @@
*/
export type EntityId = number & { readonly __brand: "EntityId" };
/**
* Coerce a raw number to the branded EntityId type. Use ONLY at trust
* boundaries where you've already established that `n` came from
* `Session.nextId()` or a fact's `id` field (e.g. UI code that carries
* a piece's id as `number` because React prop serialization doesn't
* preserve branding). Prefer passing `EntityId` through end-to-end when
* possible; this helper is the single legitimate cast site.
*/
export const asEntityId = (n: number): EntityId => n as EntityId;
/**
* Shape of a schema type parameter.
*