From 681f7f41e1e4716da1d9f33c2bbe2b8069a10253 Mon Sep 17 00:00:00 2001 From: Fini Date: Wed, 22 Apr 2026 00:20:50 +0800 Subject: [PATCH] =?UTF-8?q?test(ai):=20drift-guard=20shim=20registry=20?= =?UTF-8?q?=E2=87=94=20pen-mcp=20ELEMENT=5FTOOL=5FNAMES?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two assertions that fire if the embedded shim registry drifts out of sync with the pen-mcp catalog: 1. Every add_*_v0 name pen-mcp exposes must have a shim — catches "added a pen-mcp tool, forgot the builder / shim / Nitro SERVER_BUILDERS update" triple-edit drift. 2. Every shim key must exist in pen-mcp — catches stale shims for removed or renamed tools. Failure message names exactly which tools are missing on each side so the fix is a copy-paste, not a search. Pairs with the existing short-circuit test — together they lock the invariant that elements.md catalog = shim set = Nitro registry. --- .../element-tools-dispatcher.test.ts | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/apps/web/src/services/ai/__tests__/element-tools-dispatcher.test.ts b/apps/web/src/services/ai/__tests__/element-tools-dispatcher.test.ts index eb84a2581..3dd8356fd 100644 --- a/apps/web/src/services/ai/__tests__/element-tools-dispatcher.test.ts +++ b/apps/web/src/services/ai/__tests__/element-tools-dispatcher.test.ts @@ -1,10 +1,44 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { ELEMENT_TOOL_NAMES } from '@zseven-w/pen-mcp'; import { dispatchElementToolCall } from '../element-tools-dispatcher'; +import { SUPPORTED_EMBEDDED_ELEMENT_TOOLS } from '../element-tool-shims'; import { useHistoryStore } from '@/stores/history-store'; import { useDocumentStore } from '@/stores/document-store'; import { useCanvasStore } from '@/stores/canvas-store'; import type { DesignOutputShape } from '../design-parser'; +/** + * Drift-guard: the embedded shim registry MUST cover every tool + * name pen-mcp exposes. Adding a pen-mcp element tool without + * adding a matching pen-core builder + shim + Nitro SERVER_BUILDERS + * entry silently breaks AI generation under the flag (tool gets + * short-circuited to `unsupported` and the AI sees + * advertised-but-not-executable output). + */ +describe('SUPPORTED_EMBEDDED_ELEMENT_TOOLS drift guard', () => { + it('covers every add_*_v0 name pen-mcp exposes', () => { + const penMcpAddNames = [...ELEMENT_TOOL_NAMES].filter((n) => /^add_.+_v0$/.test(n)); + const shimSet = new Set(SUPPORTED_EMBEDDED_ELEMENT_TOOLS); + const missing = penMcpAddNames.filter((n) => !shimSet.has(n)); + expect( + missing, + `Embedded shim registry is missing the following pen-mcp tools — add pen-core builder + ` + + `shim + Nitro SERVER_BUILDERS entry together: ${missing.join(', ')}`, + ).toEqual([]); + }); + + it('does not register shims for names pen-mcp does not expose', () => { + // The other direction: a stale shim for a removed tool would + // silently send stale output. Keep shim ⊆ pen-mcp exactly. + const penMcpSet = new Set(ELEMENT_TOOL_NAMES); + const orphaned = SUPPORTED_EMBEDDED_ELEMENT_TOOLS.filter((n) => !penMcpSet.has(n)); + expect( + orphaned, + `Shim registry has entries pen-mcp does not expose (stale / renamed tool): ${orphaned.join(', ')}`, + ).toEqual([]); + }); +}); + /** * Phase 2 M2/M3 invariants under test: * 1. Every dispatch runs inside exactly one startBatch/endBatch pair