Imported from namco1992/otel-playground (
AGENTS.md). Install upstream withnpx skills add namco1992/otel-playground. Copyright stays with the author.
AGENTS.md — Coding Agent Reference
OVERVIEW
Interactive OpenTelemetry Collector playground: edit YAML configs, visualize pipelines with React Flow, run sample telemetry, inspect data at each stage. Three-tier architecture: React frontend + Cloudflare Worker + Go container with custom OTel Collector.
STRUCTURE
otel-playground-project/
├── app/ # React frontend + Cloudflare Worker (pnpm workspace)
│ ├── src/react-app/ # React 19 + Vite + Tailwind 4
│ │ ├── components/ # Feature subdirs: editor/, pipeline/, detail/, input/, layout/, ui/
│ │ ├── hooks/ # useConfig, usePipelineRun, useConfigValidation, useSessionId
│ │ └── lib/ # configParser, configToFlow, tapResolver, api, types, utils
│ ├── src/worker/ # Hono edge worker (routes/, KV, Container binding)
│ └── src/shared/ # Types shared between frontend and worker
├── container/ # Go HTTP wrapper + OTel Collector subprocess
│ ├── cmd/runner/ # Entry point
│ ├── internal/ # server, supervisor, tap, collector packages
│ └── otelcol/ # OCB builder config + Makefile
├── component/ # Custom OTel components
│ └── processor/httptapprocessor/
└── docs/plans/ # Design documents
BUILD / LINT / TEST COMMANDS
All frontend commands run from app/ directory using pnpm (not npm/yarn).
# ── Frontend (from app/) ─────────────────────────────────────────────
pnpm install # Install dependencies
pnpm dev # Vite dev server on :5173
pnpm build # tsc -b && vite build
pnpm lint # ESLint (flat config, no Prettier)
pnpm check # Full validation: tsc + vite build + wrangler deploy --dry-run
# ── Testing (from app/) ──────────────────────────────────────────────
pnpm test # Vitest watch mode
pnpm test:run # Vitest single run (CI mode)
# Run a single test file:
pnpm test:run src/react-app/lib/configParser.test.ts
pnpm test:run src/react-app/hooks/useConfigValidation.test.ts
# Run tests matching a name pattern:
pnpm test:run -t "parses valid YAML"
# ── Go container (from container/) ───────────────────────────────────
go test ./... # All tests
go test ./internal/tap/ # Single package
go test ./internal/collector/ -run TestCollector_HandleTap # Single test
# ── Go httptapprocessor (from component/processor/httptapprocessor/) ─
go test ./...
# ── Container Docker ─────────────────────────────────────────────────
docker build -t otel-runner . # From container/
./scripts/test-local.sh # Build + run + test (from container/)
# ── Deployment ────────────────────────────────────────────────────────
pnpm deploy # Push to Cloudflare (from app/)
CODE STYLE
TypeScript
Strict mode — all tsconfigs enforce: strict, noUnusedLocals,
noUnusedParameters, noFallthroughCasesInSwitch,
noUncheckedSideEffectImports.
No type suppression — never use as any, @ts-ignore, @ts-expect-error.
Path alias — @/* maps to src/react-app/*. Use for cross-feature imports
in frontend code. Worker code uses relative imports only.
Shared types — types used by both frontend and worker live in
src/shared/types.ts. Frontend re-exports from lib/types.ts, worker
re-exports from worker/types.ts.
Import order (no auto-enforced, but follow existing convention):
- React imports (
import { useState, useCallback } from 'react') - Third-party libraries (
@xyflow/react,hono,lucide-react, etc.) - Internal path-aliased imports (
@/lib/types,@/components/ui/button) - Relative same-feature imports (
./nodes,./types)
Exports — named exports for all components and utilities. Only App uses
default export.
Formatting — no Prettier; only ESLint. Indentation is inconsistent (tabs in some files, 2-space in others). Match the style of the file you're editing.
React Patterns
- React 19 with StrictMode enabled
- Components: named function exports (
export function PipelineView(...)) - Props: interface in same file (
interface PipelineViewProps { ... }) - State: custom hooks returning named-property objects, not tuples
- Memoization:
memo()for React Flow custom nodes;useCallback/useMemowhere needed - Styling: Tailwind CSS 4.x classes,
cn()utility from@/lib/utilsfor conditional merging - Icons:
lucide-reactexclusively - UI primitives: shadcn/ui pattern (
components/ui/) withclass-variance-authority - No external state library — pure React hooks (no Redux/Zustand/Jotai)
Go
- Go 1.24+, minimal dependencies (only
yaml.v3for container, OTel SDK for processor) - Internal packages: all logic in
internal/— not exported outside module - Error handling: always return
error, wrap withfmt.Errorf("context: %w", err), never panic - HTTP errors: use
respondError(w, code, message, details)helper - Testing: standard library
testingfor container;testify+goleakfor httptapprocessor - Table-driven tests: preferred pattern for Go tests with
[]struct{ name, input, expected } - Context: propagate
context.Contextfor cancellation
Naming Conventions
| Element | Convention | Example |
|---|---|---|
| React components | PascalCase file + function | PipelineView.tsx, export function PipelineView |
| Hooks | camelCase with use prefix |
useConfig.ts, usePipelineRun.ts |
| Lib utilities | camelCase file | configParser.ts, configToFlow.ts |
| Test files | collocated {name}.test.ts |
configParser.test.ts |
| TypeScript types | PascalCase | OtelConfig, Pipeline, SignalType |
| React props | {Component}Props |
PipelineViewProps, ConfigEditorProps |
| Constants | camelCase or UPPER_SNAKE | NODE_WIDTH, CONFIG_TTL |
| UI primitives | camelCase (shadcn pattern) | button.tsx, collapsible.tsx |
| Go exported | PascalCase | TransformConfig, RunResponse |
| Go unexported | camelCase | toStringSlice, respondError |
| Go files | snake_case | collector_test.go, config.go |
| Directories | kebab-case or camelCase | react-app/, pipeline/, httptapprocessor/ |
Error Handling Patterns
Frontend API layer — custom ApiError class with status code;
handleResponse<T>() generic function throws on non-OK responses.
Parse functions — return result objects ({ success, config?, error? })
rather than throwing.
Hooks — catch errors and store in state: { isRunning, results, error }
pattern.
Validation — return error arrays, don't throw.
Go HTTP — RunResponse with
Success: false, Error: &ErrorInfo{Code, Message, Details}.
Testing Patterns
TypeScript (Vitest + jsdom + @testing-library/react):
- Import
describe,it,expectfromvitest(globals enabled but explicit imports used) - Pure function tests: direct import and assertion
- Hook tests:
renderHook()+act()from@testing-library/react - Setup file at
src/react-app/test/setup.tsimports@testing-library/jest-dom
Go (standard testing):
- Table-driven tests with
t.Run(tt.name, func(t *testing.T) { ... }) - httptapprocessor uses
testify/requireandgoleak.VerifyTestMain
ARCHITECTURE FLOW
User -> React UI -> Hono Worker -> Container (Go wrapper -> OTel Collector subprocess)
| |
Workers KV (config) httptapprocessor captures data
|
Tap aggregator -> RunResponse
KNOWN TECH DEBT
- Hardcoded timeouts:
setTimeout(50)in PipelineView.tsx,500mssleep in server.go - Hardcoded endpoint:
localhost:4318in supervisor.go - No CI/CD pipeline — manual
pnpm deploy