playbook/antigravity-awesome-skills/skills/ecl-harness-engineer/references/environment-config-guide.md

17 KiB

Environment Configuration Guide

Guide for collecting complete environment information and generating harness/config/environment.json during harness creation.

Overview

environment.json is the contract between ecl-harness-engineer and harness-executor. It describes everything the executor needs to know to start the application, set up dependencies, and run verification — but it does NOT define what to verify. Verification configuration (verify.json) is dynamically generated by harness-executor at task runtime.

Key principle: ecl-harness-engineer answers "what does this project need to run?" — harness-executor answers "what should we check after making changes?"


environment.json Schema (v2.0)

{
  "version": "2.0",
  "project": {
    "name": "my-project",
    "type": "web-api | cli | frontend | library | hybrid",
    "language": "go | typescript | python | java | rust",
    "description": "Brief project description"
  },
  "runtime": {
    "language": "go",
    "version": "1.22",
    "package_manager": "go | npm | pnpm | yarn | pip | poetry | uv | maven | gradle",
    "build_command": "go build ./...",
    "test_command": "go test ./...",
    "lint_command": "make lint-arch"
  },
  "startup": {
    "command": "go run ./cmd/server",
    "args": ["--port", "${PORT:-8080}"],
    "working_dir": ".",
    "env": {
      "PORT": "${PORT:-8080}",
      "ENV": "development",
      "LOG_LEVEL": "${LOG_LEVEL:-debug}"
    },
    "readiness": {
      "type": "http",
      "config": {
        "endpoint": "/health",
        "port": "${PORT:-8080}",
        "expected_status": 200,
        "timeout_seconds": 30,
        "poll_interval_ms": 500
      }
    }
  },
  "services": [
    {
      "name": "postgres",
      "type": "database",
      "required": true,
      "image": "postgres:15",
      "ports": ["5432:5432"],
      "env": {
        "POSTGRES_USER": "${DB_USER:-postgres}",
        "POSTGRES_PASSWORD": "${DB_PASSWORD}",
        "POSTGRES_DB": "${DB_NAME:-app}"
      },
      "health_check": "pg_isready -U ${DB_USER:-postgres}",
      "connection": {
        "url_env": "DATABASE_URL",
        "default_url": "postgres://${DB_USER:-postgres}:${DB_PASSWORD}@localhost:5432/${DB_NAME:-app}?sslmode=disable"
      },
      "setup": {
        "migration_command": "go run ./cmd/migrate up",
        "seed_command": "go run ./cmd/seed"
      }
    }
  ],
  "env_vars": {
    "required": {
      "DATABASE_URL": {
        "purpose": "PostgreSQL connection string",
        "sensitive": true,
        "example": "postgres://user:pass@localhost:5432/dbname"
      },
      "JWT_SECRET": {
        "purpose": "JWT token signing key",
        "sensitive": true,
        "test_value_ok": true,
        "test_value": "test-secret-do-not-use-in-production"
      }
    },
    "optional": {
      "PORT": {
        "purpose": "HTTP server port",
        "default": "8080",
        "sensitive": false
      },
      "LOG_LEVEL": {
        "purpose": "Logging verbosity",
        "default": "info",
        "sensitive": false
      }
    }
  },
  "endpoints": {
    "health": "/health",
    "base_url": "http://localhost:${PORT:-8080}"
  },
  "scripts": {
    "setup": "harness/scripts/setup-env.sh",
    "start": "harness/scripts/start-server.sh",
    "teardown": "harness/scripts/teardown-env.sh"
  },
  "_meta": {
    "generated_by": "ecl-harness-engineer",
    "generated_at": "2026-03-30T10:00:00Z",
    "schema_version": "2.0",
    "requires_user_input": ["DATABASE_URL", "JWT_SECRET"],
    "todos": [
      "Confirm Redis connection if caching is needed"
    ]
  }
}

Detection Strategy (4-Step Pipeline)

Step 1: Detect Project Type and Language

# Language detection (high confidence)
test -f go.mod && echo "go"
test -f package.json && echo "typescript/javascript"
test -f pyproject.toml && echo "python"
test -f requirements.txt && echo "python"
test -f Cargo.toml && echo "rust"
test -f pom.xml && echo "java-maven"
test -f build.gradle && echo "java-gradle"

# Project type detection (medium confidence)
# Server indicators
grep -rq "http.ListenAndServe\|gin.Default\|chi.NewRouter\|echo.New" --include="*.go" . && echo "web-api"
grep -q '"express"\|"fastify"\|"koa"\|"hono"\|"nest"' package.json 2>/dev/null && echo "web-api"
grep -rq "FastAPI\|Flask\|Django" --include="*.py" . && echo "web-api"

# CLI indicators
test -d cmd/cli && echo "cli"
grep -rq "cobra\|urfave/cli" --include="*.go" . && echo "cli"
grep -q '"commander"\|"yargs"\|"oclif"' package.json 2>/dev/null && echo "cli"

# Frontend indicators
grep -q '"react"\|"vue"\|"svelte"\|"next"\|"nuxt"' package.json 2>/dev/null && echo "frontend"

# Library indicators (no entry point, exports only)
grep -q '"main"\|"bin"' package.json 2>/dev/null || echo "library"

Step 2: Detect Startup Command

Priority order — use the first successful detection:

Priority Source Command
1 Existing harness/config/environment.json jq .startup.command environment.json
2 Dockerfile CMD/ENTRYPOINT `grep -E "^(CMD
3 docker-compose.yml command grep "command:" docker-compose.yml
4 Makefile targets `grep -E "^(run
5 package.json scripts jq '.scripts.start // .scripts.dev' package.json
6 Go cmd/ directory ls cmd/*/main.gogo run ./cmd/<name>
7 Python main module test -f main.py && echo "python main.py"
8 User confirmation tool Required if all auto-detection fails; in Codex use request_user_input
# Go: Detect startup command
if test -d cmd/; then
    SERVER_CMD=""
    # Look for server-like directories
    for dir in cmd/*/; do
        name=$(basename "$dir")
        if echo "$name" | grep -qiE "server|api|web|app|service"; then
            SERVER_CMD="go run ./$dir"
            break
        fi
    done
    # If no server-like dir, check if only one cmd/ exists
    if [ -z "$SERVER_CMD" ]; then
        cmd_count=$(ls -d cmd/*/ 2>/dev/null | wc -l)
        if [ "$cmd_count" -eq 1 ]; then
            SERVER_CMD="go run ./$(ls -d cmd/*/)"
        fi
    fi
fi

# Node.js: Check package.json
if test -f package.json; then
    DEV_CMD=$(jq -r '.scripts.dev // empty' package.json 2>/dev/null)
    START_CMD=$(jq -r '.scripts.start // empty' package.json 2>/dev/null)
    # Detect package manager
    test -f pnpm-lock.yaml && PKG_MGR="pnpm"
    test -f yarn.lock && PKG_MGR="yarn"
    test -f bun.lockb && PKG_MGR="bun"
    PKG_MGR="${PKG_MGR:-npm}"
fi

# Python: Check for framework
if grep -q "FastAPI\|Flask" requirements.txt pyproject.toml 2>/dev/null; then
    # Look for uvicorn/gunicorn patterns
    grep -rn "uvicorn\|gunicorn" --include="*.py" . | head -1
fi

Step 3: Detect Service Dependencies

Scan these sources for service dependencies:

# Docker Compose (highest confidence)
if test -f docker-compose.yml; then
    # Extract service names and images
    grep -E "^\s+\w+:" docker-compose.yml | grep -v "version\|services"
    grep "image:" docker-compose.yml
fi

# Code imports (medium confidence)
# PostgreSQL
grep -rq "pgx\|pq\|database/sql.*postgres\|psycopg\|pg.*Pool\|sequelize.*postgres\|TypeORM.*postgres" . 2>/dev/null && echo "postgres detected"

# MySQL
grep -rq "mysql\|mariadb" --include="*.go" --include="*.py" --include="*.ts" . 2>/dev/null && echo "mysql detected"

# Redis
grep -rq "go-redis\|redigo\|redis\|ioredis\|bull" --include="*.go" --include="*.py" --include="*.ts" . 2>/dev/null && echo "redis detected"

# MongoDB
grep -rq "mongo\|bson\|mongoose" --include="*.go" --include="*.py" --include="*.ts" . 2>/dev/null && echo "mongodb detected"

# Kafka/RabbitMQ
grep -rq "kafka\|sarama\|confluent" . 2>/dev/null && echo "kafka detected"
grep -rq "rabbitmq\|amqp" . 2>/dev/null && echo "rabbitmq detected"

Step 4: Detect Environment Variables

# Scan .env.example or .env.sample
if test -f .env.example; then
    cat .env.example
elif test -f .env.sample; then
    cat .env.sample
fi

# Scan code for env var references
# Go
grep -rn "os.Getenv\|os.LookupEnv\|viper.Get" --include="*.go" . 2>/dev/null | head -30

# Node.js
grep -rn "process.env\." --include="*.ts" --include="*.js" . 2>/dev/null | head -30

# Python
grep -rn "os.environ\|os.getenv\|settings\." --include="*.py" . 2>/dev/null | head -30

# Detect sensitive variables
grep -rEi "(PASSWORD|SECRET|KEY|TOKEN|CREDENTIAL|AUTH)" --include="*.go" --include="*.ts" --include="*.py" . 2>/dev/null | grep -i "getenv\|environ\|process\.env\|viper" | head -20

Interactive Collection Flow (Mixed Mode)

Decision Matrix: When to Ask vs When to Auto-Fill vs When to Write TODO

Information Detectable? Critical? Action
Startup command Often yes Yes Auto-detect → if fail, ask with the platform user-confirmation tool immediately
Health endpoint Sometimes Yes Auto-detect → if fail, ask with the platform user-confirmation tool
Port Usually No Auto-detect → default 8080
Database type Often Yes (if code uses DB) Auto-detect → if fail, ask with the platform user-confirmation tool
DB connection URL No Yes Mark requires_user_input, use ${DATABASE_URL}
Redis/cache Sometimes No Auto-detect → write TODO if unclear
API keys No Depends Mark requires_user_input, use ${VAR_NAME}
Log level Yes (default) No Auto-fill with "info"
Feature flags Sometimes No Write TODO placeholder

User Confirmation Templates

In Codex, use request_user_input. On other platforms, use the equivalent user-confirmation tool. If no confirmation tool is available, record assumptions and required follow-up in environment.json.

Template 1: Startup Command (critical, must ask if not detected)

{
  "question": "Unable to automatically detect how to start this project. How is the application started for development?",
  "header": "Startup",
  "options": [
    {
      "label": "Custom command",
      "description": "I'll provide the specific command (e.g., 'go run ./cmd/server', 'npm run dev')"
    },
    {
      "label": "Docker Compose",
      "description": "The project uses docker-compose up to start everything"
    },
    {
      "label": "Makefile target",
      "description": "There's a Makefile with run/start/dev targets"
    },
    {
      "label": "Not applicable",
      "description": "This is a library/package — no startup command needed"
    }
  ]
}

Template 2: Database Dependency (critical if DB usage detected in code)

{
  "question": "Detected database usage in code ({detected_db_type}). Please confirm the database setup:",
  "header": "Database",
  "options": [
    {
      "label": "Docker container",
      "description": "Use Docker to run {db_type} locally (recommended for development)"
    },
    {
      "label": "Local installation",
      "description": "Database is installed directly on this machine"
    },
    {
      "label": "Remote/cloud",
      "description": "Database is hosted remotely (staging/dev environment)"
    },
    {
      "label": "SQLite/embedded",
      "description": "Use an embedded database for development/testing"
    }
  ]
}

Template 3: Sensitive Configuration (always ask if detected)

{
  "question": "Detected sensitive environment variables in code: {var_list}. These are needed for the application to run. How should they be configured?",
  "header": "Secrets",
  "options": [
    {
      "label": "Environment variables (recommended)",
      "description": "Reference via ${VAR_NAME} — you set them in your shell profile"
    },
    {
      "label": "Safe test values available",
      "description": "Some of these have safe test/development values that can be used"
    },
    {
      "label": "Config file reference",
      "description": "Reference a local config file like ~/.config/app/secrets.json"
    },
    {
      "label": "Skip for now",
      "description": "Mark as TODO — fill in later before running verification"
    }
  ]
}

Startup Scripts Generation

setup-env.sh

#!/usr/bin/env bash
# Environment setup script — starts required services
# Generated by ecl-harness-engineer, consumed by harness-executor
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"

echo "=== Setting up environment for {project_name} ==="

# Start services via Docker (if docker-compose exists)
if [ -f "$PROJECT_ROOT/docker-compose.yml" ]; then
    echo "Starting services via docker-compose..."
    docker-compose -f "$PROJECT_ROOT/docker-compose.yml" up -d
fi

# Or start individual services
# {auto-generated based on detected services}

# Wait for services to be ready
echo "Waiting for services..."
# {auto-generated health checks}

# Run migrations (if applicable)
# {auto-generated migration commands}

echo "=== Environment ready ==="

start-server.sh

#!/usr/bin/env bash
# Application startup script
# Generated by ecl-harness-engineer, consumed by harness-executor
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"

cd "$PROJECT_ROOT"

# Set default environment variables
export PORT="${PORT:-8080}"
export ENV="${ENV:-development}"
export LOG_LEVEL="${LOG_LEVEL:-debug}"

# Start the application
echo "Starting {project_name} on port $PORT..."
{startup_command}

teardown-env.sh

#!/usr/bin/env bash
# Environment teardown script
# Generated by ecl-harness-engineer, consumed by harness-executor
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"

echo "=== Tearing down environment ==="

# Stop Docker services
if [ -f "$PROJECT_ROOT/docker-compose.yml" ]; then
    docker-compose -f "$PROJECT_ROOT/docker-compose.yml" down -v
fi

# Clean up optional runtime verification artifacts when advanced tracing is enabled
if [ -d "$PROJECT_ROOT/harness/trace" ]; then
    rm -rf "$PROJECT_ROOT/harness/trace/verify-report.json" 2>/dev/null
    rm -rf "$PROJECT_ROOT/harness/trace/verification-report.json" 2>/dev/null
fi

echo "=== Environment cleaned up ==="

Readiness Check Types

Type When to Use Config
http Web API with health endpoint { "endpoint": "/health", "port": 8080, "expected_status": 200 }
tcp Service that listens on a port but no HTTP { "host": "localhost", "port": 5432 }
log_pattern Service that logs a "ready" message { "pattern": "Server listening on", "timeout_seconds": 30 }
process Just check the process is running { "command": "pgrep -f 'my-app'" }
none Library or no startup needed (omit readiness section)

Sensitive Configuration Security

Core rule: never hardcode sensitive values in environment.json or scripts.

Safe Patterns

Pattern Syntax Example
Environment variable ${VAR_NAME} "password": "${DB_PASSWORD}"
With default ${VAR_NAME:-default} "port": "${PORT:-8080}"
Config file ref $file:path:key "key": "$file:~/.config/app/secrets.json:api.key"

Detection and Marking

When sensitive variables are detected in code, mark them in _meta.requires_user_input:

{
  "_meta": {
    "requires_user_input": ["DATABASE_URL", "JWT_SECRET", "API_KEY"],
    "todos": [
      "Set DATABASE_URL environment variable before running verification",
      "Configure JWT_SECRET for authentication testing"
    ]
  }
}

What NOT to Put in environment.json

  • Actual passwords, API keys, tokens
  • Connection strings with embedded credentials
  • Test credentials that might be valid
  • Internal URLs that should not be in version control

Autonomous Mode (User Confirmation Tool Not Available)

When no user-confirmation tool is available:

  1. Auto-detect everything possible — use all detection strategies above
  2. Apply conservative defaults — use most common values
  3. Mark unknowns as TODO — never guess at critical config
  4. Document assumptions — explain what was detected and what's assumed
{
  "_meta": {
    "generated_by": "ecl-harness-engineer",
    "mode": "autonomous",
    "assumptions": [
      "Startup command inferred from cmd/server/main.go",
      "Port 8080 assumed (most common for Go web servers)",
      "PostgreSQL detected from pgx import in internal/storage/"
    ],
    "requires_user_input": ["DATABASE_URL"],
    "todos": [
      "Verify startup command is correct",
      "Confirm PostgreSQL connection details",
      "Set DATABASE_URL environment variable"
    ]
  }
}