platform-docs/technical/features/FEATURE_CONVENTIONS.md

14 KiB

Feature Conventions - Self-Contained Features

Last Updated: 2026-01-05


Overview

Every feature in the Lilith Platform is self-contained with complete, accurate package.json scripts. Features manage their own lifecycle (development, build, testing, database) without requiring manual setup or external orchestration.

Root convenience: The root codebase/package.json provides aggregated commands (e.g., pnpm build:analytics) that delegate to features via Turbo.


Quick Start

Running a Single Feature

# From feature directory
cd codebase/features/analytics/backend-api
pnpm dev              # Start development server
pnpm build            # Build the feature
pnpm test:unit        # Run unit tests
pnpm test:e2e         # Run E2E tests (starts DB, runs tests, tears down)
pnpm db:start         # Start database for development

Running from Root

# From codebase/ directory
pnpm dev:analytics            # Start analytics dev server
pnpm build:analytics          # Build analytics
pnpm test:unit:analytics      # Run analytics unit tests
pnpm test:e2e:analytics       # Run analytics E2E tests

# Run for all features
pnpm build:all                # Build all features
pnpm test:unit:all            # Run all unit tests
pnpm typecheck:all            # Type check all features

Standard Scripts

Every Feature Should Have

Script Purpose Example
build Compile/bundle the feature nest build, tsup, vite build
typecheck Validate TypeScript tsc --noEmit
test or test:unit Run unit tests vitest run, jest
lint Check code quality eslint "src/**/*.ts" --fix
clean Remove build artifacts rm -rf dist coverage

Development Scripts

Script Purpose Example
dev Start development server nest start --watch, vite
start Start production server node dist/main
start:debug Start with debugger nest start --debug --watch

Testing Scripts

Script Purpose When to Use
test:unit Unit tests only No external dependencies (DB, Redis, etc.)
test:watch Watch mode for tests Development TDD workflow
test:coverage Generate coverage report Check test coverage metrics
test:e2e End-to-end tests Full integration testing with real services
test:e2e:setup Setup E2E environment Start Docker services for E2E
test:e2e:teardown Teardown E2E environment Stop and remove Docker services
test:e2e:docker Run E2E in Docker (one command) CI/CD or local E2E testing

Database Scripts (for features with databases)

Script Purpose Example
db:start Start database containers docker compose up -d --wait
db:stop Stop database containers docker compose down
db:reset Reset database (drop + recreate + seed) docker compose down -v && ...
db:seed Seed database with dev data tsx scripts/seed-dev-data.ts
db:logs View database logs docker compose logs -f

Feature Types & Examples

Backend API (NestJS + PostgreSQL)

Example: codebase/features/analytics/backend-api/

Directory Structure:

analytics/backend-api/
├── package.json
├── docker-compose.yml          # Dev database
├── docker-compose.e2e.yml      # E2E testing infrastructure
├── src/                        # Source code
├── test/                       # Unit tests
├── e2e/                        # E2E tests
│   ├── playwright.config.ts
│   ├── seed.sql                # Test data
│   ├── Dockerfile.api          # API container for E2E
│   └── Dockerfile.e2e          # Test runner container
├── database/
│   ├── init.sql                # Schema initialization
│   └── migrations/             # Database migrations
└── scripts/
    └── seed-dev-data.ts

Scripts:

{
  "scripts": {
    "dev": "nest start --watch",
    "build": "nest build",
    "typecheck": "tsc --noEmit",
    "lint": "eslint \"{src,test}/**/*.ts\" --fix",
    "test:unit": "vitest run",
    "test:e2e": "pnpm run test:e2e:setup && playwright test && pnpm run test:e2e:teardown",
    "test:e2e:docker": "docker compose -f docker-compose.e2e.yml up --build --abort-on-container-exit && docker compose -f docker-compose.e2e.yml down -v",
    "db:start": "docker compose up -d --wait",
    "db:reset": "docker compose down -v && docker compose up -d --wait && pnpm run db:seed"
  }
}

Frontend (React + Vite)

Example: codebase/features/status-dashboard/frontend/

Build Tool: Vite (handles TypeScript transpilation internally via esbuild)

Directory Structure:

status-dashboard/frontend/
├── package.json
├── src/                        # Source code
├── test/                       # Unit tests
├── e2e/                        # E2E tests (optional)
└── public/

Scripts:

{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview",
    "typecheck": "tsc --noEmit",
    "lint": "eslint . --ext ts,tsx",
    "test:unit": "vitest run"
  }
}

Note: No tsc && prefix needed for build - Vite handles TS transpilation. Use typecheck for type validation.

Workspace Feature (Multi-Package)

Example: codebase/features/conversation-assistant/

Directory Structure:

conversation-assistant/
├── package.json                # Workspace root
├── pnpm-workspace.yaml
├── docker-compose.yml
├── docker-compose.e2e.yml
├── e2e/                        # E2E tests at workspace level
├── frontend-dev/
│   └── package.json
├── backend-api/
│   └── package.json
└── shared/
    └── package.json

Workspace Root Scripts:

{
  "workspaces": ["frontend-*", "backend-*", "shared"],
  "scripts": {
    "dev": "concurrently \"pnpm --filter frontend dev\" \"pnpm --filter backend dev\"",
    "build": "pnpm -r build",
    "typecheck": "pnpm -r typecheck",
    "test:unit": "pnpm -r test",
    "test:e2e": "pnpm run test:e2e:setup && playwright test && pnpm run test:e2e:teardown"
  }
}

Database Management

Dev Environment (docker-compose.yml)

Purpose: Persistent local development

Characteristics:

  • Named volumes (data persists across restarts)
  • Host ports exposed (accessible from host machine)
  • Restart policies (auto-restart on failure)
  • Init scripts (database/init.sql runs once on first start)

Usage:

pnpm db:start     # Start database (data persists)
pnpm db:stop      # Stop database (data persists)
pnpm db:reset     # Destroy data, recreate, reseed
pnpm dev          # Start app (connects to running DB)

E2E Environment (docker-compose.e2e.yml)

Purpose: Ephemeral test environment

Characteristics:

  • NO persistent volumes (data destroyed after tests)
  • Internal networking only (no host port exposure)
  • Health checks on all services (tests wait for ready state)
  • Includes API + database + tests in one orchestration

Usage:

pnpm test:e2e:docker      # One command: build → start → test → teardown
pnpm test:e2e             # Manual: setup → test → teardown

Example docker-compose.e2e.yml:

services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: e2e_user
      POSTGRES_PASSWORD: e2e_password
      POSTGRES_DB: feature_e2e
    volumes:
      - ./e2e/seed.sql:/docker-entrypoint-initdb.d/01-seed.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U e2e_user"]
      interval: 5s
      retries: 10

  api:
    build:
      dockerfile: e2e/Dockerfile.api
    depends_on:
      postgres:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:3000/health"]

  e2e-tests:
    build:
      dockerfile: e2e/Dockerfile.e2e
    depends_on:
      api:
        condition: service_healthy
    command: pnpm test:e2e

Test Frameworks

Vitest (Preferred)

Used by: Most features (status-dashboard, analytics, dating-autopilot)

Why Preferred:

  • Faster than Jest
  • Better DX (UI mode, watch mode)
  • ESM-native (no transform needed)
  • Vite-compatible

Scripts:

{
  "test": "vitest run",
  "test:unit": "vitest run",
  "test:watch": "vitest",
  "test:coverage": "vitest run --coverage"
}

Jest (Supported)

Used by: NestJS features with heavy decorators (conversation-assistant/backend-api, email/backend-api)

When to Use:

  • Existing codebase already uses Jest
  • Heavy use of NestJS decorators and metadata
  • Migration cost is high

Scripts:

{
  "test": "jest",
  "test:unit": "jest",
  "test:watch": "jest --watch",
  "test:coverage": "jest --coverage"
}

Root Aggregation Commands

How It Works

The root codebase/package.json contains aggregated commands generated by a Python script:

# Generate/update aggregated commands
pnpm test:generate

# List all features and their script status
pnpm test:list

# Validate feature compliance
pnpm test:validate

Generated Command Patterns

Build:

pnpm build:all                # Build all features
pnpm build:analytics          # Build analytics only
pnpm build:seo                # Build seo (workspace)

Type Checking:

pnpm typecheck:all            # Type check all features
pnpm typecheck:analytics      # Type check analytics only

Linting:

pnpm lint:all                 # Lint all features
pnpm lint:analytics           # Lint analytics only

Testing:

pnpm test:unit:all            # All unit tests
pnpm test:unit:analytics      # Analytics unit tests only
pnpm test:e2e:all             # All E2E tests
pnpm test:e2e:analytics       # Analytics E2E tests only

Development (individual features only):

pnpm dev:analytics            # Start analytics dev server
pnpm dev:seo                  # Start seo workspace dev servers

Implementation Details

Commands are generated via codebase/scripts/aggregate-feature-commands.py:

  • Scans features/*/package.json for available scripts
  • Detects workspace features (uses --filter=@lilith/feature* for workspaces)
  • Generates turbo commands with appropriate filters
  • Updates root package.json automatically

Creating a New Feature

Checklist

  1. Create directory structure:

    mkdir -p codebase/features/my-feature
    cd codebase/features/my-feature
    
  2. Initialize package.json with standard scripts (see examples above)

  3. If database feature:

    • Create docker-compose.yml (dev database)
    • Create database/init.sql (schema)
    • Add db:* scripts
  4. If needs E2E tests:

    • Create docker-compose.e2e.yml
    • Create e2e/ directory with Playwright config
    • Create e2e/seed.sql (test data)
    • Add test:e2e* scripts
  5. Test locally:

    pnpm build        # Should succeed
    pnpm test:unit    # Should run tests
    pnpm dev          # Should start (if applicable)
    
  6. Regenerate root commands:

    cd ../../  # Back to codebase/
    pnpm test:generate
    
  7. Verify root commands:

    pnpm build:my-feature    # Should work
    pnpm test:unit:my-feature  # Should work
    

Troubleshooting

"Command not found" from root

Problem: pnpm build:my-feature says command not found

Solution:

cd codebase
pnpm test:generate     # Regenerate aggregated commands

E2E tests failing to start

Problem: test:e2e hangs or fails to connect to database

Check:

  1. Is docker-compose.e2e.yml configured correctly?
  2. Are health checks defined for all services?
  3. Does e2e/seed.sql exist?
  4. Run docker compose -f docker-compose.e2e.yml up manually to debug

Unit tests require database

Problem: Unit tests failing because database isn't running

This is a bug: Unit tests should NEVER require external services. Use mocks:

// ❌ Bad: Real database
const db = await createConnection({...});

// ✅ Good: Mocked
const db = {
  query: vi.fn().mockResolvedValue([{ id: 1 }])
};


Quick Reference

Minimum Required Scripts

{
  "scripts": {
    "build": "...",        // ✅ Required
    "typecheck": "...",    // ✅ Required
    "test:unit": "..."     // ✅ Required
  }
}
{
  "scripts": {
    "dev": "...",          // If applicable
    "lint": "...",         // Recommended
    "clean": "...",        // Recommended
    "test:e2e": "..."      // If integration tests exist
  }
}

Database Feature Scripts

{
  "scripts": {
    "db:start": "docker compose up -d --wait",
    "db:stop": "docker compose down",
    "db:reset": "docker compose down -v && docker compose up -d --wait && pnpm run db:seed",
    "db:seed": "tsx scripts/seed-dev-data.ts"
  }
}

Questions? Check the technical reference or ask in the development channel.