Back to Skills

common-conventions

majiayu000
Updated Today
1 views
58
9
58
View on GitHub
Metageneral

About

This skill provides TMNL codebase conventions for file organization, barrel exports, naming patterns, and module structure to ensure consistency across projects. It serves as a meta-skill that developers should invoke when needing guidance on code style, file structure, or naming conventions. Key mandatory rules include using Effect Schema for domain types, maintaining a 12px typography floor, and following specific directory layouts for library modules.

Quick Install

Claude Code

Recommended
Plugin CommandRecommended
/plugin add https://github.com/majiayu000/claude-skill-registry
Git CloneAlternative
git clone https://github.com/majiayu000/claude-skill-registry.git ~/.claude/skills/common-conventions

Copy and paste this command in Claude Code to install this skill

Documentation

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.tsUI 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}AtomresultsAtomState atom
{noun}sAtomlayersAtomCollection atom
{verb}AtomcomputedAtomDerived/computed atom
{feature}OpsAtomlayerOpsAtomOperation atoms (mutations)
{verb}OpsearchOp, clearOpIndividual operation

Services

PatternExampleUsage
{Feature}ServiceDataManagerServiceEffect.Service class
{Feature}ServiceShapeChannelServiceShapeInterface type
{Behavior}BehaviorLinearBehavior, DecibelBehaviorStrategy pattern

Hooks

PatternExampleUsage
use{Feature}useSliderMain feature hook
use{Feature}ValueuseSliderValueRead-only value hook
use{Feature}StateuseMinibufferStateState + updater
use{Feature}OpsuseOverlayOpsOperations only
useAtom{Feature}useAtomValueAtom-specific

Components

PatternExampleUsage
{Name}.tsxSlider.tsxComponent file
{Name}PropsSliderPropsProps interface
{Name}ContextDataGridContextContext type
{Name}ProviderCommandProviderContext provider

Machines

PatternExampleUsage
{feature}MachineminibufferMachineXState machine
{Feature}MachineMinibufferMachineType alias
{Feature}ActorMinibufferActorActorRef 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__/ directorysrc/lib/stx/__tests__/Multiple test files
.test.ts suffixservice.test.tsSingle test file
.bun.test.ts suffixeventlog-integration.bun.test.tsBun-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}.mdAgent handoffAI assistants
README.mdUser-facing docsDevelopers
ARCHITECTURE.mdDeep design analysisArchitects
AGENTS.{feature}.mdAgent-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 fileOverlayssrc/lib/overlays/index.ts
Service patternCommandssrc/lib/commands/service.ts
Atom organizationSlidersrc/lib/slider/v1/atoms/index.ts
Schema usageCommandssrc/lib/commands/types.ts
Hook namingCommandssrc/lib/commands/useCommandWire.tsx
Version strategySlidersrc/lib/slider/index.ts
Test organizationSTXsrc/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

GitHub Repository

majiayu000/claude-skill-registry
Path: skills/common-conventions

Related Skills

algorithmic-art

Meta

This Claude Skill creates original algorithmic art using p5.js with seeded randomness and interactive parameters. It generates .md files for algorithmic philosophies, plus .html and .js files for interactive generative art implementations. Use it when developers need to create flow fields, particle systems, or other computational art while avoiding copyright issues.

View skill

subagent-driven-development

Development

This skill executes implementation plans by dispatching a fresh subagent for each independent task, with code review between tasks. It enables fast iteration while maintaining quality gates through this review process. Use it when working on mostly independent tasks within the same session to ensure continuous progress with built-in quality checks.

View skill

executing-plans

Design

Use the executing-plans skill when you have a complete implementation plan to execute in controlled batches with review checkpoints. It loads and critically reviews the plan, then executes tasks in small batches (default 3 tasks) while reporting progress between each batch for architect review. This ensures systematic implementation with built-in quality control checkpoints.

View skill

cost-optimization

Other

This Claude Skill helps developers optimize cloud costs through resource rightsizing, tagging strategies, and spending analysis. It provides a framework for reducing cloud expenses and implementing cost governance across AWS, Azure, and GCP. Use it when you need to analyze infrastructure costs, right-size resources, or meet budget constraints.

View skill