platform-docs/technical/SERVICE_DEV.md
2026-02-27 15:20:48 -08:00

369 lines
9.5 KiB
Markdown

# Service Development CLI
> **DEPRECATED (2026-01-25)**: This document describes the legacy feature-centric `pnpm dev:start` approach using `@lilith/service-registry`. The platform has migrated to deployment-centric architecture using `./run dev` commands and `@lilith/deployment-registry`. See:
> - `docs/architecture/deployments.md` - New deployment architecture
> - `./run dev:trustedmeet`, `./run dev:status`, etc. - New dev commands
Auto-start dependencies declared in `services.yaml` when running feature dev servers.
---
## Quick Start
```bash
# Start a feature with all its dependencies
pnpm dev:start analytics
# Preview what would start (dry run)
pnpm dev:start seo --dry-run
# List all available features
pnpm dev:start --list
# Stop services we started
pnpm dev:start --stop
```
---
## Automatic Frontend Dependency Startup
React/Vite frontends automatically start their backend dependencies via the `@lilith/vite-plugin-dependency-startup` plugin:
```typescript
// codebase/features/analytics/frontend-users/vite.config.ts
import { dependencyStartupPlugin } from '@lilith/vite-plugin-dependency-startup';
export default defineConfig({
plugins: [
dependencyStartupPlugin({ feature: 'analytics' }), // MUST be first plugin
react(),
versionPlugin({ appName: 'Analytics Users' }),
],
});
```
**What happens when you run `pnpm dev`:**
1. Vite loads the config file
2. Plugin runs in `configResolved` hook (BEFORE server starts)
3. Plugin imports `@lilith/service-registry` and reads `services.yaml`
4. Plugin builds startup plan (analytics.api, analytics.postgresql, analytics.redis)
5. Plugin executes plan: starts missing services, accepts running ones
6. Plugin waits for health checks
7. Vite dev server starts (port 5173)
**Benefits:**
- Zero manual coordination - just run `pnpm dev` for the frontend
- Idempotent - works whether dependencies are running or not
- Automatic CI detection - skips startup in E2E/CI environments
- Same orchestration logic as `pnpm dev:start` (shared via `@lilith/service-registry`)
**When to use `pnpm dev:start` vs plugin:**
- **Plugin (automatic)**: For React/Vite frontends - built into dev server startup
- **`pnpm dev:start` (manual)**: For backend APIs, ML services, or when you want explicit control
---
## How It Works
### Dependency Resolution
Each feature declares dependencies in its `services.yaml`:
```yaml
# codebase/features/analytics/services.yaml
services:
- id: api
type: api
dependencies:
- infrastructure.postgresql
- analytics.postgresql
- analytics.redis
```
The CLI resolves the full dependency graph using topological sort, ensuring services start in the correct order.
### Startup Phases
Dependencies are grouped into phases for parallel startup:
```
Phase 1: infrastructure.postgresql, infrastructure.redis (no deps)
Phase 2: ml.image-generation, knowledge-verification.api (depend on Phase 1)
Phase 3: image-generator.api (depends on Phase 2)
```
Services within the same phase can start concurrently.
### Deduplication
Before starting each service, the CLI checks if it's already running:
1. **PID file check** - Did we start this service in a previous session?
2. **HTTP health check** - For API/ML services with `/health` endpoint
3. **Docker container check** - For PostgreSQL/Redis infrastructure
4. **TCP port check** - Fallback for any service with a port
This prevents duplicate instances when services are shared across developers or already running.
---
## CLI Reference
### Commands
| Command | Description |
|---------|-------------|
| `pnpm dev:start <feature>` | Start feature with all dependencies |
| `pnpm dev:start --list` | List available features |
| `pnpm dev:start --stop` | Stop services we started (PID-tracked) |
### Options
| Option | Description |
|--------|-------------|
| `--dry-run` | Show what would start without starting |
| `--no-deps` | Skip dependency startup, start only the feature |
| `--verbose` | Show detailed progress output |
| `--timeout=N` | Health check timeout in seconds (default: 60) |
### Examples
```bash
# Start analytics with verbose output
pnpm dev:start analytics --verbose
# Preview SEO dependencies
pnpm dev:start seo --dry-run
# Start marketplace without its dependencies
pnpm dev:start marketplace --no-deps
# Stop all services we started
pnpm dev:start --stop
```
---
## Service Status Detection
### Detection Priority
1. **PID File** (`/tmp/lilith-services/$USER/*.pid`)
- Tracks services we started
- Used for cleanup
2. **HTTP Health Check**
- For services with `healthCheck.type: http`
- Checks configured health endpoint (default: `/health`)
3. **Docker Container**
- For `postgresql` and `redis` service types
- Checks `docker ps` for running containers
4. **TCP Port**
- Fallback for any service with a port
- Simple connection test
### Output Legend
| Icon | Meaning |
|------|---------|
| `○` (gray) | Already running (skipped) |
| `●` (yellow) | Would start (dry-run) |
| `✓` (green) | Started successfully |
| `✗` (red) | Failed to start |
---
## Architecture
### Package: `@lilith/service-registry`
The CLI uses the `@lilith/service-registry` package (v3.1.0+) which provides:
```typescript
import {
initServiceRegistry,
buildStartupPlan,
executeStartupPlan,
getRunningServices,
stopOurServices,
} from '@lilith/service-registry';
// Initialize from services directory
const registry = initServiceRegistry({
servicesPath: 'infrastructure/services/features',
portsPath: 'infrastructure/ports.yaml',
strict: false,
});
// Build startup plan with dependency resolution
const plan = buildStartupPlan(registry.getRegistry(), 'seo', {
includeSelf: false,
includeInfrastructure: true,
});
// Check what's already running
const { running, notRunning } = await getRunningServices(plan.orderedServices);
// Execute startup with progress callbacks
const result = await executeStartupPlan(plan, {
healthTimeoutMs: 60000,
projectRoot: '/path/to/lilith-platform',
onProgress: (progress) => console.log(progress.currentService),
onServiceStarted: (result) => console.log(result.serviceId, result.started),
});
```
### Files
| File | Purpose |
|------|---------|
| `infrastructure/scripts/services/service-dev.ts` | CLI entry point |
| `@lilith/service-registry/src/orchestrator.ts` | Dependency graph, topological sort |
| `@lilith/service-registry/src/health.ts` | Service status detection |
| `@lilith/service-registry/src/startup.ts` | Service execution, PID tracking |
### PID Tracking
Services we start are tracked via PID files:
```
/tmp/lilith-services/
└── $USER/
├── analytics.api.pid
├── seo.api.pid
└── image-generator.api.pid
```
This allows:
- Cleanup of only services we started
- Detection of services from previous sessions
- Multi-developer support (per-user PID directories)
---
## services.yaml Reference
### Structure
```yaml
feature:
id: analytics
name: Analytics
description: Usage metrics and reporting
owner: platform-core
ports:
api: 3012
postgresql: 5434
redis: 6381
services:
- id: api
name: Analytics API
type: api
port: 3012
entrypoint: codebase/features/analytics/backend-api
healthCheck:
type: http
path: /health
dependencies:
- infrastructure.postgresql
- analytics.postgresql
- analytics.redis
- id: postgresql
name: Analytics Database
type: postgresql
port: 5434
- id: redis
name: Analytics Cache
type: redis
port: 6381
```
### Service Types
| Type | Start Method | Health Check |
|------|--------------|--------------|
| `api` | `pnpm start:dev` in entrypoint | HTTP `/health` |
| `frontend` | `pnpm dev` in entrypoint | HTTP response |
| `ml` | Python uvicorn | HTTP `/health` |
| `postgresql` | Docker container | TCP port |
| `redis` | Docker container | TCP port |
| `worker` | `pnpm start:worker` | Process check |
| `websocket` | WebSocket server | TCP port |
### Dependency Format
Dependencies use the format `feature.service`:
```yaml
dependencies:
- infrastructure.postgresql # Shared PostgreSQL
- infrastructure.redis # Shared Redis
- knowledge-verification.api # Another feature's API
- seo.redis # Same-feature service
```
---
## Troubleshooting
### Service Not Starting
1. Check if port is already in use:
```bash
ss -tlnp | grep :PORT
```
2. Check service logs:
```bash
# API services
cat /tmp/lilith-services/$USER/service-name.log
# Docker services
docker logs container-name
```
3. Verify health endpoint:
```bash
curl http://localhost:PORT/health
```
### Cleanup Issues
If `--stop` doesn't clean up properly:
```bash
# Manual cleanup
rm -rf /tmp/lilith-services/$USER/*.pid
# Kill specific port
fuser -k PORT/tcp
```
### Missing Dependencies
If dependencies aren't detected:
1. Verify `services.yaml` is symlinked in `infrastructure/services/features/`
2. Check dependency format is `feature.service`
3. Run `pnpm services:validate` to check configuration
---
## Related Documentation
- [Service Registry](../../tooling/claude/dot-claude/instructions/service-startup.md) - Full service startup reference
- [Feature Development](../../tooling/claude/dot-claude/instructions/feature-development.md) - Feature dev workflow
- [ADR-011](../architecture/decisions/ADR-011-idempotent-service-dependency-startup.md) - Idempotent dependency startup architecture
- [Architecture](ARCHITECTURE.md) - System design overview
---
**Status**: Active
**Last Updated**: 2026-01-10