-
Notifications
You must be signed in to change notification settings - Fork 2
Design Decisions
Why FeatherSpec is shaped the way it is. Every rule here has a cost attached to breaking it.
No rule and no workflow is written down twice — apart from three reinforcements listed below, which are deliberate and documented.
-
AGENTS.mdis the only place mutable state lives —DocLanguage, thearchitecture:snapshot, and Style & Output Preferences. Every command that changes those writes toAGENTS.md.CLAUDE.mdis 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.mdstates policy; rule files state craft.AGENTS.mddefines the spec and plan lifecycle and the Memory Bank file set..claude/rules/specs.md,plans.mdandmemory-bank.mdcover only how to write those files, and link toAGENTS.mdfor the policy..specs/README.mdis 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.
They all 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 |
AGENTS.md remains authoritative in every case: 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. The regression would be silent, too: nothing fails, the new
plan file just comes out in the wrong language with no status line.
Each exception is annotated from both ends. The command says it is restating something and
names AGENTS.md as authoritative; the corresponding rule file carries a reverse note saying
why the command repeats it. specs.md, plans.md and memory-bank.md all carry that note. A
deliberate duplication that is not labelled as one is indistinguishable from an accident, and
the next contributor deletes it.
The rule that follows from this: a command may restate a rule when it must be in front of the model at the moment it acts. Anything else is a copy, and copies drift. The Copilot persona file used to hold five of them — an operating protocol with a fourth step the constitution does not have, a memory-sync rule missing its "same change set" clause, a preference rule missing its rewrite clause. All five had drifted from the original before anyone noticed. It is a pointer now.
A gate that exists in one file and not in another is not a gate — the effective behaviour is
whatever happened to be in context. The architecture snapshot used to be written immediately for
"obvious" drift by the constitution and only after confirmation by /sdd-architecture-update,
with new top-level folder listed on both sides. There is now exactly one gate for that write,
and the drift rule proposes rather than writes.
The same principle sets the shape of the non-negotiables: always, ask first, never — with ask first naming the operations whose blast radius reaches your codebase rather than a Markdown file (dependencies, migrations, deleting or moving files, git writes, network calls). The earlier version had no ask first tier at all, and every gate in the repository protected a document while nothing protected the code.
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 nine
workflow descriptions could sit in the system prompt of every single request.
Workflows here are commands, which no model is offered:
-
.claude/commands/<name>.mdholds the body and Claude Code runs it as/<name> -
.github/prompts/<name>.prompt.mdis 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.
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
WebFetchin one and#fetchin 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.mdand.github/prompts/sdd-plan.prompt.mdmust share the stem.
A comment at the top of each body restates this, so nobody reintroduces the syntax later.
A plan that lives only in the chat is gone when the session ends. Making the plan a file changes three things:
-
Resumability — a new session reads
Current stepandSession handoffand continues, instead of asking you to re-explain. - Reviewability — the plan shows up in a pull request next to the code it produced.
- 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.
.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.
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.
Start here
Reference
Design & interop
- Interop Matrix
- Design Decisions
- Specify Method
- Committing to One Tool
- Migrating from the predecessor
Help