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

119 lines
3.6 KiB
Markdown

# Answer Injection
Pre-supply answers and secrets to eliminate interactive prompts during headless execution.
## Usage
```bash
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
```json
{
"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
```bash
# 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}'
```