Skip to content

Design Decisions

Gregor Biswanger edited this page Aug 3, 2026 · 4 revisions

Design Decisions

Why FeatherSpec is shaped the way it is. Every rule here has a cost attached to breaking it.

One rule, one place

No rule and no workflow is written down twice — apart from three reinforcements listed below, which are deliberate and documented.

  • AGENTS.md is the only place mutable state livesDocLanguage, the architecture: snapshot, and Style & Output Preferences. Every command that changes those writes to AGENTS.md. CLAUDE.md is one line and holds no copy.
  • Rules and workflow bodies are single files both tools read. No .github/instructions/, no .github/copilot-instructions.md — they would only duplicate what .claude/rules/ and .claude/commands/ already provide. The one Copilot-side folder that does exist, .github/prompts/, holds loaders: a link and a sentence, never a rule.
  • AGENTS.md states policy; rule files state craft. AGENTS.md defines the spec and plan lifecycle and the Memory Bank file set. .claude/rules/specs.md, plans.md and memory-bank.md cover only how to write those files, and link to AGENTS.md for the policy. .specs/README.md is orientation and links rather than a restatement.

This is not tidiness for its own sake. Two copies of a rule drift, and when they drift the model follows whichever it read last. One copy cannot drift.

The three documented exceptions

All three exist for the same reason: a path-scoped rule loads only when a matching file is read, which is too late for a workflow that is creating or moving that very file.

Reinforcement Where Why
Spec essentials (DocLanguage, the **Status:** line, the document structure) commands/sdd-specify.md a brand-new spec has not been read, so .claude/rules/specs.md may not have loaded yet
Plan essentials (DocLanguage, naming, the **Status:** line, the file structure) commands/sdd-plan.md same reason — a brand-new plan file has not been read, so .claude/rules/plans.md may not have loaded yet
Lifecycle safety rules (one folder at a time, delete the original, duplicate check, plan travels with spec) commands/sdd-lifecycle.md this workflow performs the moves and deletions; the safety rules must be in front of the model as it acts

In all three cases AGENTS.md remains authoritative: if a copy ever diverges, fix the copy. Do not "clean up" these by deleting them — each one is load-order insurance, and removing it reintroduces a real failure mode.

No skills on purpose

A skill advertises itself: both tools read every skill's name and description up front so the model can decide to load it. Claude Code drops that advertising when a skill sets disable-model-invocation: true, but VS Code does not document the same guarantee — so eight workflow descriptions could sit in the system prompt of every single request.

Workflows here are commands, which no model is offered:

  • .claude/commands/<name>.md holds the body and Claude Code runs it as /<name>
  • .github/prompts/<name>.prompt.md is a one-line loader that points Copilot at that same body

Reach for a skill only when a workflow genuinely needs the model to trigger it on its own. These do not: they create, move and delete files, and they should run when you say so.

Deliberate constraints in the shared bodies

Because one body file is executed by both tools, it is written to the lowest common denominator. Each constraint has a reason:

  • No !`shell` injection. Copilot does not support it; it would land in the prompt as literal text. Bodies instruct the model to run a command and use the result instead.
  • No $ARGUMENTS / $0 / $1. Same reason — Copilot's documented mechanism is ${input:…}. Bodies say "the user may name a spec path after the command; if none is given, list the candidates and ask." Both tools append the user's trailing text to the invocation, so the model still sees it.
  • No tool names in the bodies. The tool that fetches a web page is WebFetch in one and #fetch in the other, so bodies name the capability ("your web search or fetch capability") and state what to do when it is missing. Same for terminal work: bodies say which command to run and use the result, rather than assuming a tool name.
  • The file name is the command name in both tools, so .claude/commands/sdd-plan.md and .github/prompts/sdd-plan.prompt.md must share the stem.

A comment at the top of each body restates this, so nobody reintroduces the syntax later.

Why planning writes a file

A plan that lives only in the chat is gone when the session ends. Making the plan a file changes three things:

  1. Resumability — a new session reads Current step and Session handoff and continues, instead of asking you to re-explain.
  2. Reviewability — the plan shows up in a pull request next to the code it produced.
  3. Impact analysis — the traceability table can be read in reverse, so a changed requirement yields the list of steps and code paths it reaches before anything is edited.

The rule that makes all three work: the plan is updated in the same change set as the code. The assistant's own todo list is scratch state that dies with the session; the plan file is the durable one, and when the two differ, the file wins and gets corrected.

Why the data folders are top-level

.specs/ and .memory-bank/ are your project's content, not tool configuration. The predecessor template kept them under .github/, which made them look like GitHub plumbing and tied them to one vendor's folder. At the top level they are visibly data, they are tool-neutral, and they survive any decision you later make about which assistant to keep.

Why sdd- prefixes everything

Two reasons: the commands do not shadow Claude Code's built-in commands, and typing /sdd shows you the whole workflow set in one autocomplete group.

Clone this wiki locally