Repository navigation
fix(t3team): show_widget live schema carries the theme contract; resnapshot widget iframe theme on host flips - #200
Merged
Conversation
added 2 commits
September 12, 2026 18:00
…on-sprite contract
The widget authoring guidance (T3TEAM_WIDGET_AUTHORING_GUIDANCE) lived only in
the catalog snapshot and the t3team_help('widget-guidance') topic; the live
MCP tool shipped a bare description and Schema.String properties with no
descriptions, so agents never saw the rule 'never hard-code light or dark
palette colors' and rendered widgets with fixed hex palettes that clashed
with the host theme.
- Move the tool-level description into a shared constant
(T3TEAM_WIDGET_SHOW_TOOL_DESCRIPTION) used by both the live toolkit and
the catalog entry — one source of truth, no restated copy.
- Annotate widget_code with the full authoring contract and title with the
artifact-name rule in the live Tool.make schema.
- Lock the live JSON schema to the documented contract in
t3team-mcpToolInputSchema.test.ts so the surfaces cannot drift again.
The widget srcdoc snapshots the host's theme CSS variables at build time (a sandboxed iframe :root cannot inherit them), but the snapshot was memoized on [widget.html, nonce] — so after any light/dark flip (user toggle or OS change under system-follow) widgets stayed frozen on the mount-time palette while the host re-themed around them. - Subscribe the controller to the theme store via a new useThemeSnapshot() export: referentially stable, invalidated only when the resolved theme actually changes. (useTheme() allocates a fresh object per render and would have defeated the memo — the resync test caught exactly that.) - New test locks the behavior: one snapshot on mount, exactly one rebuild on a light→dark flip reaching the live iframe, zero rebuilds on plain re-renders.
johnnyelwailer
force-pushed
the
fix/widget-theme-contract-live-tool
branch
from
September 12, 2026 16:07
db75e77 to
8b2d7b4
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
show_widget-produced widgets rendered with a hard-coded dark palette regardless of the host theme — and stayed frozen on the mount-time palette after any light/dark flip.Two independent defects:
The model never saw the widget authoring contract. The theme-token + icon-sprite contract (use
var(--*)host theme variables for every color, use thet3w-iconsprite, never ship a palette) was codified inpackages/project-contextasT3TEAM_WIDGET_AUTHORING_GUIDANCEwith a doc that explicitly names "the MCP tool'swidget_codeinput schema" as a carrying surface — but the liveTool.make("t3team_show_widget", ...)wrapper had shipped with a bare description andwidget_code: Schema.Stringsince the MCP toolkit was carved out of the catalog on 2026-07-19 (03953ee"expose inline widgets to provider agents"). The Aug 29 guidance-constant work wired the contract into the catalog snapshot and the help topic, and tests locked the catalog — but the live schema was never wired, and no test locked the live surface.The host theme snapshot was taken once, at mount. The widget srcdoc cannot inherit host CSS custom properties, so the theme is snapshotted into the iframe's own
:rootat build time — but thesrcdocmemo depended only on[widget.html, nonce], so after a light/dark flip (user toggle, or OS change under system-follow) the widget stayed on the old palette while the host re-themed around it.Changes
Fix 1 — live schema carries the contract (
62c49e3)T3TEAM_WIDGET_SHOW_TOOL_DESCRIPTIONadded tot3teamWidgetGuidance.tsas a second shared constant (single source of truth; the "two surfaces must not restate" invariant is preserved — the guidance doc now enumerates the constants instead of restating the text).apps/server/.../t3team/tools.ts: live tool description +title/widget_codeproperty annotations now use the shared constants viaSchema.annotate({ description })(the established Effect-4 pattern in this file).t3team-mcpToolInputSchema.test.ts: three new assertions lock the live JSON schema (not just the catalog snapshot) to the documented contract — the class of drift that produced this bug is now a red build.Fix 2 — re-snapshot on theme flips (
4d06e84)useThemeSnapshot()export inapps/web/src/hooks/useTheme.ts: the rawThemeSnapshotwith referential stability (invalidated only when the resolved theme actually changes).useTheme()allocates a fresh object per render and would defeat the memo — the new test caught exactly that.t3team-useWidgetBlockController.ts: the srcdoc memo now depends on the stable snapshot → widgets rebuild exactly on real theme flips, zero rebuilds on plain re-renders.t3team-widgetBlockThemeResync.test.tsx: mount-snapshot, exactly-one-rebuild on light→dark flip (verified in the live iframe DOM), no rebuild on plain re-render.Verification
packages/project-context: typecheck + guidance/catalog tests green.apps/server: 25/25 MCP tool input-schema tests (incl. the 3 new live-schema locks), 25/25 t3team toolkit + widget-show tests green.apps/web: web typecheck green; 3/3 new resync tests; 22/22 pre-existing widget-suite tests green.src/t3team-bin.ts servefrom this branch, scratch project, Nexplore provider): two fresh threads asked to visualize things with zero design hints (no mention of themes, colors, tokens, icons, or layout). Both agents't3team_show_widgetcalls (4 total, verified in provider event logs) produced contract-clean widgets: HTML fragment (no<!DOCTYPE>), zero hard-coded hex colors, 12 host theme tokens each,t3w-iconsprite, no emoji glyphs — and neither agent called the help topic, so the contract reached them purely through the live tool schema. Pre-fix control: this conversation's own orchestrator widget hard-coded 8+ hex values with the old schema.Notes
widget-guidancehelp topic is unchanged — it remains the on-demand deep reference.