# Environment Configuration Schema **Status**: Implemented and tested (25 test cases) **File**: `src/env.ts` **Tests**: `src/tests/env.test.ts` ## Overview SF uses 80+ `SF_*` environment variables to control behavior at startup and runtime. Previously, these were read directly from `process.env` throughout the codebase, leading to: - Silent failures when config was missing (no errors, just wrong behavior) - Type-unsafe access (IDE couldn't auto-complete, linters couldn't check) - No documentation about what variables exist or what they do - Scattered default logic (each module computed its own defaults) This schema provides **centralized, type-safe, validated** access to all SF configuration. ## Quick Start ### Using the env schema ```typescript import { getCompleteSfEnv } from "./env"; // Get fully validated, type-safe environment config const config = getCompleteSfEnv(); // IDE completion works: config.SF_DEBUG; // boolean config.SF_HOME; // string config.sfHome; // computed default config.stateDir; // computed default (SF_STATE_DIR or SF_HOME) ``` ### Setting variables ```bash # Enable debug mode export SF_DEBUG=1 # Set custom home directory export SF_HOME=/opt/sf # Disable RTK compression export SF_RTK_DISABLED=1 # Enable the machine surface with prompt tracing export SF_HEADLESS=1 export SF_HEADLESS_PROMPT_TRACE=1 ``` ## Schema Categories ### Core Paths (set by loader.ts) - `SF_PKG_ROOT` — Package installation root (where SF is installed) - `SF_BIN_PATH` — Path to the SF executable (used for spawning) - `SF_VERSION` — Package version from package.json - `SF_WORKFLOW_PATH` — Path to bundled SF-WORKFLOW.md - `SF_BUNDLED_EXTENSION_PATHS` — Serialized extension manifests - `SF_CODING_AGENT_DIR` — PI SDK agent directory ### Directories All directory variables are optional and have sensible defaults: - `SF_HOME` (default: `~/.sf`) — Root state directory - `SF_STATE_DIR` (default: `SF_HOME`) — Milestone/slice/task state - `SF_WORKSPACE_BASE` (default: `SF_STATE_DIR/workspace`) — User workspaces - `SF_HISTORY_BASE` (default: `SF_STATE_DIR/history`) — Session history - `SF_NOTIFICATIONS_BASE` (default: `SF_STATE_DIR/notifications`) — Notifications - `SF_SCHEDULE_FILE` (legacy import only; default: `SF_STATE_DIR/schedule.jsonl`) — pre-DB schedule queue compatibility input - `SF_RECOVERY_BASE` (default: `SF_STATE_DIR/recovery`) — Recovery artifacts - `SF_FORENSICS_BASE` (default: `SF_STATE_DIR/forensics`) — Diagnostics - `SF_SETTINGS_BASE` (default: `SF_STATE_DIR/settings`) — User settings - And 5+ more for specific recovery/export/cleanup artifacts ### Performance Tuning - `SF_RTK_DISABLED` (boolean: 0/1, default: 0) — Disable RTK compression - `SF_RTK_PATH` — Custom path to RTK tool (auto-detected) - `SF_RTK_REWRITE_TIMEOUT_MS` (integer, default: 5000) — Timeout in ms - `SF_CIRCUIT_BREAKER_OPEN_DURATION_MS` (integer, default: 60000) - `SF_CIRCUIT_BREAKER_FAILURE_THRESHOLD` (integer, default: 5) - `SF_CIRCUIT_BREAKER_HALF_OPEN_MAX_ATTEMPTS` (integer, default: 2) - `SF_HEADLESS_PROMPT_TRACE_CHARS` (integer, default: 1000) ### Debug Flags All debug flags are **0 or 1** (disabled or enabled): - `SF_QUIET` — Suppress startup banner - `SF_DEBUG` — Enable verbose logging - `SF_DEBUG_EXTENSIONS` — Enable extension debug logging - `SF_TRACE_ENABLED` — Collect execution traces - `SF_HEADLESS` — Suppress TUI for the machine surface, use stdio only - `SF_HEADLESS_PROMPT_TRACE` — Trace prompts in the machine surface - `SF_STARTUP_TIMING` — Measure cold-start latency - `SF_SHOW_TOKEN_COST` — Show LLM token costs - `SF_FIRST_RUN_BANNER` — Show first-run welcome - `SF_DISABLE_STARTUP_DOCTOR` — Skip health checks - `SF_ENGINE_BYPASS` — Use JS implementation instead of Rust - `SF_DISABLE_NATIVE_SF_PARSER` — Disable native parser - `SF_DISABLE_NATIVE_SF_GIT` — Disable native git ### Extensions - `SF_SKILL_MANIFEST_STRICT` (boolean) — Fail on invalid manifests - `SF_PERMISSION_LEVEL` (enum: `full`, `restricted`, `sandbox`, default: `sandbox`) - `SF_GEMINI_PERMISSION_MODE` (enum: `ask`, `auto`, `deny`, default: `ask`) - `SF_SESSION_BROWSER_DIR` — Override browser session directory - `SF_SESSION_BROWSER_CWD` — Override browser working directory - `SF_FETCH_ALLOWED_URLS` — Comma-separated list of allowed URLs - `SF_ALLOWED_COMMAND_PREFIXES` — Comma-separated command prefixes ### Recovery and Dispatch - `SF_RECOVERY_DOCTOR_MODULE` — Custom recovery doctor module - `SF_RECOVERY_FORENSICS_MODULE` — Custom forensics module - `SF_RECOVERY_SCOPE` (enum: `unit`, `milestone`, `global`, default: `unit`) - `SF_RECOVERY_SESSION_FILE` — Recovery session state path - `SF_RECOVERY_ACTIVITY_DIR` — Recovery activity logs - `SF_PARALLEL_WORKER` (boolean) — Enable parallel worker mode - `SF_WORKER_MODEL` — Model for worker dispatch - `SF_MILESTONE_LOCK` — Lock file for milestone operations - `SF_SLICE_LOCK` — Lock file for slice operations - `SF_WORKTREE` — Current git worktree - `SF_CLI_WORKTREE` — CLI worktree path - `SF_CLI_WORKTREE_BASE` — CLI worktree base directory - `SF_CLEANUP_BRANCHES` (boolean, default: 1) — Enable branch cleanup - `SF_CLEANUP_SNAPSHOTS` (boolean, default: 1) — Enable snapshot cleanup ### Settings Modules All optional (allow custom implementations): - `SF_SETTINGS_BUDGET_MODULE` — Custom budget settings - `SF_SETTINGS_HISTORY_MODULE` — Custom history settings - `SF_SETTINGS_METRICS_MODULE` — Custom metrics settings - `SF_SETTINGS_PREFS_MODULE` — Custom preferences settings - `SF_SETTINGS_ROUTER_MODULE` — Custom router settings - `SF_WORKSPACE_MODULE` — Custom workspace module - `SF_SESSION_MANAGER_MODULE` — Custom session manager ### Miscellaneous - `SF_TRIAGE_SUFFIX` (default: `_triage`) — Suffix for triaged issues - `SF_PROJECT_ID` — Current project ID (UUID) - `SF_DOCTOR_SCOPE` (enum: `fast`, `normal`, `deep`, default: `normal`) - `SF_EXPORT_FORMAT` (enum: `json`, `csv`, `markdown`, default: `json`) - `SF_TARGET_SESSION_NAME` — Target session for testing - `SF_TARGET_SESSION_PATH` — Target session path for testing - `SF_VISUALIZER_BASE` — Visualization output directory ## API Reference ### `getCompleteSfEnv(env?: NodeJS.ProcessEnv): CompleteSfEnv` **Primary entry point.** Returns fully validated environment configuration with computed defaults. ```typescript const config = getCompleteSfEnv(); // Type-safe access console.log(config.SF_DEBUG); // boolean console.log(config.SF_HOME); // string or undefined console.log(config.sfHome); // string (computed default) console.log(config.stateDir); // string (computed from SF_STATE_DIR || SF_HOME) console.log(config.agentDir); // string (computed from SF_AGENT_DIR || SF_CODING_AGENT_DIR || sfHome/agent) ``` ### `parseCompleteSfEnv(env?: NodeJS.ProcessEnv): CompleteSfEnv` **Alternative**: Parse environment with graceful degradation (doesn't throw on validation errors). ### `getSfEnv(env?: NodeJS.ProcessEnv): SfEnv` **Backward-compatible**: Parses minimal schema (original set of variables). Use `getCompleteSfEnv()` for new code. ### `getEnvValidationSummary(env?: NodeJS.ProcessEnv): { configured: string[], defaults: string[], total: number }` **For diagnostics**: Shows which variables are explicitly set vs using defaults. ```typescript const summary = getEnvValidationSummary(); console.log(`Configured: ${summary.configured.length}/${summary.total}`); console.log(`Using defaults: ${summary.defaults.length}`); ``` ## Schema Design ### Zod-based validation Uses [Zod](https://zod.dev) for composable, type-safe schema definition: ```typescript // Boolean flags (0 or 1) const booleanOneZero = z .enum(["0", "1"]) .transform((value) => value === "1") .optional(); // Positive integers (parsed from strings) const positiveInteger = z .string() .transform((v) => parseInt(v, 10)) .pipe(z.number().int().positive()); // Enums with defaults SF_PERMISSION_LEVEL: z.enum(["full", "restricted", "sandbox"]).optional() ``` ### Two-schema approach **Minimal schema** (`sfEnvSchema`): - Backward-compatible with existing code - 8 essential variables - Used by loader.ts, CLI entry points **Complete schema** (`completeSfEnvSchema`): - All 80+ known SF_* variables - Organized by category - Comprehensive validation and defaults - Used by modules needing full environment access ### Graceful degradation If validation fails: - `getCompleteSfEnv()` returns partial config (missing fields undefined) - No throws (never blocks dispatch) - Warnings logged to stderr if `SF_DEBUG=1` - Allows SF to run with misconfigured variables (degraded behavior) ## Testing All 25 tests passing. Coverage includes: - Boolean flag parsing (0 → false, 1 → true) - Enum validation (rejects invalid values) - Integer parsing and validation (positive only) - Default computation (SF_HOME, SF_STATE_DIR, agentDir) - Fallback behavior (graceful degradation) - Round-trip parsing consistency ```bash # Run tests npm run test:unit -- src/tests/env.test.ts ``` ## Migration Guide ### For existing code reading `process.env.SF_*` directly **Before**: ```typescript const debug = process.env.SF_DEBUG === "1"; const home = process.env.SF_HOME || join(homedir(), ".sf"); ``` **After**: ```typescript import { getCompleteSfEnv } from "./env"; const config = getCompleteSfEnv(); const debug = config.SF_DEBUG; // already parsed boolean const home = config.sfHome; // already computed default ``` ### For modules needing environment access 1. Import at module level: ```typescript import { getCompleteSfEnv } from "./env"; ``` 2. Call in initialization (not hot path): ```typescript const config = getCompleteSfEnv(); ``` 3. Pass config to functions instead of re-reading process.env ## Why This Matters **Problem**: Silent misconfiguration ```bash # Typo in env var name (SF_DEBG instead of SF_DEBUG) export SF_DEBG=1 # SF runs normally but without debug logging (silent failure) sf run ``` **Solution**: Centralized validation catches mistakes early ```typescript const config = getCompleteSfEnv(); // Now SF knows all 80+ valid variable names // Unknown variables can trigger warnings ``` **Benefit**: Type safety ```typescript // IDE auto-completion works config.SF_DEBUG // ✓ recognized config.SF_DEBG // ✗ compile error config.unknownVar // ✗ compile error // Future refactors are safe (rename variables with confidence) ``` ## Future Enhancements 1. **Config file support** (.sfrc.json with env override) 2. **Env schema generation** (export schema as JSON Schema for docs) 3. **Config diagnostics** (sf doctor --env shows all settings) 4. **Secrets redaction** (API keys not logged) 5. **Per-project overrides** (project-specific .sf/.env) ## See Also - `src/env.ts` — Implementation - `src/tests/env.test.ts` — Test suite - `.nvmrc` — Node.js version (requires Zod support)