Core concepts

The step vocabulary

One project, one wording

Every step your project knows lives in a derived index, built from your files and your recorded caches, never maintained by hand:

.feature and .saffron files The wording, including steps nobody has recorded yet. .saffron/cache Recorded actions and status, per step. The step vocabulary A derived index, rebuilt from the files and the caches on every read. Never hand-maintained, never stale. RECORDED · REPLAYS FREE DIVERGENT · NEEDS A LOOK WRITTEN, NOT RECORDED YET IDE completion Humans typing, through the language server: badges, navigation, diagnostics. saffron mcp AI assistants writing files: search_steps, list_step_sets, project_status. saffron author Plain paragraphs in, a feature file out, in your own wording. Duplicate detection Identical actions under different words become a rename proposal. One index, three audiences Humans, AI assistants and the runner all speak the same step vocabulary, so every suggestion shows whether the step is already paid for. Accepted renames flow back into the files, and the vocabulary converges instead of drifting.
The step vocabulary and its consumers

The two sources matter:

  • Files contribute the wording, including steps a teammate wrote yesterday that nobody has recorded yet. If completion only knew about recorded steps, two people writing files in parallel would still diverge.
  • Caches contribute the status. Each suggestion carries a badge: recorded (replays at zero tokens), divergent (same text, different recordings: worth a look), ○ written but not recorded yet. You can see which steps are already paid for while you type.

saffron steps lists and searches this vocabulary from the CLI, each step printed with its badge, usage count, and owning StepSet; saffron steps --json exports it for tooling, and saffron steps --snippets writes .vscode/saffron.code-snippets for native VS Code completion with zero extension code (regenerate after recording sessions).

Duplicate wording

Two layers attack pain #1 from both sides:

  • Prevention (IDE): as you type a new step, near-duplicates of existing wording get a warning: "similar to 'I navigate to the home page': recorded, used in 4 scenarios", with a one-click rename. The suggestion popup helps the author who pauses; the warning catches the author who doesn't.
  • Cure (runner, shipped): if a duplicate slips through and gets recorded, Saffron compares the new recording against the index. Two wordings that produced identical action lists are a duplicate proven by behavior, not guessed by string similarity, the proposal arrives with a rename feature-edit to the canonical wording plus an adaptation note; saffron accept --with-feature-edit applies it. Each duplicate costs exactly one recording, then the vocabulary converges. (No-op steps and divergent canonicals are excluded.)

That second trick is only possible in Saffron: Cucumber never observes what a step does, so it can never prove two wordings are the same.

Writing feature files with AI

The same vocabulary is served to machines, because in practice a lot of feature files are now written with an AI assistant in the loop:

  • saffron author <prose-file> (shipped), hand Saffron a plain-paragraph description; it drafts the .saffron file with the step index injected as the required vocabulary and reports how many drafted lines reuse it. Live-validated: from two paragraphs it produced a file that reused every existing wording verbatim, invoked the existing StepSet instead of repeating its steps, and used doc strings for the note content: for $0.12, and recording it would be almost fully seeded. Review the draft, then saffron run it.
  • saffron mcp (shipped): a stdio MCP server exposing search_steps, list_step_sets and project_status. Point your AI IDE (Claude Code, Cursor, Copilot) at it and any assistant writing files in your project speaks your vocabulary too: assistants can't read completion popups, so they get the index as context instead. Claude Code example: claude mcp add saffron -- npx saffron mcp.