Dev Environment Lifecycle (IaC)

Declarative dev environment management that ensures apps are always running, seeded, and testable β€” both on-demand and during warp-drive autonomous coding.

Overview

Every project declares its dev environment in dev.json at the project root. The dev-up command reads this manifest and orchestrates the full lifecycle: server management, migrations, seed data, and test user provisioning. It’s idempotent, composable, and integrates directly into warp-drive.

Quick Start

# 1. Copy the template to your project
cp ~/.claude/templates/dev.json ./dev.json
 
# 2. Customize for your project (edit server command, port, auth adapter, etc.)
vi dev.json
 
# 3. Set up seed data
mkdir -p seed/
cp ~/.claude/templates/seed/users.json ./seed/
cp ~/.claude/templates/seed/001-base-data.sql ./seed/
cp ~/.claude/templates/seed/002-sample-entities.sql ./seed/
# Customize the SQL for your schema
 
# 4. Run it
~/.claude/scripts/dev-lifecycle/dev-up.sh . --verbose

dev.json Manifest

{
  "server": {
    "command": "npm run dev",        // Command to start the dev server
    "port": 5173,                    // Port the server listens on
    "health": "/api/health",         // Health check endpoint path
    "restart": "on-failure",         // Restart strategy: "on-failure" | "always" | "never"
    "env": {}                        // Additional environment variables
  },
  "migrations": {
    "command": "npm run migrate:dev", // Migration command (null to skip)
    "auto_run": true                  // Run migrations automatically
  },
  "seed": {
    "directory": "seed/",            // Path to seed scripts
    "runner": "node",                // Script runner: "node" | "npx tsx" | custom
    "order": "alphabetical",         // Execution order
    "d1_database": "db"              // D1 database name for SQL seeds (if applicable)
  },
  "auth": {
    "adapter": "d1",                 // Auth adapter: "d1" | "sqlite" | "supabase" | "script" | "custom"
    "users_file": "seed/users.json", // Path to test user definitions
    "table": "users",                // Database table name for users
    "provision_command": null         // Custom provisioning command (for "custom" adapter)
  },
  "access": {
    "localhost": "http://localhost:5173", // Local access URL
    "farm_01": null                       // Remote dev server URL (if accessible)
  }
}

Scripts

All scripts live in ~/.claude/scripts/dev-lifecycle/ and follow the IaC prime directive: standalone, idempotent, parameterized, with --help.

ScriptPurposeUsage
dev-up.shFull lifecycle orchestratordev-up.sh [DIR] [--check|--verbose|--skip-*]
health-check.shLightweight health probehealth-check.sh [DIR] [--port N --path /path]
provision-users.shTest user provisioningprovision-users.sh [DIR] [--adapter TYPE]
check-seed-coverage.shSeed data coverage checkcheck-seed-coverage.sh [DIR]

dev-up.sh

The main orchestrator. Runs the full lifecycle:

  1. Server: Check health β†’ start if not running β†’ kill & restart if unhealthy
  2. Migrations: Run migration command if configured and auto_run is true
  3. Seed data: Execute all scripts in seed/ directory alphabetically
  4. Test users: Provision users via the configured auth adapter
  5. Report: Output JSON status and access URL

Flags:

  • --check β€” Health check only (no start/seed/users). Fast gate for warp-drive.
  • --skip-seed β€” Skip seed data scripts
  • --skip-users β€” Skip user provisioning
  • --skip-server β€” Skip server management (run seed/users only)
  • --verbose β€” Show detailed step-by-step output

health-check.sh

Lightweight standalone health probe. Returns JSON:

{"status": "healthy", "port": 5173, "url": "http://localhost:5173"}

provision-users.sh

Reads seed/users.json and provisions users via pluggable adapters:

AdapterHow it works
d1Generates SQL, executes via wrangler d1 execute --local
sqliteDirect sqlite3 INSERT OR REPLACE
supabaseDelegates to supabase db reset --local
scriptRuns project-local seed/provision-users.sh
customRuns auth.provision_command from dev.json

check-seed-coverage.sh

Advisory check: warns if migration/schema files changed but no seed files were modified. Used by warp-drive during the coding phase to remind about additive seed data.

Seed Data Convention

See ~/.claude/templates/seed/README.md for full details.

Key rules:

  • Files run in alphabetical order. Use numeric prefixes: 001-base.sql, 002-entities.sql
  • Every script MUST be idempotent (INSERT OR REPLACE, UPSERT, etc.)
  • Seed entities in ALL lifecycle states your domain defines β€” not just fresh/active records
  • New features add their own seed file in the same commit (e.g., 010-feature-invoicing.sql)

Standardized Test Users

Defined in seed/users.json. The superuser credentials are consistent across all projects:

RoleEmailPasswordPurpose
superuseradmin@test.localadmin123Full access, consistent across projects
editoreditor@test.localeditor123Read/write/publish access
viewerviewer@test.localviewer123Read-only access
guestguest@test.localguest123Minimal/no access

Projects should customize the role-specific users to match their permission model while keeping the superuser unchanged.

Warp-Drive Integration

When a project has dev.json, warp-drive automatically manages the dev environment:

PhaseAction
prerequisitesFull dev-up --verbose β€” start everything before coding begins
codingcheck-seed-coverage β€” advisory reminder if schema changes lack seed data
chunk_completedev-up --check β€” fast health gate before next chunk
chunk_complete (recovery)Full dev-up --verbose if health check fails

Dev health failure is treated as a blockable event. If recovery fails after 2 attempts, warp-drive escalates per its error protocol.

Projects without dev.json skip all dev lifecycle steps β€” the integration is opt-in.

Adopting in a Project

  1. Copy ~/.claude/templates/dev.json to your project root
  2. Customize the server command, port, and health endpoint
  3. Create seed/ directory with your initial seed scripts
  4. Copy ~/.claude/templates/seed/users.json and customize roles
  5. Set the auth adapter in dev.json to match your stack
  6. Run dev-up . --verbose to verify

The template at ~/.claude/templates/dev.json has sensible defaults for SvelteKit + Cloudflare Workers projects.

Docs Site Lifecycle (#153)

Every BoB-provisioned project can host a Quartz 4 docs site that reads its own docs/, README.md, and CLAUDE.md. The lifecycle is orthogonal to the dev environment lifecycle but follows the same declarative pattern.

Sub-commandScriptPurpose
/docs-site init~/.claude/scripts/docs-site/init.shScaffold .docs-site/ (Quartz 4 install, default quartz.config.ts, docs-site.json if missing). Idempotent on re-run.
/docs-site build~/.claude/scripts/docs-site/build.shSync content + run Quartz build. Output at .docs-site/public/.
/docs-site dev~/.claude/scripts/docs-site/dev.shSync content + Quartz dev server with hot reload (default port 8080).
/docs-site deploy~/.claude/scripts/docs-site/deploy.shBuild, then publish to the configured target (cloudflare-pages, static, custom).
/docs-site clean~/.claude/scripts/docs-site/clean.shDrop build artifacts (preserves config + Quartz install). --hard also drops the Quartz install.

Per-project config (docs-site.json at project root):

{
  "title": "Bodmail Docs",
  "description": "Documentation for the Bodmail email platform",
  "base_url": "https://docs.bodmail.app",
  "sources": ["docs/", "README.md", "CLAUDE.md"],
  "exclude": ["docs/archive/**", "docs/audits/**"],
  "deploy": { "target": "cloudflare-pages", "project": "bodmail-docs" }
}

Adopting in a project:

  1. cd /path/to/project && /docs-site init β€” scaffolds .docs-site/ and a default docs-site.json.
  2. Edit docs-site.json β€” set the title, base URL, and deploy target.
  3. /docs-site dev β€” preview at http://localhost:8080.
  4. /docs-site deploy when ready.

Wikilinks: Quartz’s Obsidian-flavored markdown plugin handles [[page]] links, including across the unified content tree (everything under .docs-site/content/ is one namespace, regardless of which source it came from).

Universal portability: the scripts read --root, $PROJECT_ROOT, or cwd. Smoke-tested in /tmp/docs-site-test (a non-BoB tree) β€” init.sh --no-clone scaffolds docs-site.json + quartz.config.ts + .gitignore additions correctly.

Auto-deploy on doc changes (#155). A GitHub Actions workflow at .github/workflows/docs-deploy.yml rebuilds and publishes the site whenever a published doc source changes (docs/**, README.md, CLAUDE.md, bob-identity.md, docs-site.json, the docs-site scripts, or the Quartz config). On master push it deploys to production; on PRs it deploys a preview and posts the URL back as a sticky comment.

The pipeline is:

checkout β†’ cache .docs-site/.quartz/node_modules
        β†’ docs-site init  (idempotent)
        β†’ make docs-check (high-severity drift fails the build, blocks deploy)
        β†’ docs-site build
        β†’ wrangler pages deploy β†’ bigbrain-docs (production OR pr-NN preview)
        β†’ comment preview URL on the PR

Required repo configuration:

  • Secret CLOUDFLARE_API_TOKEN β€” Cloudflare API token with Pages:Edit scope.
  • Secret CLOUDFLARE_ACCOUNT_ID β€” the Cloudflare account UUID.
  • Variable DOCS_SITE_DEPLOY_ENABLED=true β€” feature flag that gates the wrangler steps. Until it’s set, the workflow still builds and runs the drift gate (catching breakage), but skips the actual deploy. See #187 for the one-time Cloudflare Pages connection step.

NPM script aliases live in the root package.json so users can run npm run docs:build / docs:dev / docs:deploy from anywhere in the repo.

Drift report on the site (#156)

Every /doc-audit run writes two artifacts to docs/audits/:

  • doc-audit-YYYY-MM-DD.{md,json} β€” date-stamped historical record
  • latest.{md,json} β€” canonical aliases the docs site reads

The site (when built via /docs-site build) consumes docs/audits/latest.md for a β€œDocs last verified” badge on the landing page and an β€œAudits” entry in the nav. latest.json is exposed as a downstream API endpoint at /audits/latest.json. See README.md for the canonical-path contract and history convention.

The Quartz nav + landing-badge component wiring lands as a follow-up to this issue β€” the data plumbing (latest aliases + history) is in place.