Claude-skill-registry common-conventions

TMNL codebase conventions for file organization, barrel exports, naming patterns, comments, and module structure. The meta-skill for consistency.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/common-conventions" ~/.claude/skills/majiayu000-claude-skill-registry-common-conventions && rm -rf "$T"
manifest: skills/data/common-conventions/SKILL.md
source content

TMNL Common Conventions

Overview

TMNL maintains strict conventions for code organization, naming, and documentation. This skill codifies patterns observed across the codebase to ensure consistency.

Mandatory Conventions (Non-negotiable):

  1. Schema discipline — All domain types use Effect Schema
  2. Typography 12px floor — No text smaller than 12px
  3. Atom-as-State doctrine — No Effect.Ref for React consumers
  4. Dependency audit — Grep before cutting imports

Pattern 1: Library Module Structure

When: Creating a new library at

src/lib/{feature}/

Standard Directory Layout

src/lib/{feature}/
├── index.ts              # Barrel export (required)
├── types.ts              # TypeScript types (UI props, configs)
├── schemas/              # Effect Schema definitions
│   └── index.ts
├── services/             # Effect.Service implementations
│   ├── index.ts
│   └── {Feature}Service.ts
├── atoms/                # effect-atom definitions
│   └── index.ts
├── hooks/                # React hooks
│   └── use{Feature}.ts
├── components/           # React components (if UI-heavy)
│   └── {Component}.tsx
├── machines/             # XState machines (if stateful)
│   └── {feature}-machine.ts
├── v1/, v2/              # Version directories (for evolution)
└── CLAUDE.{feature}.md   # Agent handoff documentation

When to Use Each Directory

DirectoryWhen to Create
services/
Multiple Effect services, or single complex service
service.ts
(root)
Single simple service (e.g.,
src/lib/commands/service.ts
)
atoms/
effect-atom state management
hooks/
Multiple React hooks
use{Feature}.tsx
(root)
Single hook
schemas/
Domain types requiring validation
types.ts
UI props, configs, non-validated types
machines/
XState state machines
v1/
,
v2/
Major version evolution (not breaking changes)

Pattern 2: Barrel File (index.ts) Structure

When: Every

src/lib/{feature}/
directory requires a barrel export.

Standard Format

/**
 * {Feature} Library
 *
 * {Brief description}
 *
 * @example
 * ```tsx
 * import { Component, useFeature } from '@/lib/{feature}'
 * ```
 */

// ═══════════════════════════════════════════════════════════════════════════
// CORE EXPORTS
// ═══════════════════════════════════════════════════════════════════════════

export { MainComponent } from './components/Main'
export { FeatureService } from './services'

// ═══════════════════════════════════════════════════════════════════════════
// SCHEMAS
// ═══════════════════════════════════════════════════════════════════════════

export * from './schemas'

// ═══════════════════════════════════════════════════════════════════════════
// ATOMS (effect-atom)
// ═══════════════════════════════════════════════════════════════════════════

export {
  stateAtom,
  configAtom,
  operationsAtom,
} from './atoms'

// ═══════════════════════════════════════════════════════════════════════════
// REACT HOOKS
// ═══════════════════════════════════════════════════════════════════════════

export { useFeature, useFeatureState } from './hooks'

// ═══════════════════════════════════════════════════════════════════════════
// TYPES
// ═══════════════════════════════════════════════════════════════════════════

export type { FeatureConfig, FeatureState } from './types'

Section Separators

Use box-drawing characters for major sections:

// ═══════════════════════════════════════════════════════════════════════════
// SECTION NAME (all caps)
// ═══════════════════════════════════════════════════════════════════════════

Use simple dashes for subsections:

// ───────────────────────────────────────────────────────────────────────────
// Subsection name
// ───────────────────────────────────────────────────────────────────────────

Version Re-exports

For modules with v1/v2:

// Default exports from v1
export * from './v1'

// Explicit v2 namespace
export * as v2 from './v2'

Usage:

import { Slider } from '@/lib/slider'         // v1 (default)
import { Slider } from '@/lib/slider/v2'      // v2 (explicit)

Pattern 3: Naming Conventions

Atoms

PatternExampleUsage
{noun}Atom
resultsAtom
State atom
{noun}sAtom
layersAtom
Collection atom
{verb}Atom
computedAtom
Derived/computed atom
{feature}OpsAtom
layerOpsAtom
Operation atoms (mutations)
{verb}Op
searchOp
,
clearOp
Individual operation

Services

PatternExampleUsage
{Feature}Service
DataManagerService
Effect.Service class
{Feature}ServiceShape
ChannelServiceShape
Interface type
{Behavior}Behavior
LinearBehavior
,
DecibelBehavior
Strategy pattern

Hooks

PatternExampleUsage
use{Feature}
useSlider
Main feature hook
use{Feature}Value
useSliderValue
Read-only value hook
use{Feature}State
useMinibufferState
State + updater
use{Feature}Ops
useOverlayOps
Operations only
useAtom{Feature}
useAtomValue
Atom-specific

Components

PatternExampleUsage
{Name}.tsx
Slider.tsx
Component file
{Name}Props
SliderProps
Props interface
{Name}Context
DataGridContext
Context type
{Name}Provider
CommandProvider
Context provider

Machines

PatternExampleUsage
{feature}Machine
minibufferMachine
XState machine
{Feature}Machine
MinibufferMachine
Type alias
{Feature}Actor
MinibufferActor
ActorRef type

Pattern 4: Comment Styles

File Header (JSDoc)

/**
 * {Module Name} — {Brief description}
 *
 * {Longer description if needed}
 *
 * @example
 * ```tsx
 * // Usage example
 * ```
 */

Architectural Notes

For non-obvious design decisions:

// ARCHITECTURAL NOTE:
// CommandProvider lives here (commands/), not in minibuffer/.
// Minibuffer is a generic prompt engine. Commands USES minibuffer, not the reverse.

TODO/FIXME

// TODO(prime): Migrate to v2 API after EPOCH-0003
// FIXME: This breaks when input is empty
// HACK: Workaround for WSLg rendering bug

Pattern 5: Types vs Schemas

When to Use
types.ts

  • React component props
  • UI configuration
  • Local function parameters
  • Types that don't need runtime validation
// src/lib/slider/v1/types.ts
export interface SliderProps {
  value: number
  onChange: (value: number) => void
  min?: number
  max?: number
}

export interface SliderConfig {
  min: number
  max: number
  step: number
  defaultValue: number
}

When to Use
schemas/

  • Domain types requiring validation
  • Event payloads
  • API responses
  • Discriminated unions with pattern matching
  • EventLog integration
// src/lib/overlays/schemas/core.ts
import { Schema } from 'effect'

export const OverlayId = Schema.String.pipe(
  Schema.brand('OverlayId')
)
export type OverlayId = typeof OverlayId.Type

export const OverlayOpened = Schema.TaggedStruct('OverlayOpened', {
  id: OverlayId,
  timestamp: Schema.DateFromSelf,
})

Pattern 6: Test File Organization

Location Patterns

PatternExampleWhen
__tests__/
directory
src/lib/stx/__tests__/
Multiple test files
.test.ts
suffix
service.test.ts
Single test file
.bun.test.ts
suffix
eventlog-integration.bun.test.ts
Bun-specific

Test Naming

// src/lib/feature/__tests__/feature.test.ts
describe('FeatureService', () => {
  describe('operation', () => {
    it('does X when Y', () => { ... })
    it('fails with Z when W', () => { ... })
  })
})

Effect Service Tests

Use

@effect/vitest
with
it.effect()
:

import { describe, it } from '@effect/vitest'

describe('MyService', () => {
  it.effect('returns data', () =>
    Effect.gen(function* () {
      const service = yield* MyService
      const result = yield* service.getData()
      expect(result).toBeDefined()
    }).pipe(Effect.provide(MyService.Default))
  )
})

Atom Tests

Use

Registry.make()
:

it('atom updates', () => {
  const r = Registry.make()
  expect(r.get(counterAtom)).toBe(0)
  r.set(counterAtom, 1)
  expect(r.get(counterAtom)).toBe(1)
})

Pattern 7: Documentation Files

Per-Module Documentation

FilePurposeAudience
CLAUDE.{feature}.md
Agent handoffAI assistants
README.md
User-facing docsDevelopers
ARCHITECTURE.md
Deep design analysisArchitects
AGENTS.{feature}.md
Agent-specific notesAI assistants

CLAUDE.{feature}.md Structure

# {Feature} — Claude Context

## Overview
{Brief description}

## Key Files
- `service.ts` — Main service implementation
- `atoms/index.ts` — Reactive state

## Patterns Used
- Effect.Service<>() for DI
- Atom.runtime() for state

## Gotchas
- Don't use X because Y
- Always Z before W

## Related Skills
- effect-patterns
- effect-atom-integration

Pattern 8: Service File Patterns

Single Service (Root Level)

// src/lib/commands/service.ts

/**
 * TMNL Commands — Effect Service
 */

// ───────────────────────────────────────────────────────────────────────────
// Atoms (Reactive State)
// ───────────────────────────────────────────────────────────────────────────

export const commandsAtom = Atom.make<ReadonlyMap<string, Command>>(new Map())

// ───────────────────────────────────────────────────────────────────────────
// Service Implementation
// ───────────────────────────────────────────────────────────────────────────

export class CommandService extends Effect.Service<CommandService>()('app/CommandService', {
  effect: Effect.gen(function* () {
    // ...
  }),
}) {}

Multiple Services (Directory)

src/lib/overlays/services/
├── index.ts              # Re-exports all services
├── OverlayRegistry.ts    # Registry service
├── PortHub.ts            # Port management
└── EventDispatcher.ts    # Event dispatch

Anti-Patterns

1. Barrel File Without Sections

// WRONG — Unorganized exports
export * from './components'
export * from './hooks'
export * from './types'
export * from './atoms'

// CORRECT — Sectioned exports
// ═══════════════════════════════════════════════════════════════════════════
// COMPONENTS
// ═══════════════════════════════════════════════════════════════════════════
export { Slider } from './components/Slider'

// ═══════════════════════════════════════════════════════════════════════════
// HOOKS
// ═══════════════════════════════════════════════════════════════════════════
export { useSlider } from './hooks/useSlider'

2. Types in Wrong Location

// WRONG — Domain type without Schema
// src/lib/feature/types.ts
export interface UserEvent {
  _tag: 'UserCreated'
  id: string
  name: string
}

// CORRECT — Domain type with Schema
// src/lib/feature/schemas/events.ts
export const UserCreated = Schema.TaggedStruct('UserCreated', {
  id: Schema.String,
  name: Schema.NonEmptyString,
})

3. Inconsistent Naming

// WRONG — Mixed naming styles
export const user_state_atom = Atom.make(...)  // snake_case
export const UseUserHook = () => { ... }       // PascalCase for hook
export const userservice = Effect.Service()    // no separator

// CORRECT — Consistent camelCase with type suffix
export const userStateAtom = Atom.make(...)
export const useUser = () => { ... }
export const UserService = Effect.Service()

4. Missing JSDoc on Exports

// WRONG — No documentation
export const searchOp = runtimeAtom.fn<string>()(...)

// CORRECT — JSDoc on public exports
/**
 * Search operation. Triggers search with given query.
 *
 * @param query - Search query string
 * @returns Effect that updates resultsAtom
 */
export const searchOp = runtimeAtom.fn<string>()(...)

Checklist: New Module Creation

When creating

src/lib/{feature}/
:

  • Create
    index.ts
    with JSDoc header and sectioned exports
  • Create
    types.ts
    for non-domain types (UI props, configs)
  • Create
    schemas/
    for domain types using Effect Schema
  • Create
    services/
    (or
    service.ts
    ) with Effect.Service<>()
  • Create
    atoms/index.ts
    with Atom definitions
  • Create
    hooks/
    with React hooks
  • Use section comments:
    // ═══════...
  • Add ARCHITECTURAL NOTE for non-obvious decisions
  • Create
    CLAUDE.{feature}.md
    for agent handoff
  • Prefer
    Atom.make<T>()
    over
    Effect.Ref<T>
    for React
  • Test with
    @effect/vitest
    for services,
    Registry.make()
    for atoms

Canonical Examples

ConventionBest ExampleFile
Barrel fileOverlays
src/lib/overlays/index.ts
Service patternCommands
src/lib/commands/service.ts
Atom organizationSlider
src/lib/slider/v1/atoms/index.ts
Schema usageCommands
src/lib/commands/types.ts
Hook namingCommands
src/lib/commands/useCommandWire.tsx
Version strategySlider
src/lib/slider/index.ts
Test organizationSTX
src/lib/stx/__tests__/

Integration Points

  • tmnl-file-organization — Directory structure details
  • effect-patterns — Service definition patterns
  • effect-atom-integration — Atom patterns
  • tmnl-typography-discipline — Typography rules
  • effect-schema-mastery — Schema patterns