fix(ui): hide engine-internal presets from user authoring surface

The Modifier Profile editor's Presets tab surfaced the plumbing
preset '__modifier-profile-integration__' — an internal hook bundle
that wires modifier-profile facts (CaptureFlags, DirectionAdditions,
DamageResistance, on-damage / on-capture / aura triggers) into the
engine runtime. ChessEngine auto-activates it when a profile is
supplied; toggling it from a profile has no meaningful effect and
confuses users who see 'Modifier Profile Integration' as a bundle-able
rule variant.

Filter any preset id matching the '__x__' double-underscore naming
convention out of:
- PresetPanel's allPresets list (the Presets tab in the editor)
- LayoutPicker's suggestedPresets chip row (in case a layout ever
  accidentally lists one)

Generalized so future internal presets following the same convention
are auto-hidden without maintaining an exclusion list. New unit test
(PresetPanel.filter.test.ts) pins the contract: registry contains the
internal preset, filter yields a non-empty user-safe list, known
public preset (extinction-chess) survives.
This commit is contained in:
Joey Yakimowich-Payne 2026-04-21 15:43:04 -06:00
commit e5594b0d3c
No known key found for this signature in database
3 changed files with 71 additions and 2 deletions

View file

@ -144,7 +144,14 @@ export function LayoutPicker({
value.suggestedPresets.length > 0 && (
<div className="pt-2 flex flex-wrap items-center gap-1.5">
<span className="text-xs text-neutral-400 italic mr-1">Suggested rules:</span>
{value.suggestedPresets.map((presetId) => {
{value.suggestedPresets
// Never surface engine-internal plumbing presets
// (__x__ naming convention) even if a layout
// accidentally lists one — they auto-activate via
// the engine constructor and can't be meaningfully
// toggled by the user.
.filter((id) => !(id.startsWith('__') && id.endsWith('__')))
.map((presetId) => {
const presetDef = PRESET_REGISTRY.get(presetId);
const label = presetDef?.name ?? presetId;
const isActive = activations.some(a => a.id === presetId);

View file

@ -0,0 +1,46 @@
import { describe, it, expect } from "vitest";
import { PRESET_REGISTRY } from "../presets/registry.js";
import "../presets/index.js";
// Side-effect import: registers the __modifier-profile-integration__
// pseudo-preset (top-level register call in apply.ts). The modifier
// barrel doesn't pull this in, so we import directly to mirror the
// engine constructor's boot ordering.
import "../modifiers/apply.js";
/**
* Regression guard — the Modifier Profile editor's Presets tab used
* to surface the engine-internal `__modifier-profile-integration__`
* preset because it calls PRESET_REGISTRY.getAll() directly. That
* preset is plumbing (it wires modifier facts into move-gen / damage
* pipelines) and is auto-activated by the ChessEngine constructor,
* so it should never appear as something a user can toggle on a
* profile.
*
* The panel now filters any id matching the `__x__` double-underscore
* convention. This test pins that contract.
*/
describe("Preset authoring surface excludes internal presets", () => {
const isInternal = (id: string) => id.startsWith("__") && id.endsWith("__");
it("registry contains the modifier-profile-integration internal preset", () => {
const all = PRESET_REGISTRY.getAll();
const internal = all.filter((p) => isInternal(p.id));
expect(internal.length).toBeGreaterThan(0);
expect(internal.map((p) => p.id)).toContain(
"__modifier-profile-integration__",
);
});
it("filtering internal presets yields a user-authoring-safe list", () => {
const all = PRESET_REGISTRY.getAll();
const userFacing = all.filter((p) => !isInternal(p.id));
expect(userFacing.length).toBeGreaterThan(0);
for (const p of userFacing) {
expect(isInternal(p.id)).toBe(false);
}
// Sanity: at least one well-known user-facing preset survives
// the filter. extinction-chess is a stable long-standing id so
// this assertion won't churn with preset-registry shuffling.
expect(userFacing.map((p) => p.id)).toContain("extinction-chess");
});
});

View file

@ -121,7 +121,23 @@ export function PresetPanel({
onChange,
boundLayout,
}: PresetPanelProps) {
const allPresets = useMemo(() => PRESET_REGISTRY.getAll(), []);
// Filter engine-internal presets out of the authoring surface.
// Convention: any id wrapped in double-underscores (`__x__`) is
// reserved for plumbing presets the engine auto-activates — e.g.
// `__modifier-profile-integration__` wires modifier facts into
// move-gen / damage pipelines. Those should never appear in a
// profile's bundled activations because they can't be meaningfully
// toggled; they either flip on via the engine constructor or they
// don't. Same guard protects against any future internal presets
// that follow the naming convention without our having to
// hand-maintain an exclusion list.
const allPresets = useMemo(
() =>
PRESET_REGISTRY.getAll().filter(
(p) => !(p.id.startsWith('__') && p.id.endsWith('__')),
),
[],
);
const activeById = useMemo(() => {
const m = new Map<string, PresetActivation>();
for (const a of activations) m.set(a.id, a);