singularity-forge/sf-orchestrator/references/answer-injection.md
ace-pm 6b0ac484ba refactor: update log prefixes and string values from gsd- to sf- namespace
Updates channel prefixes, log messages, comments, and configuration values
across daemon, mcp-server, and related packages to complete the rebrand from
gsd to sf-run naming.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 15:37:12 +02:00

3.6 KiB

Answer Injection

Pre-supply answers and secrets to eliminate interactive prompts during headless execution.

Usage

sf headless --answers answers.json auto
sf headless --answers answers.json new-milestone --context spec.md --auto

The --answers flag takes a path to a JSON file containing pre-supplied answers and secrets.

Answer File Schema

{
  "questions": {
    "question_id": "selected_option_label",
    "multi_select_question": ["option_a", "option_b"]
  },
  "secrets": {
    "API_KEY": "sk-...",
    "DATABASE_URL": "postgres://..."
  },
  "defaults": {
    "strategy": "first_option"
  }
}

Fields

Field Type Description
questions Record<string, string | string[]> Map question ID → answer. String for single-select, string array for multi-select.
secrets Record<string, string> Map env var name → value. Injected into child process environment variables.
defaults.strategy "first_option" | "cancel" Fallback for unmatched questions. Default: "first_option".

How Secrets Work

Secrets are injected as environment variables into the SF child process:

  1. The orchestrator passes the answer file via --answers
  2. SF reads the file and sets secret values as env vars in the child process
  3. When secure_env_collect runs inside the agent, it finds the keys already in process.env
  4. The tool skips the interactive prompt and reports the keys as "already configured"

Secrets are never logged or included in event streams.

How Question Matching Works

Two-phase correlation:

  1. Observe — SF monitors tool_execution_start events for ask_user_questions to extract question metadata (ID, options, allowMultiple)
  2. Match — Subsequent extension_ui_request events are correlated to the metadata and responded to with the pre-supplied answer

Handles out-of-order events (extension_ui_request can arrive before tool_execution_start) via a deferred processing queue with 500ms timeout.

Coexistence with --supervised

Both --answers and --supervised can be active simultaneously. Priority order:

  1. Answer injector tries first
  2. If no answer found, supervised mode forwards to the orchestrator
  3. If no orchestrator response within --response-timeout, the auto-responder kicks in

Without Answer Injection

Headless mode has built-in auto-responders for all prompt types:

Prompt Type Default Behavior
Select Picks first option
Confirm Auto-confirms
Input Empty string
Editor Returns prefill or empty

Answer injection overrides these defaults with specific answers when precision matters.

Diagnostics

The injector tracks statistics printed in the session summary:

Stat Description
questionsAnswered Questions resolved from the answer file
questionsDefaulted Questions handled by the default strategy
secretsProvided Number of secrets injected

Unused question IDs and secret keys are warned about at exit.

Example: Orchestrator with Answers

# Create answer file
cat > answers.json << 'EOF'
{
  "questions": {
    "test_framework": "vitest",
    "package_manager": "pnpm"
  },
  "secrets": {
    "OPENAI_API_KEY": "sk-...",
    "DATABASE_URL": "postgres://localhost:5432/mydb"
  },
  "defaults": {
    "strategy": "first_option"
  }
}
EOF

# Run with pre-supplied answers
sf headless --answers answers.json --output-format json auto 2>/dev/null

# Parse result
RESULT=$(sf headless --answers answers.json --output-format json next 2>/dev/null)
echo "$RESULT" | jq '{status: .status, cost: .cost.total}'