# 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) ```json { "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": ["127.0.0.1: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 ```bash # 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|ENTRYPOINT)" Dockerfile` | | 3 | docker-compose.yml command | `grep "command:" docker-compose.yml` | | 4 | Makefile targets | `grep -E "^(run|start|serve|dev):" Makefile` | | 5 | package.json scripts | `jq '.scripts.start // .scripts.dev' package.json` | | 6 | Go cmd/ directory | `ls cmd/*/main.go` → `go run ./cmd/` | | 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` | ```bash # 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:** ```bash # 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 ```bash # 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)** ```json { "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)** ```json { "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)** ```json { "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 ```bash #!/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 ```bash #!/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 ```bash #!/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 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`: ```json { "_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 ```json { "_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" ] } } ```