Agent skill
tmnl-testbed-patterns
Creating testbed components at /testbed/*, adding cards to App.tsx portal, route registration. Testbed structure conventions.
Install this agent skill to your Project
npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/data/tmnl-testbed-patterns
SKILL.md
TMNL Testbed Patterns
Canonical patterns for creating, routing, and presenting testbed components in TMNL.
Overview
Testbeds are isolated demonstration pages for features, patterns, and systems under development. They serve as:
- Living documentation
- Integration test surfaces
- Pattern exemplars
- Performance benchmarks
Location: src/components/testbed/*
Routes: /testbed/*
Portal: src/App.tsx card grid
Canonical Sources
Primary Files:
src/router.tsx- Route registration checklist and patternssrc/App.tsx- Portal card data structure and renderingsrc/components/testbed/DataManagerTestbed.tsx- Exemplar testbed with antipattern documentationsrc/components/testbed/SliderV2Testbed.tsx- Trait-based testbed pattern
Reference Testbeds:
AnimationTestbed.tsx- Animation system demonstrationEffectAtomTestbed.tsx- Effect-atom integration patternsOverlayTestbed.tsx- Overlay system capabilitiesScadaOverlayTestbed.tsx- Industrial overlay patterns
Patterns
1. Testbed File Structure
Template:
/**
* [Feature] Testbed
*
* [Brief description of what this testbed demonstrates]
*
* Route: /testbed/[feature-name]
*
* HYPOTHESES (if experimental):
* - H1: [Hypothesis to validate]
* - H2: [Another hypothesis]
* ...
*
* ANTIPATTERNS (if discovered):
* - Document failure modes and fixes
* - Provide root cause analysis
* - Link to related testbeds with same issues
*/
import { useState } from 'react'
import { Link } from '@tanstack/react-router'
import { ArrowLeft } from 'lucide-react'
export function FeatureTestbed() {
return (
<div className="min-h-screen bg-neutral-950 text-neutral-100 p-8">
{/* Header with back link */}
<div className="max-w-4xl mx-auto mb-8">
<Link to="/" className="text-cyan-400 hover:underline text-sm mb-4 block">
← Back to Home
</Link>
<h1 className="text-3xl font-mono font-bold mb-2">[Feature] Testbed</h1>
<p className="text-neutral-400">
[Brief description]
</p>
</div>
{/* Content sections */}
<div className="max-w-4xl mx-auto grid gap-6">
{/* Demo sections here */}
</div>
</div>
)
}
Naming Convention: [Feature]Testbed.tsx (PascalCase, no spaces)
2. Route Registration (4-Step Checklist)
From src/router.tsx header comment:
CHECKLIST FOR NEW ROUTES:
1. Import the component at top of file
2. Create route constant (e.g., `const fooRoute = createRoute({ ... })`)
3. Add route to `routeTree.addChildren([...])` array
4. Add Link in App.tsx homepage navigation
DON'T FORGET STEP 4. THE LINK. IN APP.TSX. SERIOUSLY.
Example Implementation:
// Step 1: Import
import { FeatureTestbed } from './components/testbed/FeatureTestbed'
// Step 2: Create route constant
const featureTestbedRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/testbed/feature',
component: FeatureTestbed,
})
// Step 3: Add to routeTree.addChildren([...])
const router = createRouter({
routeTree: rootRoute.addChildren([
indexRoute,
tmnlRoute,
// ... other routes ...
featureTestbedRoute, // ← Add here
]),
})
Path Convention:
- Basic:
/testbed/[feature-name](kebab-case) - Versioned:
/testbed/[feature]/v1or/testbed/[feature]/v2 - Nested:
/testbed/[category]/[feature]
3. Portal Card Registration (App.tsx)
Step 4 from checklist: Add card to CARDS array in src/App.tsx.
Card Definition Type:
interface CardDef {
readonly title: string // ALL CAPS by convention
readonly body: string // 1-2 sentences, technical detail
readonly route: string // Must match route path exactly
readonly status?: IndicatorStatus // 'active' | 'idle' | 'warning' | 'error'
readonly label?: string // Badge text (e.g., 'NEW', 'v1', 'BETA')
readonly glow?: GlowColor // 'cyan' | 'rose' | 'amber' | 'emerald'
}
Categories in CARDS Array:
- Feature (Data Manager, AG-Grid, Overlays)
- Infrastructure (Effect-Atom, Animation, Streams)
- Controls (Slider, Hotkeys, Keybindings)
- Abstractions (Traits, Base Modal, Capabilities)
Example Card:
const CARDS: readonly CardDef[] = [
// Feature
{
title: 'FEATURE NAME',
body: 'Effect.Service pattern with kernel architecture. FlexSearch and Linear drivers for progressive streaming.',
route: '/testbed/feature',
status: 'active',
label: 'NEW',
glow: 'cyan',
},
// ... more cards
]
Glow Color Guidelines:
cyan- Data/infrastructure featuresemerald- New/experimental featuresamber- High-value integrationsrose- Critical/system features
4. Demo Section Components
Reusable Section Pattern:
function DemoSection({
title,
description,
children,
}: {
title: string
description: string
children: React.ReactNode
}) {
return (
<div className="p-4 bg-neutral-900/50 rounded-lg border border-neutral-800">
<h3 className="text-lg font-mono font-bold text-neutral-100 mb-1">{title}</h3>
<p className="text-sm text-neutral-400 mb-4">{description}</p>
<div className="space-y-4">{children}</div>
</div>
)
}
Usage:
<div className="max-w-4xl mx-auto grid gap-6">
<DemoSection
title="1. Basic Usage"
description="Demonstrates the fundamental pattern"
>
{/* Demo content */}
</DemoSection>
<DemoSection
title="2. Advanced Usage"
description="Shows edge cases and configuration"
>
{/* Demo content */}
</DemoSection>
</div>
5. Shared Components
Import from shared module:
import { SectionLabel } from '@/components/testbed/shared'
Available Shared Components (in src/components/testbed/shared.tsx):
SectionLabel- Standardized section headers- Render tracking utilities (for performance analysis)
6. Version Switching Pattern
For multi-version features (e.g., DataGrid V1/V2):
// Component: DataGridTestbedSwitch.tsx
export function DataGridTestbedSwitch() {
const [version, setVersion] = useState<'v1' | 'v2'>('v2')
return (
<div>
<VersionToggle value={version} onChange={setVersion} />
{version === 'v1' ? <DataGridTestbed /> : <DataGridTestbedV2 />}
</div>
)
}
Route: Single route /testbed/data-grid with internal switching.
7. Hypothesis Validation Pattern
When conducting experiments (EPOCH-style):
/**
* HYPOTHESES:
* - H1: Search results flow correctly to AG-Grid rowData
* - H2: Progressive stream updates trigger grid re-renders without flicker
* - H3: Stream-first search provides clean DX
* - H4: Real-time metrics (throughput, items/sec)
* - H5: Driver switching (flex/linear) is seamless
*/
export function ExperimentalTestbed() {
const [hypotheses, setHypotheses] = useState({
h1: false,
h2: false,
h3: false,
h4: false,
h5: false,
})
// Verify actual outcomes, not just function calls
const validateH1 = () => {
setHypotheses(prev => ({ ...prev, h1: gridData.length > 0 })) // ✓ Correct
// NOT: setHypotheses(prev => ({ ...prev, h1: true })) after setGridData() // ✗ Wrong
}
// Display validation status
return (
<div>
<HypothesisPanel hypotheses={hypotheses} />
{/* Testbed content */}
</div>
)
}
Antipattern Fix (from DataManagerTestbed.tsx):
- Verify outcomes (e.g.,
gridData.length > 0) - NOT function calls (e.g., "setGridData was called")
8. Antipattern Documentation
When discovering antipatterns, document them prominently:
/**
* ═══════════════════════════════════════════════════════════════════════════════
* ANTIPATTERN DISCOVERED: [Title]
* ═══════════════════════════════════════════════════════════════════════════════
*
* ROOT CAUSE: [Why it failed]
*
* FAILURE SCENARIO:
* 1. [Step 1]
* 2. [Step 2]
* 3. [Outcome]
*
* SYMPTOM: [Observable behavior]
*
* FIX: [Solution]
*
* See also: [Related testbed] for similar antipattern documentation.
* ═══════════════════════════════════════════════════════════════════════════════
*/
Reference: DataManagerTestbed.tsx for canonical antipattern documentation format.
Examples
Example 1: Simple Testbed
/**
* Slider Testbed
*
* Demonstrates DAW-grade slider with Effect.Service behaviors.
*
* Route: /testbed/slider
*/
import { useState } from 'react'
import { Link } from '@tanstack/react-router'
import { Slider, LinearBehavior } from '@/lib/slider'
export function SliderTestbed() {
const [value, setValue] = useState(50)
return (
<div className="min-h-screen bg-neutral-950 text-neutral-100 p-8">
<div className="max-w-4xl mx-auto mb-8">
<Link to="/" className="text-cyan-400 hover:underline text-sm mb-4 block">
← Back to Home
</Link>
<h1 className="text-3xl font-mono font-bold mb-2">Slider Testbed</h1>
<p className="text-neutral-400">
DAW-grade slider with runtime-swappable behaviors
</p>
</div>
<div className="max-w-4xl mx-auto">
<Slider
value={value}
onChange={setValue}
behavior={LinearBehavior.shape}
config={{ min: 0, max: 100 }}
/>
</div>
</div>
)
}
Route Registration:
// router.tsx
import { SliderTestbed } from './components/testbed/SliderTestbed'
const sliderTestbedRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/testbed/slider',
component: SliderTestbed,
})
Card Registration:
// App.tsx CARDS array
{
title: 'SLIDER V1',
body: 'DAW-grade slider with Effect.Service behaviors. Runtime-swappable linear, log, decibel curves.',
route: '/testbed/slider',
status: 'idle',
}
Example 2: Complex Testbed with Sections
/**
* Data Manager Testbed
*
* Route: /testbed/data-manager
*/
import { useState } from 'react'
import { Link } from '@tanstack/react-router'
import { DataGrid } from '@/components/data-grid'
import { SectionLabel } from '@/components/testbed/shared'
function DemoSection({
title,
description,
children,
}: {
title: string
description: string
children: React.ReactNode
}) {
return (
<div className="p-4 bg-neutral-900/50 rounded-lg border border-neutral-800">
<h3 className="text-lg font-mono font-bold text-neutral-100 mb-1">{title}</h3>
<p className="text-sm text-neutral-400 mb-4">{description}</p>
<div className="space-y-4">{children}</div>
</div>
)
}
export function DataManagerTestbed() {
const [gridData, setGridData] = useState([])
return (
<div className="min-h-screen bg-neutral-950 text-neutral-100 p-8">
<div className="max-w-4xl mx-auto mb-8">
<Link to="/" className="text-cyan-400 hover:underline text-sm mb-4 block">
← Back to Home
</Link>
<h1 className="text-3xl font-mono font-bold mb-2">Data Manager Testbed</h1>
<p className="text-neutral-400">
Effect.Service pattern with kernel architecture
</p>
</div>
<div className="max-w-4xl mx-auto grid gap-6">
<DemoSection
title="1. Search Integration"
description="Progressive streaming search with AG-Grid"
>
<DataGrid rowData={gridData} />
</DemoSection>
<DemoSection
title="2. Driver Switching"
description="FlexSearch vs Linear comparison"
>
{/* Driver switching controls */}
</DemoSection>
</div>
</div>
)
}
Anti-Patterns
Don't: Forget Step 4 (Portal Card)
// ✗ BAD - Route works but card missing from homepage
// User can't discover testbed without directly typing URL
Fix: Always add card to App.tsx CARDS array.
Don't: Use Inconsistent Naming
// ✗ BAD
export function sliderTestbedComponent() { } // lowercase
export function Slider_Testbed() { } // snake_case
export function testbedSlider() { } // inverted order
Fix: Use [Feature]Testbed pattern (PascalCase).
Don't: Create Routes Without Comments
// ✗ BAD
const foo = createRoute({ path: '/testbed/foo', component: Foo })
Fix: Add descriptive comment.
// ✓ GOOD
// Create slider testbed route
const sliderTestbedRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/testbed/slider',
component: SliderTestbed,
})
Don't: Skip Hypothesis Validation
// ✗ BAD - Tracking function calls, not outcomes
setHypotheses(prev => ({ ...prev, h1: true })) // Called setGridData
// ✓ GOOD - Verifying actual outcomes
setHypotheses(prev => ({ ...prev, h1: gridData.length > 0 }))
Don't: Hardcode Paths in Components
// ✗ BAD
<Link to="/testbed/slider">...</Link>
// ✓ GOOD - Use card definition route
<Link to={cardDef.route}>...</Link>
Quick Reference
Create New Testbed Checklist:
- Create file:
src/components/testbed/[Feature]Testbed.tsx - Import in
src/router.tsx - Create route constant:
[feature]TestbedRoute - Add to
routeTree.addChildren([...]) - Add card to
App.tsxCARDS array - Test route navigation
- Verify card appears on homepage
File Template Locations:
- Basic testbed:
SliderTestbed.tsx - Section-based:
DataManagerTestbed.tsx - Trait-based:
SliderV2Testbed.tsx - Versioned:
DataGridTestbedSwitch.tsx
Portal Card Categories:
- Feature (new capabilities)
- Infrastructure (foundational systems)
- Controls (UI components)
- Abstractions (patterns/DSLs)
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
agent-ops-spec
Manage specification documents in .agent/specs/. Use when user provides requirements, acceptance criteria, or feature descriptions that need to be tracked and validated against implementation.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-testing
Test strategy, execution, and coverage analysis. Use when designing tests, running test suites, or analyzing test results beyond baseline checks.
agent-ops-state
Maintain .agent state files. Use at session start, after meaningful steps, and before concluding: read/update constitution/memory/focus/issues/baseline consistently.
Didn't find tool you were looking for?