Every exported function, type, class, and module-level constant opens with a JSDoc block whose first sentence is its **purpose** — the consumer-facing reason it exists. Not what it does (the signature shows that), but **why**.
```ts
/**
* Acquire a unit claim atomically. Returns true on success, false if another worker
* already holds an unexpired lease.
*
* Purpose: prevent two workers from dispatching the same unit when the run-lock is
* unavailable (shared NFS, broken filesystem semantics) — the conditional UPDATE in
export function claimUnit(unitId: string, leaseMs: number): boolean { ... }
```
Required for every exported symbol whose behaviour is non-trivial:
- **First line** — what it returns / does, in the present tense.
- **Purpose:** — why it exists; the value it protects.
- **Consumer:** — who calls it in production. If you can't name a consumer, the symbol shouldn't exist yet.
A bare `/** Helper. */` is a code smell. Either write the purpose or delete the symbol.
For module-level JSDoc (file headers): keep the existing `module-name.ts — short description` opening, then a `Purpose:` line stating why the module exists as a separable unit.
## Testing Guidelines
- **Primary test runner**: Vitest via `npm run test:unit`, `npm run test:integration`, and `npm test`
- **Node test runner**: used only by specific package/native/browser-tool scripts where `package.json` says `node --test`
- **Coverage tool**: Vitest coverage with `@vitest/coverage-v8`; thresholds are enforced in CI
- **Naming**: `*.test.ts` and `*.test.mjs` patterns
- **Smoke tests**: `npm run test:smoke`
- **Live tests**: `npm run test:live` (requires environment variables)
### Purposeful Tests
Test names are contract claims. Use the form `<what>_<when>_<expected>`:
Write behaviour contracts first. They are the work order.
A test that asserts call counts or mock interactions is **mechanical**, not purposeful — it should be a labelled implementation guard, not a primary contract test. A test that breaks on a refactor without behaviour change is mechanical too. Fix the test or relabel it.
**Bug = missing correct-behaviour test.** When fixing a bug, write a test for the *correct* behaviour first — it must fail (RED) because the bug exists. If it passes immediately, the test is testing the broken behaviour; fix the test, not the code.
## Extension Development
Extensions live in `src/resources/extensions/`. Each extension should:
- Export a manifest with `name`, `version`, `tools[]`, and `agents[]`
- Include tests in `src/resources/extensions/<name>/tests/`
- Register tools via the extension API
## Pull Request Guidelines
1.**Link an issue** — PRs without a linked issue will be closed without review
2.**One concern per PR** — don't bundle unrelated changes
3.**No drive-by formatting** — don't reformat code you didn't touch
4.**CI must pass** — fix failing tests before requesting review
5.**Rebase onto main** — do not merge main into your feature branch
6. Use the PR template at `.github/PULL_REQUEST_TEMPLATE.md`
## Environment Setup
Copy `docker/.env.example` to `.env` and fill in API keys. At minimum you need one LLM provider key (Anthropic, OpenAI, Google, or OpenRouter).
## Architecture Notes
- State lives on disk in `.sf/` — no in-memory state survives across sessions
- Bundled extensions/agents sync to `~/.sf/agent/` on every launch
- LLM providers are lazy-loaded on first use to reduce cold-start time
SQLite (`.sf/sf.db`) is the canonical structured store for SF agent state whenever schema, ordering, priority, joins, or validation matter. Runtime files under `.sf/` are working artifacts, generated projections, evidence, or recovery inputs.
**Promote-only rule:** Agent runtime state (`.sf/milestones/`, `.sf/evals/`, `.sf/harness/`, locks, journals, and generated manifests) is transient and gitignored — never committed directly. Project `.sf/` files tracked in the repo root are limited to deliberate human-authored guidance such as `PRINCIPLES.md`, `TASTE.md`, `ANTI-GOALS.md`, `DECISIONS.md`, `KNOWLEDGE.md`, `REQUIREMENTS.md`, and `ROADMAP.md`.
SF keeps the working spec contract in `.sf`, database first. Root-level `SPEC.md`, `BASE_SPEC.md`, product spec files, and `docs/specs/` are human exports, reports, review surfaces, or external evidence, not a competing planning model. SF can read any repo file as source evidence, but information required for SF's own future operation must be analyzed into `.sf`/DB-backed state. New plans must state purpose on every milestone, slice, and task before implementation detail.
SF has one flow engine across TUI, CLI, web, editor, and machine entrypoints.
Keep integration language separated: **surface** means TUI/CLI/web/editor/machine,
**protocol** means ACP/RPC/stdio JSON-RPC/HTTP/wire, **output format** means
text/json/stream-json, **run control** means manual/assisted/autonomous, and
**permission profile** means restricted/normal/trusted/unrestricted.
`sf headless` is the current machine-surface command, not a separate flow and
not a synonym for JSON. See `docs/specs/sf-operating-model.md`.
Source placement follows the same model. `src/resources/extensions/sf/` owns the
SF flow extension, `src/headless*.ts` owns the `sf headless` machine-surface
command path, `web/` owns the browser surface, `vscode-extension/` owns the
editor surface, `packages/rpc-client/` owns reusable RPC adapter code, and
See [`docs/plans/README.md`](docs/plans/README.md), [`docs/adr/README.md`](docs/adr/README.md), and [`docs/specs/README.md`](docs/specs/README.md) for directory-specific conventions.
The SF schedule system (`/sf schedule`) stores project time-bound reminders in the repo SQLite DB (`.sf/sf.db`, `schedule_entries`) and global reminders in `~/.sf/sf.db`. Legacy `.sf/schedule.jsonl` rows are import-only compatibility input when a project has no schedule rows yet. Items surface on their due date via pull queries at launch and autonomous mode boundaries — there is no background daemon.
- **`sf schedule`** — time-bound items that must surface at a future date: a 2-week adoption review after shipping a feature, a 1-month audit of an architectural decision, a 30-minute reminder to run a command. Use when the *timing* matters, not just the *priority*.
- **Backlog** (milestone/slice queue) — priority-ordered items with no specific timing. Items are dispatched in sequence by the autonomous controller based on readiness and dependency, not wall-clock time.
**Examples:**
```
sf schedule add --in 2w "Review feature adoption metrics"