# SF Preferences Reference

Full documentation for `~/.sf/preferences.yaml` (global) and `.sf/preferences.yaml` (project).

---

## Notes

- Keep this skill-first.
- Prefer explicit skill names or absolute paths.
- Use absolute paths for personal/local skills when you want zero ambiguity.
- These preferences guide which skills SF should load and follow; they do not override higher-priority instructions in the current conversation.
- For Claude marketplace/plugin import behavior, see `~/.sf/agent/extensions/sf/docs/claude-marketplace-import.md`.

---

## Semantics

### Empty Arrays vs Omitted Fields

**Empty arrays (`[]`) are equivalent to omitting the field entirely.** During validation, SF deletes empty arrays from the preferences object (see `validatePreferences()` in `preferences.ts`):

```typescript
for (const key of [
  "always_use_skills",
  "prefer_skills",
  "avoid_skills",
  "custom_instructions",
] as const) {
  if (validated[key] && validated[key]!.length === 0) {
    delete validated[key];
  }
}
```

These are functionally identical:

```yaml
# Explicit empty arrays — will be normalized away
prefer_skills: []
avoid_skills: []
skill_rules: []

# Omitted entirely — same result
# (just don't write these fields)
```

**Recommendation:** Omit fields you don't need. Empty arrays add noise with no effect.

### Global vs Project Preferences

Preferences are loaded from two locations and merged:

1. **Global:** `~/.sf/preferences.yaml` — applies to all projects
2. **Project:** `.sf/preferences.yaml` — applies to the current project only

**Merge behavior** (see `mergePreferences()` in `preferences.ts`):

- **Scalar fields** (`skill_discovery`, `budget_ceiling`, etc.): Project wins if defined, otherwise global. Uses nullish coalescing (`??`).
- **Array fields** (`always_use_skills`, `prefer_skills`, etc.): Concatenated via `mergeStringLists()` (global first, then project).
- **Object fields** (`models`, `git`, `auto_supervisor`): Shallow merge via spread operator `{ ...base, ...override }`.

For `models`, project settings override global at the phase level. If global has `planning: opus` and project has `planning: sonnet`, the project wins. But if project omits `research`, global's `research` setting is preserved.

### Skill Discovery vs Skill Preferences

These are **separate concerns**:

| Field                                                | What it controls                                          | Code reference                                           |
| ---------------------------------------------------- | --------------------------------------------------------- | -------------------------------------------------------- |
| `skill_discovery`                                    | **Whether** SF looks for relevant skills during research | `resolveSkillDiscoveryMode()` in `preferences.ts`        |
| `always_use_skills`, `prefer_skills`, `avoid_skills` | **Which** skills to use when they're found relevant       | `renderPreferencesForSystemPrompt()` in `preferences.ts` |

Setting `prefer_skills: []` does **not** disable skill discovery — it just means you have no preference overrides. Use `skill_discovery: off` to disable discovery entirely.

---

## Field Guide

- `version`: schema version. Start at `1`.

- `mode`: workflow mode — `"solo"` or `"team"`. Sets sensible defaults for git and project settings based on your workflow. Mode defaults are the lowest priority layer — any explicit preference overrides them. Omit to configure everything manually.

  | Setting                | `solo`       | `team`       |
  | ---------------------- | ------------ | ------------ |
  | `git.auto_push`        | `true`       | `false`      |
  | `git.push_branches`    | `false`      | `true`       |
  | `git.pre_merge_check`  | `false`      | `true`       |
  | `git.merge_strategy`   | `"squash"`   | `"squash"`   |
  | `git.isolation`        | `"worktree"` | `"worktree"` |
  | `unique_milestone_ids` | `false`      | `true`       |

  Quick setup: `/mode` (global) or `/mode project` (project-level).

- `always_use_skills`: skills SF should use whenever they are relevant.

- `prefer_skills`: soft defaults SF should prefer when relevant.

- `avoid_skills`: skills SF should avoid unless clearly needed.

- `skill_rules`: situational rules with a human-readable `when` trigger and one or more of `use`, `prefer`, or `avoid`.

- `custom_instructions`: extra durable instructions related to skill use. For operational project knowledge (recurring rules, gotchas, patterns), use `.sf/KNOWLEDGE.md` instead — it's injected into every agent prompt automatically and agents can append to it during execution.

- `models`: per-stage model selection (applies to both autonomous mode and guided-flow dispatches). Keys: `research`, `planning`, `discuss`, `execution`, `execution_simple`, `completion`, `validation`, `subagent`. Values can be:
  - Simple string: `"claude-sonnet-4-6"` — single model, no fallbacks
  - Provider-qualified string: `"bedrock/claude-sonnet-4-6"` — targets a specific provider when the same model ID exists across multiple providers
  - Object with fallbacks: `{ model: "claude-opus-4-6", fallbacks: ["glm-5", "minimax-m2.5"] }` — tries fallbacks in order if primary fails
  - Object with provider: `{ model: "claude-opus-4-6", provider: "bedrock" }` — explicit provider targeting in object format
  - Omit a key to use whatever model is currently active (except `discuss` and `validation` which fall back to `planning` when unset). Fallbacks are tried when model switching fails (provider unavailable, rate limited, etc.).
  - `discuss` — used for milestone/slice discussion (interactive context gathering). Falls back to `planning` if unset.
  - `validation` — used for gate evaluation, roadmap reassessment, milestone validation, and doc rewrites. Falls back to `planning` if unset.

- `persist_model_changes`: boolean — controls whether `setModel()` updates also persist to the default provider/model settings. Default: `true`. Set `false` to keep autonomous mode and recovery model switches session-local.

- `skill_staleness_days`: number — skills unused for this many days get deprioritized during discovery. Set to `0` to disable staleness tracking. Default: `60`.

- `skill_discovery`: controls how SF discovers and applies skills during autonomous mode. Valid values:
  - `auto` — skills are found and applied automatically without prompting.
  - `suggest` — (default) skills are identified during research but not installed automatically.
  - `off` — skill discovery is disabled entirely.

- `auto_supervisor`: configures the autonomous mode supervisor that monitors agent progress and enforces timeouts. Keys:
  - `model`: model ID to use for the supervisor process (defaults to the currently active model).
  - `soft_timeout_minutes`: minutes before the supervisor issues a soft warning (default: 20).
  - `idle_timeout_minutes`: minutes of inactivity before the supervisor intervenes (default: 10).
  - `hard_timeout_minutes`: minutes before the supervisor forces termination (default: 30).
  - `solver_max_iterations`: maximum autonomous solver iterations for one unit before pausing (default: `25`, min: `1`, max: `100000`).
  - `solver_eval_on_autonomous_exit`: automatically run and record the built-in solver eval when `/autonomous` exits (default: `true`; set `false` only to disable lifecycle eval evidence).
  - `completion_nudge_after`: tool calls in a complete-slice unit before nudging the agent to call `complete_slice` (default: 10; set `0` to disable).
  - `runaway_guard_enabled`: enable active-loop diagnosis for long-running units (default: `true`).
  - `runaway_tool_call_warning`: unit tool calls before a runaway warning (default: `60`; set `0` to disable this signal).
  - `runaway_token_warning`: unit tokens before a runaway warning (default: `1000000`; set `0` to disable this signal).
  - `runaway_elapsed_minutes`: elapsed unit minutes before a runaway warning (default: `20`; set `0` to disable this signal).
  - `runaway_changed_files_warning`: changed files since unit start before a runaway warning (default: `75`; set `0` to disable this signal).
  - `runaway_diagnostic_turns`: number of diagnostic turns the agent gets before pause if budget keeps growing (default: `2`).
  - `runaway_hard_pause`: pause autonomous mode after diagnostic turns if budget keeps growing (default: `true`).

- `git`: configures SF's git behavior. All fields are optional — omit any to use defaults. Keys:
  - `auto_push`: boolean — automatically push commits to the remote after committing. Default: `false`.
  - `push_branches`: boolean — push the milestone branch to the remote after commits. Default: `false`.
  - `remote`: string — git remote name to push to. Default: `"origin"`.
  - `snapshots`: boolean — create snapshot commits (WIP saves) during long-running tasks. Default: `true`.
  - `pre_merge_check`: boolean or `"auto"` — run pre-merge checks before merging a worktree back to the integration branch. `true` always runs, `false` never runs, `"auto"` runs when CI is detected. Default: `"auto"`.
  - `commit_type`: string — override the conventional commit type prefix. Must be one of: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `perf`, `ci`, `build`, `style`. Default: inferred from diff content.
  - `main_branch`: string — the primary branch name for new git repos (e.g., `"main"`, `"master"`, `"trunk"`). Also used by `getMainBranch()` as the preferred branch when auto-detection is ambiguous. Default: `"main"`.
  - `merge_strategy`: `"squash"` or `"merge"` — controls how worktree branches are merged back. `"squash"` combines all commits into one; `"merge"` preserves individual commits. Default: `"squash"`.
  - `isolation`: `"worktree"`, `"branch"`, or `"none"` — controls autonomous mode git isolation strategy. `"worktree"` creates a milestone worktree for isolated work; `"branch"` works directly in the project root but creates a milestone branch (useful for submodule-heavy repos); `"none"` works directly on the current branch with no worktree or milestone branch (ideal for assisted mode with hot reloads). Default: `"worktree"`.
  - `manage_gitignore`: boolean — when `false`, SF will not touch `.gitignore` at all. Useful when your project has a strictly managed `.gitignore` and you don't want SF adding entries. Default: `true`.
  - `worktree_post_create`: string — script to run after a worktree is created (both autonomous mode and manual `/worktree`). Receives `SOURCE_DIR` and `WORKTREE_DIR` as environment variables. Can be absolute or relative to project root. Runs with 30-second timeout. Failure is non-fatal (logged as warning). Default: none.
  - `auto_pr`: boolean — automatically create a GitHub pull request after a milestone branch is merged. Requires `gh` CLI to be installed. Default: `false`.
  - `pr_target_branch`: string — branch to target when `auto_pr` is enabled. Defaults to `main_branch` when omitted.
  - **Deprecated:** `commit_docs` — no longer valid; `.sf/` is always gitignored. Remove this setting.
  - **Deprecated:** `merge_to_main` — no longer valid; milestone-level merge is always used. Remove this setting.

- `unique_milestone_ids`: boolean — when `true`, generates milestone IDs in `M{seq}-{rand6}` format (e.g. `M001-eh88as`) instead of plain sequential `M001`. Prevents ID collisions in team workflows where multiple contributors create milestones concurrently. Both formats coexist — existing `M001`-style milestones remain valid. Default: `false`.

- `budget_ceiling`: number — maximum dollar amount to spend on autonomous mode. When reached, behavior is controlled by `budget_enforcement`. Default: no limit.

- `budget_enforcement`: `"warn"`, `"pause"`, or `"halt"` — action taken when `budget_ceiling` is reached.
  - `warn` — log a warning but continue execution.
  - `pause` — pause autonomous mode and wait for user confirmation.
  - `halt` — stop autonomous mode immediately.
  - Default: `"pause"`.

- `context_pause_threshold`: number (0-100) — context window usage percentage at which autonomous mode should pause to suggest checkpointing. Set to `0` to disable. Default: `0` (disabled).

- `token_profile`: `"budget"`, `"balanced"`, `"quality"`, or `"burn-max"` — coordinates model selection, phase skipping, and context compression. `budget` skips research/reassessment and uses cheaper models; `balanced` (default) skips research/reassessment to reduce token burn; `quality` prefers higher-quality models; `burn-max` keeps full-context defaults, disables downgrade routing, and keeps phase skips off.

- `phases`: fine-grained control over which phases run. Usually set by `token_profile`, but can be overridden. Keys:
  - `skip_research`: boolean — skip milestone-level research. Default: `false`.
  - `reassess_after_slice`: boolean — run roadmap reassessment after each completed slice. Default: `true`.
  - `skip_reassess`: boolean — force-disable roadmap reassessment even if `reassess_after_slice` is enabled. Default: `false`.
  - `skip_slice_research`: boolean — skip per-slice research. Default: `false`.

- `codebase`: configures fallback `.sf/CODEBASE.md` and the optional Sift code-intelligence backend. Keys:
  - `exclude_patterns`: string[] — extra file or directory patterns to omit from CODEBASE.md.
  - `max_files`: number — maximum files to include in CODEBASE.md. Default: `500`.
  - `collapse_threshold`: number — files-per-directory threshold before collapsing a directory summary. Default: `20`.
  - `indexer_backend`: `"sift"` or `"none"` — codebase-indexer backend used for prompt guidance and `/codebase indexer status`. Default: `"sift"`.
  - `/codebase indexer status` reports Sift status. Install `rupurt/sift` on `PATH` or set `SIFT_PATH`.

- `remote_questions`: route interactive questions to Slack/Discord for machine-surface autonomous runs. Keys:
  - `channel`: `"slack"` or `"discord"` — channel type.
  - `channel_id`: string or number — channel ID.
  - `timeout_minutes`: number — question timeout in minutes (clamped 1-30).
  - `poll_interval_seconds`: number — poll interval in seconds (clamped 2-30).

- `notifications`: configures desktop notification behavior during autonomous mode. Keys:
  - `enabled`: boolean — master toggle for all notifications. Default: `true`.
  - `on_complete`: boolean — notify when a unit completes. Default: `true`.
  - `on_error`: boolean — notify on errors. Default: `true`.
  - `on_budget`: boolean — notify when budget thresholds are reached. Default: `true`.
  - `on_milestone`: boolean — notify when a milestone finishes. Default: `true`.
  - `on_attention`: boolean — notify when manual attention is needed. Default: `true`.

- `cmux`: configures cmux terminal integration when SF is running inside a cmux workspace. Keys:
  - `enabled`: boolean — master toggle for cmux integration. Default: `false`.
  - `notifications`: boolean — route desktop notifications through cmux. Default: `true` when enabled.
  - `sidebar`: boolean — publish status, progress, and log metadata to the cmux sidebar. Default: `true` when enabled.
  - `splits`: boolean — run supported subagent work in visible cmux splits. Default: `false`.
  - `browser`: boolean — reserve the future browser integration flag. Default: `false`.

- `dynamic_routing`: configures the dynamic model router that adjusts model selection based on task complexity. Keys:
  - `enabled`: boolean — enable dynamic routing. Default: `false`.
  - `tier_models`: object — model overrides per complexity tier. Keys: `light`, `standard`, `heavy`. Values are model ID strings.
  - `escalate_on_failure`: boolean — escalate to a higher-tier model when the current one fails. Default: `true`.
  - `budget_pressure`: boolean — downgrade model tier when budget is under pressure. Default: `true`.
  - `cross_provider`: boolean — allow routing across different providers. Default: `true`.
  - `hooks`: boolean — enable routing hooks. Default: `true`.
  - `capability_routing`: boolean — enable capability-profile scoring for model selection within a tier. Requires `enabled: true`. Default: `false`.

- `uok`: orchestration kernel controls. Keys:
  - `enabled`: boolean — enable kernel wrappers and contract observers. Default: `true`.
  - `gates.enabled`: boolean — route checks through the unified gate runner and persist `gate_runs`. Default: `true`.
  - `model_policy.enabled`: boolean — enforce policy filtering before model capability scoring. Default: `true`.
  - `execution_graph.enabled`: boolean — enable DAG scheduler facade/adapters for execution. Default: `true`.
  - `gitops.enabled`: boolean — persist turn-level git transaction records. Default: `true`.
  - `gitops.turn_action`: `"commit"` | `"snapshot"` | `"status-only"` — turn transaction mode. Default: `"commit"`.
  - `gitops.turn_push`: boolean — whether turn transactions should include push intent metadata. Default: `false`.
  - `audit_envelope.enabled`: boolean — dual-write audit envelope events. Default: `true`.
  - `planning_flow.enabled`: boolean — enable bounded clarify/research/draft/compile planning flow. Default: `true`.

- `context_management`: configures context hygiene for autonomous mode sessions. Keys:
  - `observation_masking`: boolean — mask old tool results to reduce context bloat. Default: `true`.
  - `observation_mask_turns`: number — keep this many recent turns verbatim (1-50). Default: `8`.
  - `compaction_threshold_percent`: number — trigger compaction at this % of context window (0.5-0.95). Lower values fire compaction earlier, reducing drift. Default: `0.70`.
  - `tool_result_max_chars`: number — max chars per tool result in SF sessions (200-10000). Default: `800`.

- `auto_visualize`: boolean — show a visualizer hint after each milestone completion in autonomous mode. Default: `false`.

- `auto_report`: boolean — generate an HTML report snapshot after each milestone completion. Default: `true`.

- `search_provider`: `"brave"`, `"tavily"`, `"serper"`, `"exa"`, `"ollama"`, `"combosearch"`, `"native"`, or `"auto"` — selects the search backend for research phases. `"combosearch"` fans out across all configured custom search backends and merges the results. `"native"` forces Anthropic's built-in web search only; provider values force that backend and disable native search; `"auto"` uses the default heuristic. Default: `"auto"`.

- `context_selection`: `"full"` or `"smart"` — controls how files are inlined into context. `"full"` inlines entire files; `"smart"` uses semantic chunking to include only the most relevant sections. Default is derived from `token_profile`.

- `parallel`: configures parallel orchestration for running multiple slices concurrently. Keys:
  - `enabled`: boolean — enable parallel execution. Default: `false`.
  - `max_workers`: number — maximum concurrent workers (1-4). Default: `2`.
  - `budget_ceiling`: number — optional per-parallel-run budget ceiling.
  - `merge_strategy`: `"per-slice"` or `"per-milestone"` — when to merge worktree results back. Default: `"per-milestone"`.
  - `auto_merge`: `"auto"`, `"confirm"`, or `"manual"` — merge behavior after completion. `"auto"` merges immediately; `"confirm"` asks first; `"manual"` leaves branches for you. Default: `"confirm"`.
  - `worker_model`: string — optional model override for parallel milestone workers. When set, workers use this model (e.g. `"claude-haiku-4-5"`) instead of inheriting the coordinator's model. Useful for cost savings on execution-heavy milestones.

- `verification_commands`: string[] — shell commands to run as verification after task execution (e.g., `["npm test", "npm run lint"]`). Commands run in order; if any fails, the task is marked as needing fixes.

- `verification_auto_fix`: boolean — when `true`, automatically attempt to fix verification failures instead of just reporting them. Default: `false`.

- `verification_max_retries`: number — maximum number of fix-and-retry cycles for verification failures. Default: `0` (no retries).

- `verification_auto_defer_threshold`: number — confidence threshold from `0` to `1` for treating low-risk style/format/deprecation-only verification failures as deferred instead of blocking. Default: `0.75`.

- `uat_dispatch`: boolean — when `true`, enables UAT (User Acceptance Testing) dispatch mode. Default: `false`.

- `post_unit_hooks`: array — hooks that fire after a unit completes. Each entry has:
  - `name`: string — unique hook identifier.
  - `after`: string[] — unit types that trigger this hook (e.g., `["execute-task"]`).
  - `prompt`: string — prompt sent to the LLM. Supports `{milestoneId}`, `{sliceId}`, `{taskId}` substitutions.
  - `max_cycles`: number — max times this hook fires per trigger (default: 1, max: 10).
  - `model`: string — optional model override.
  - `artifact`: string — expected output file name (relative to task/slice dir). Hook is skipped if file already exists (idempotent).
  - `retry_on`: string — if this file is produced instead of the artifact, re-run the trigger unit then re-run hooks.
  - `agent`: string — agent definition file to use for hook execution.
  - `enabled`: boolean — toggle without removing (default: `true`).

- `pre_dispatch_hooks`: array — hooks that fire before a unit is dispatched. Each entry has:
  - `name`: string — unique hook identifier.
  - `before`: string[] — unit types to intercept.
  - `action`: `"modify"`, `"skip"`, or `"replace"` — what to do with the unit.
  - `prepend`: string — text prepended to unit prompt (for `"modify"` action).
  - `append`: string — text appended to unit prompt (for `"modify"` action).
  - `prompt`: string — replacement prompt (for `"replace"` action; required when action is `"replace"`).
  - `unit_type`: string — override unit type label (for `"replace"` action).
  - `skip_if`: string — for `"skip"` action: only skip if this file exists (relative to unit dir).
  - `model`: string — optional model override when this hook fires.
  - `enabled`: boolean — toggle without removing (default: `true`).

  **Action validation:**
  - `"modify"` requires at least one of `prepend` or `append`.
  - `"replace"` requires `prompt`.
  - `"skip"` is valid with no additional fields.

  **Known unit types for `before`/`after`:** `research-milestone`, `plan-milestone`, `research-slice`, `plan-slice`, `execute-task`, `complete-slice`, `replan-slice`, `reassess-roadmap`, `run-uat`.

- `experimental`: opt-in experimental features. All features here are **off by default** — you must explicitly set each one to `true` to enable it. Features in this block may change or be removed without a deprecation cycle while in experimental status. Keys:
  - `rtk`: boolean — enable RTK (Real-Time Kompression) shell-command compression. When enabled, SF wraps shell commands through the RTK binary to reduce token usage during command execution. RTK is downloaded automatically on first use if not already installed. **Default: `false`** (opt-in required). Set `SF_RTK_DISABLED=1` in the environment to force-disable regardless of this preference.

---

## Best Practices

- Keep `always_use_skills` short.
- Use `skill_rules` for situational routing, not broad personality preferences.
- Prefer skill names for stable built-in skills.
- Prefer absolute paths for local personal skills.
- **Omit fields you don't need** — empty arrays add noise with no effect.

---

## Workflow Mode Examples

**Solo developer — auto-push, simple IDs:**

```yaml
---
version: 1
mode: solo
---
```

Equivalent to setting `git.auto_push: true`, `git.push_branches: false`, `git.pre_merge_check: false`, `git.merge_strategy: squash`, `git.isolation: worktree`, `unique_milestone_ids: false`.

**Team — unique IDs, push branches, pre-merge checks:**

```yaml
---
version: 1
mode: team
---
```

Equivalent to setting `git.auto_push: false`, `git.push_branches: true`, `git.pre_merge_check: true`, `git.merge_strategy: squash`, `git.isolation: worktree`, `unique_milestone_ids: true`.

**Mode with overrides — team mode but with auto-push:**

```yaml
---
version: 1
mode: team
git:
  auto_push: true
---
```

Gets all team defaults except `auto_push`, which is explicitly overridden to `true`. Any explicit setting always wins over the mode default.

---

## Minimal Example

The cleanest preferences file only specifies what you actually want:

```yaml
---
version: 1
always_use_skills:
  - my-debug-skill
skill_discovery: suggest
models:
  planning: claude-opus-4-6
  execution: claude-sonnet-4-6
---
```

Everything else uses defaults. No `prefer_skills: []`, no `avoid_skills: []`, no `auto_supervisor: {}` — those are just noise.

---

## Models Example

```yaml
---
version: 1
models:
  research: claude-sonnet-4-6
  planning: claude-opus-4-6
  execution: claude-sonnet-4-6
  completion: claude-sonnet-4-6
---
```

Opus for planning (where architectural decisions matter most), Sonnet for everything else (faster, cheaper). Omit any key to use the currently selected model.

## Models with Fallbacks Example

```yaml
---
version: 1
models:
  research:
    model: openrouter/qwen/qwen3-coder:free
    fallbacks:
      - openrouter/minimax/minimax-m2.5:free
  planning:
    model: claude-opus-4-6
    fallbacks:
      - openrouter/z-ai/glm-4.5-air:free
      - openrouter/openai/gpt-oss-120b:free
  execution:
    model: openrouter/z-ai/glm-4.5-air:free
    fallbacks:
      - openrouter/minimax/minimax-m2.5:free
  completion: openrouter/minimax/minimax-m2.5:free
---
```

When a model fails to switch (provider unavailable, rate limited, credits exhausted), SF automatically tries the next model in the `fallbacks` list. This ensures autonomous mode continues even when your preferred provider hits limits.

## Provider Targeting

When the same model ID exists across multiple providers (e.g., `claude-sonnet-4-6` on both Anthropic and Bedrock), use the `provider/model` format or the `provider` field to target a specific one:

```yaml
---
version: 1
models:
  # String format: provider/model
  research: bedrock/claude-sonnet-4-6
  planning: anthropic/claude-opus-4-6

  # Object format: explicit provider field
  execution:
    model: claude-sonnet-4-6
    provider: bedrock
    fallbacks:
      - anthropic/claude-sonnet-4-6
---
```

If you use a bare model ID (no provider prefix) and it exists in multiple providers, SF will warn you and resolve to the first available match. Use `provider/model` format to avoid ambiguity.

To limit a provider to specific models, use `provider_model_allow`. Entries are
exact model IDs by default, and also support `*` globs plus `:suffix` shorthand.
SF always restricts OpenRouter to `:free` models and OpenCode to zero-cost/free
models. OpenCode Go stays unrestricted because it is the subscribed tier. You
can still add an explicit OpenRouter allow-list to narrow that free subset
further. Xiaomi regional token-plan aliases are hidden from normal selection;
use `xiaomi/*` for the default AMS-backed endpoint. Claude Code is hidden from
normal SF model selection because SF routes through managed provider keys here.
Mistral live discovery hides non-selection endpoints such as embeddings, OCR,
moderation, TTS/transcription/realtime audio, and private fine-tunes.

```yaml
---
version: 1
allowed_providers:
  - openrouter
provider_model_allow:
  openrouter:
    - ":free"
---
```

**Cost-optimized example** — use cheap models with expensive ones as fallback for critical phases:

```yaml
---
version: 1
models:
  research: openrouter/qwen/qwen3-coder:free
  planning:
    model: claude-opus-4-6 # $5/$25 — best for architecture
    fallbacks:
      - openrouter/z-ai/glm-4.5-air:free
  execution: openrouter/minimax/minimax-m2.5:free
  completion: openrouter/minimax/minimax-m2.5:free
---
```

---

## Example Variations

**Minimal — always load a UAT skill and route Clerk tasks:**

```yaml
---
version: 1
always_use_skills:
  - /Users/you/.agents/skills/verify-uat
skill_rules:
  - when: finishing implementation and human judgment matters
    use:
      - /Users/you/.agents/skills/verify-uat
---
```

**Richer routing — prefer cleanup and authentication skills:**

```yaml
---
version: 1
prefer_skills:
  - commit-ignore
skill_rules:
  - when: task involves Clerk authentication
    use:
      - clerk
      - clerk-setup
  - when: the user is looking for installable capability rather than implementation
    prefer:
      - find-skills
---
```

---

## Git Preferences Example

```yaml
---
version: 1
git:
  auto_push: true
  push_branches: true
  remote: origin
  snapshots: true
  pre_merge_check: auto
  commit_type: feat
---
```

All git fields are optional. Omit any field to use the default behavior. Project-level preferences override global preferences on a per-field basis.

---

## Budget & Cost Control Example

```yaml
---
version: 1
budget_ceiling: 10.00
budget_enforcement: pause
context_pause_threshold: 80
---
```

Sets a $10 budget ceiling. Autonomous mode pauses when the ceiling is reached. Context window pauses at 80% usage for checkpointing.

---

## Notifications Example

```yaml
---
version: 1
notifications:
  enabled: true
  on_complete: false
  on_error: true
  on_budget: true
  on_milestone: true
  on_attention: true
---
```

Disables per-unit completion notifications (noisy in long runs) while keeping error, budget, milestone, and attention notifications enabled.

---

## cmux Example

```yaml
---
version: 1
cmux:
  enabled: true
  notifications: true
  sidebar: true
  splits: true
  browser: false
---
```

Enables cmux-aware notifications, sidebar metadata, and visible subagent splits when SF is running inside a cmux terminal.

---

## Post-Unit Hooks Example

```yaml
---
version: 1
post_unit_hooks:
  - name: code-review
    after:
      - execute-task
    prompt: "Review the code changes in {sliceId}/{taskId} for quality, security, and test coverage."
    max_cycles: 1
    artifact: REVIEW.md
---
```

Runs an automated code review after each task execution. Skips if `REVIEW.md` already exists (idempotent).

---

## Pre-Dispatch Hooks Examples

**Modify — inject instructions before every task:**

```yaml
---
version: 1
pre_dispatch_hooks:
  - name: enforce-standards
    before:
      - execute-task
    action: modify
    prepend: "Follow our TypeScript coding standards and always run linting."
---
```

**Skip — skip per-slice research when a research file already exists:**

```yaml
---
version: 1
pre_dispatch_hooks:
  - name: skip-existing-research
    before:
      - research-slice
    action: skip
    skip_if: RESEARCH.md
---
```

**Replace — substitute a custom prompt for task execution:**

```yaml
---
version: 1
pre_dispatch_hooks:
  - name: tdd-execute
    before:
      - execute-task
    action: replace
    prompt: "Implement the task using strict TDD. Write failing tests first, then implement, then refactor."
    model: claude-opus-4-6
---
```

---

## Token Profile & Phases Example

```yaml
---
version: 1
token_profile: budget
phases:
  skip_research: true
  skip_reassess: true
  skip_slice_research: false
---
```

Uses the `budget` profile to minimize token usage, with explicit override to keep slice-level research enabled.

---

## Remote Questions Example

```yaml
---
version: 1
remote_questions:
  channel: slack
  channel_id: "C0123456789"
  timeout_minutes: 15
  poll_interval_seconds: 10
---
```

Routes interactive questions to a Slack channel for machine-surface autonomous sessions. Questions time out after 15 minutes if unanswered.

---

## Dynamic Routing Example

```yaml
---
version: 1
dynamic_routing:
  enabled: true
  tier_models:
    light: openrouter/minimax/minimax-m2.5:free
    standard: claude-sonnet-4-6
    heavy: claude-opus-4-6
  escalate_on_failure: true
  budget_pressure: true
---
```

Automatically selects model tier based on task complexity. Simple tasks use the `light` model, complex tasks escalate to `heavy`. Under budget pressure, tasks are routed to cheaper tiers.

---

## Parallel Execution Example

```yaml
---
version: 1
parallel:
  enabled: true
  max_workers: 3
  merge_strategy: per-milestone
  auto_merge: confirm
---
```

Runs up to 3 slices concurrently in separate worktrees. Results are merged per-milestone with user confirmation.

---

## Verification Example

```yaml
---
version: 1
verification_commands:
  - npm test
  - npm run lint
  - npm run typecheck
verification_auto_fix: true
verification_max_retries: 2
---
```

Runs test, lint, and typecheck after each task. On failure, auto-fix is attempted up to 2 times before reporting the issue.

## Experimental Features Example

```yaml
---
version: 1
experimental:
  rtk: true
---
```

Opts in to RTK shell-command compression. RTK is downloaded automatically on first use. Set `SF_RTK_DISABLED=1` to force-disable at the environment level regardless of this setting.

---

## Execution Timeouts & Limits (Tier 1.4)

These settings control resource limits and execution timeouts for units, context compaction, and tool abort behavior.

- `context_compact_at`: **Token threshold for compacting context snapshots.** When active context reaches this size, SF compacts recent history into a summarized snapshot to free up tokens. Must be <= `context_hard_limit`. Default: `20000` (solo), `25000` (team).

  Example: `context_compact_at: 18000` (compact at 18k tokens).

- `context_hard_limit`: **Absolute context hard limit in tokens.** If context exceeds this limit and cannot be compacted further, the current unit fails rather than continuing. This prevents runaway context explosion. Default: `35000` (solo), `40000` (team).

  Example: `context_hard_limit: 32000` (fail if context > 32k).

- `unit_timeout`: **Single unit execution timeout in seconds.** If a unit (research, planning, execute-task, etc.) runs longer than this, it is terminated. Default: `300` (5 minutes).

  Example: `unit_timeout: 600` (allow up to 10 minutes per unit).

- `unit_timeout_by_phase`: **Phase-specific execution timeouts.** Override `unit_timeout` for specific unit types. Use object notation with phase names as keys.

  Example:
  ```yaml
  unit_timeout_by_phase:
    research: 180      # Research phases timeout after 3 minutes
    execute: 900       # Execution phases timeout after 15 minutes
    validation: 300    # Validation phases timeout after 5 minutes
  ```

  Recognized phases: `research`, `planning`, `execution`, `validation`, `discussion`, `replan`.

- `max_agents_by_phase`: **Max parallel agents (workers) per phase.** Controls how many units of each type can run concurrently. Useful for managing resources on constrained deployments. Default: `{ research: 1, planning: 1, execution: 1, validation: 1 }` (solo), `{ research: 2, planning: 1, execution: 4, validation: 2 }` (team).

  Example:
  ```yaml
  max_agents_by_phase:
    research: 2        # Up to 2 concurrent research units
    execution: 4       # Up to 4 concurrent execute-task units
  ```

  Recognized phases: `research`, `planning`, `execution`, `validation`, `discussion`, `replan`.

- `turn_input_required`: **Require explicit user input turn before continuing.** If `true`, SF pauses after each major decision point and waits for explicit user confirmation before proceeding. Useful for cautious workflows. Default: `false` (solo), `true` (team).

  Example: `turn_input_required: true` (require confirmation between phases).

- `worktree_mode`: **Worktree management mode.** Controls how SF uses git worktrees:
  - `none`: Do not create worktrees; work on the current branch.
  - `auto`: Automatically create worktrees for isolation when needed.
  - `manual`: Require explicit worktree setup; fail if not available.

  Default: `auto` (solo), `manual` (team).

  Example: `worktree_mode: manual` (require explicit worktree setup).

- `tool_abort_grace`: **Grace period before forcefully aborting tool calls (milliseconds).** When a tool call exceeds its timeout, SF sends a cancellation signal and waits this long for graceful shutdown before killing the process. Default: `5000` (solo), `8000` (team).

  Example: `tool_abort_grace: 10000` (allow 10 seconds for graceful shutdown).

- `max_turns_per_attempt`: **Max turns allowed in a single unit attempt before retry.** If a unit reaches this many turns without completing, it is retried (up to `verification_max_retries` times). Default: `50` (solo), `60` (team).

  Example: `max_turns_per_attempt: 100` (allow up to 100 turns before retry).

- `hot_cache_turns`: **Number of recent turns to keep in hot memory cache.** Older turns are archived to disk. Larger values improve query speed but consume more RAM. Default: `5` (solo), `10` (team).

  Example: `hot_cache_turns: 20` (keep 20 recent turns in fast memory).

### Execution Timeouts & Limits Example

```yaml
---
version: 1
mode: team
context_compact_at: 22000
context_hard_limit: 38000
unit_timeout: 300
unit_timeout_by_phase:
  research: 180
  planning: 240
  execution: 600
max_agents_by_phase:
  research: 2
  execution: 4
turn_input_required: true
worktree_mode: manual
tool_abort_grace: 8000
max_turns_per_attempt: 75
hot_cache_turns: 12
---
```

This team-mode configuration:
- Compacts context at 22k tokens, fails hard at 38k.
- Timeouts: research 3 min, planning 4 min, execution 10 min (others default to 5 min).
- Runs up to 2 research and 4 execution units in parallel.
- Requires explicit user confirmation between phases.
- Uses manual worktree setup (must be created ahead of time).
- Gives tool calls 8 seconds to shut down gracefully.
- Allows up to 75 turns per unit attempt.
- Keeps 12 turns in fast memory for query performance.

---

### Doctor Checks

Run `/doctor` to validate your config:

- **Error:** `context_compact_at` > `context_hard_limit` (illogical; compact must happen before hitting hard limit).
- **Error:** Invalid `worktree_mode` value.
- **Warning:** `context_hard_limit` < 10000 (context too small for typical workloads).
- **Warning:** `unit_timeout` < 60 seconds (most units need multiple minutes).
- **Warning:** `max_turns_per_attempt` < 5 or > 200 (out of typical range).
- **Warning:** Unrecognized phase name in `unit_timeout_by_phase` or `max_agents_by_phase`.
- **Warning:** Phase timeout < 60 seconds or agent count out of range [1, 16].

Run `/doctor --fix` to auto-correct fixable errors (e.g., `context_compact_at` > `context_hard_limit`).

---

## Vault Secret Resolver (Tier 1.1)

### Overview

The Vault Secret Resolver allows you to store sensitive credentials (API keys, OAuth tokens) in HashiCorp Vault instead of plaintext in environment variables or config files.

**URI Format:** `vault://secret/path/to/secret#fieldname`

Example: `vault://secret/anthropic/prod#api_key` resolves the `api_key` field from the Vault secret at `secret/anthropic/prod`.

### Setup

#### 1. Install Vault

Download and install HashiCorp Vault from https://www.vaultproject.io/downloads.html.

Start a local dev server (for testing):
```bash
vault server -dev
```

This outputs a root token and `VAULT_ADDR`.

#### 2. Create a Secret

```bash
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='<root-token>'

vault kv put secret/anthropic/prod api_key='sk-ant-...'
vault kv put secret/openai/prod api_key='sk-...'
```

#### 3. Configure SF to Use Vault

Set your API key as a Vault URI in environment variables or `.env`:

```bash
# Option A: Environment variable
export ANTHROPIC_API_KEY='vault://secret/anthropic/prod#api_key'

# Option B: .env file
OPENAI_API_KEY=vault://secret/openai/prod#api_key
```

#### 4. Authenticate SF to Vault

Use one of these methods (tried in order):

**Method 1: Environment Variable (Simplest)**
```bash
export VAULT_TOKEN='<your-root-token>'
sf execute-task "Hello"
```

**Method 2: Token File (Recommended for Local Dev)**
```bash
# Save token to ~/.vault-token (readable only by you)
vault token lookup  # Confirm token is valid
cat ~/.vault-token  # Should show token

# Now SF will automatically use this token
ANTHROPIC_API_KEY='vault://secret/anthropic/prod#api_key' sf execute-task "Hello"
```

**Method 3: AppRole (Kubernetes / CI/CD)**
```bash
# Create AppRole on Vault (admin only)
vault auth enable approle
vault write auth/approle/role/sf-role token_ttl=1h policies="sf-policy"
vault read auth/approle/role/sf-role/role-id
vault generate-and-list-secret auth/approle/role/sf-role/secret-id

# Set environment variables
export VAULT_ROLE_ID='<role-id>'
export VAULT_SECRET_ID='<secret-id>'
```

### Examples

#### Solo Developer

```bash
# Start vault dev server
vault server -dev

# Create a secret
vault kv put secret/my-keys anthropic_key='sk-ant-...' openai_key='sk-...'

# Run SF with vault URIs
export VAULT_TOKEN='<dev-root-token>'
export ANTHROPIC_API_KEY='vault://secret/my-keys#anthropic_key'
export OPENAI_API_KEY='vault://secret/my-keys#openai_key'
sf execute-task "Write a poem"
```

#### Team Deployment

Store secrets in a production Vault instance:

```yaml
# Production Vault setup (admin responsibility)
# 1. Create AppRole for SF
# 2. Create secrets: secret/anthropic/prod, secret/openai/prod, etc.
# 3. Assign read policy for SF role

# SF deployment (.env or deployment config)
VAULT_ADDR=https://vault.company.com
VAULT_ROLE_ID=<role-id>
VAULT_SECRET_ID=<secret-id>
ANTHROPIC_API_KEY=vault://secret/anthropic/prod#api_key
OPENAI_API_KEY=vault://secret/openai/prod#api_key
```

### Troubleshooting

Run `/doctor` to check Vault setup:

```bash
/doctor
```

**Common Issues:**

- **`vault_no_credentials`** — No VAULT_TOKEN or ~/.vault-token found. Set one using methods above.
- **`vault_invalid_uri_format`** — URI doesn't match `vault://path#field`. Check format.
- **`vault_unreachable`** — Can't connect to Vault server. Check VAULT_ADDR and firewall.
- **Vault secret not found** — Path doesn't exist in Vault. Create it: `vault kv put secret/path field='value'`.

### Behavior

- **Lazy resolution:** Secrets are only fetched when an API key is actually used (not at startup).
- **Caching:** Resolved secrets are cached in memory for 5 minutes to avoid repeated vault requests.
- **Fail-open:** If Vault is unavailable, SF falls back to the plaintext URI (so you can continue working).
- **No persistent cache:** Secrets are not cached to disk; Vault can revoke/update them at any time.

### Security Notes

- Never commit `.env` or config files with `vault://` URIs to version control.
- Keep `VAULT_TOKEN` and `VAULT_SECRET_ID` in a secure location (use `~/.vault-token` or secret management).
- Use AppRole in production; avoid root tokens.
- Vault audit logs will record all secret access; review regularly.

---

### Related Documentation

- **Preferences Overview:** See top of this file for global vs project merging behavior.
- **Mode Defaults:** See `mode` field for workflow-specific defaults.
- **Memory System:** See `docs/dev/MEMORY-SYSTEM-ARCHITECTURE.md` for cache behavior integration.
- **UOK Architecture:** See `docs/adr/0075-uok-gate-architecture.md` and `docs/adr/0076-uok-memory-integration.md`.

## turn_status Marker System (Tier 2.5)

### Overview

The turn_status marker system allows agents to signal semantic state during task execution, enabling SF to respond appropriately without relying on timeouts or error detection.

**Marker Format:** `<turn_status>complete|blocked|giving_up</turn_status>` (placed at end of agent output)

**Three States:**
- **`complete`** — Task verified and finished; normal completion path.
- **`blocked`** — Discovered prerequisite or upstream failure; pause and wait for user input.
- **`giving_up`** — Multiple approaches failed; transition to phase reassessment.

### Why Use turn_status Markers?

Instead of waiting for timeouts or detecting errors, agents can explicitly signal:
1. **Completion** — "I've successfully completed the task; move to next phase."
2. **Blockers** — "I found a prerequisite missing; I'm pausing pending user input."
3. **Reassessment** — "I've tried multiple approaches and none work; let's reconsider the strategy."

This enables faster iteration and clearer agent-harness communication.

### How It Works

1. **Agent adds marker** — At the end of their output, agent writes: `<turn_status>blocked</turn_status>`
2. **Harness extracts marker** — SF parses the marker from agent output
3. **Harness responds** — SF triggers appropriate action (continue, pause, or reassess)

### Examples

#### Example 1: Normal Completion

Agent output:
```
I've successfully implemented the feature. Tests pass, code is clean.

<turn_status>complete</turn_status>
```

**Harness action:** Continue to next phase (normal completion path).

#### Example 2: Blocked by Missing Dependency

Agent output:
```
I need the database schema to implement this feature, but it's not documented.
I'll pause here pending your input on the schema definition.

<turn_status>blocked</turn_status>
```

**Harness action:** Pause unit and wait for user to provide missing information (e.g., schema documentation).

#### Example 3: Giving Up After Multiple Attempts

Agent output:
```
I've tried three approaches to optimize this query:
1. Indexing — didn't help
2. Query rewrite — made it slower
3. Caching layer — requires architectural changes

None of these approaches work within the current constraints. 
I recommend reassessing the problem statement or constraints.

<turn_status>giving_up</turn_status>
```

**Harness action:** Transition to phase reassessment (strategy change).

### Best Practices

1. **Be explicit** — Include the marker *only* when you have semantic knowledge about completion, blockers, or failure.
2. **Use complete for verification** — Only mark `complete` when you've tested and verified the result.
3. **Use blocked for *prerequisites*** — Use `blocked` when *external input or dependency* is missing, not for internal implementation details.
4. **Use giving_up for reassessment** — Use `giving_up` when you've exhausted multiple approaches within the current constraints.
5. **Fallback behavior** — If no marker is present, SF assumes `complete` (normal completion).

### Doctor Check

Run `/doctor` to validate turn_status marker coverage:

- **Warning:** Executive prompts missing turn_status marker templates. Agents won't be able to signal `blocked` or `giving_up` state.

If prompts are missing markers, SF will still function normally, but agents won't have a clear way to signal blockers or reassessment needs.

### Related Documentation

- **Turn Status Parser:** See `src/resources/extensions/sf/turn-status-parser.js` for implementation.
- **Prompt Templates:** See `src/resources/extensions/sf/prompts/*.md` for marker usage in agent instructions.
- **Tier 2.5 Architecture:** See `docs/adr/` for Tier 2.5 design decisions.
