spec
Functional contract of the goal workflow — normalize any planning source (Jira US, GitHub issue, spec file, PRD/BMAD story, a ticket cut by /goal:tickets, brainstorm, inline note), grill the functional gaps closed one question at a time, build the functional Definition of Done (an observable acceptance criterion per business rule — no project command named here), run the opt-in adversarial grill, and OPTIONALLY mirror the intent as a GitHub issue. Technical grill, decomposition and the locked plan are /goal:plan's.
- License 许可
- MIT
- 最近更新
- 2026-10-13
- Stars
- ★ 9
- Forks
- 2
01 前言 Front Matter
- name
- spec
- description
- Functional contract of the goal workflow — normalize any planning source (Jira US, GitHub issue, spec file, PRD/BMAD story, a ticket cut by /goal:tickets, brainstorm, inline note), grill the functional gaps closed one question at a time, build the functional Definition of Done (an observable acceptance criterion per business rule — no project command named here), run the opt-in adversarial grill, and OPTIONALLY mirror the intent as a GitHub issue. Technical grill, decomposition and the locked plan are /goal:plan's.
02 资源文件 1
- 参考文档index.json500 B
03 SKILL.md 正文
Portability
This skill degrades gracefully outside Claude Code:
- Shared fallbacks: read
<root>/shared/portability.md. - No Atlassian MCP → ask the developer to paste the Jira source inline (see the
$ARGUMENTStable below, which already routes to this fallback). - No
pbcopy→ print the content instead of copying it to the clipboard.
Host
<root> is Claude Code's substituted ${CLAUDE_PLUGIN_ROOT}. A host that leaves it literal
(Copilot CLI) reads <root> as two levels above Base directory for this skill:.
A host that injects no base-directory line and reads SKILL.md itself (Codex CLI) takes <root> as two levels
above that file's directory.
$ARGUMENTS left literal is the text typed after the command (empty when none).
Use <root> wherever a command below says ${CLAUDE_PLUGIN_ROOT}.
When <root>/src/artifacts.ts or a shared file this skill names is missing, print
cannot locate the goal plugin scripts, the host, and the install commands, run nothing and write nothing:
copilot plugin marketplace add <repository> then
copilot plugin install goal@fabien-claude-marketplace under Copilot,
codex plugin marketplace add <repository> then
codex plugin add goal@fabien-claude-marketplace under Codex,
/plugin install goal@fabien-claude-marketplace under Claude Code.
Then read <root>/shared/host.md.
/goal:spec — Source → functional contract (+ optional GitHub issue)
You are turning a raw planning artifact into the functional contract that
/goal:plan executes against: the need, the scope, the business rules, and a
functional Definition of Done — one observable acceptance criterion per rule.
Creating a GitHub issue from it is optional — you ask, you never impose it.
This is the level where what must become true is settled. How it is proven
(commands), decomposed and delivered is /goal:plan's level, and stays out of this
session entirely.
(chantier too big for one spec)
└─ /goal:tickets ─▶ ordered backlog; each ticket lands here at its turn
(idea / Jira US / GitHub issue / PRD / BMAD story / brainstorm)
└─ /goal:spec ← YOU ARE HERE
├─ functional contract written to <plans>/<work-id>-spec.md (always)
└─ GitHub issue mirror (only if the developer says yes)
└─ /goal:plan <source>
└─ manual: /goal + /goal:next · commit+pr: /goal:supervise
Project artifact root
Before resolving the source, run the existing resolver from the installed goal plugin:
project="$(git rev-parse --show-toplevel)"
node "${CLAUDE_PLUGIN_ROOT}/src/artifacts.ts" "$project"
Then read <root>/shared/artifact-root.md.
If supplied is false, ask for docs/goal/, .goal/ or a custom path and
wait for the answer. Ask which project-root file, .env or .env.local, should
hold GOAL_ROOT_PATH. Preserve all unrelated content; update only its assignment,
quoting the chosen literal path. Do not execute shell expressions or expand variable
references. A selected empty or invalid value is an error, never an absent value.
If supplied is true, use the effective value without asking for a location.
Use the chosen value for this session by passing it as the resolver process's
GOAL_ROOT_PATH; a child cannot change the parent session's environment. Resolve
again with that value before writing. Do not create Claude JSON configuration.
Argument — resolve the source
Source: $ARGUMENTS
$ARGUMENTS shape |
How to read it | work-id |
|---|---|---|
Jira key ^[A-Z][A-Z0-9]+-[0-9]+$ (e.g. CT-1234) |
Atlassian MCP — see below | key lowercased → ct-1234 |
Bare integer 42 or #42 |
gh issue view 42 --json number,title,body,labels,comments (needs gh auth) |
issue-42 |
A backlog written by /goal:tickets (file starts # Backlog:) |
Take the ticket marked ← next — and only it. The rest of the backlog is context, not scope |
≤40-char kebab slug of the ticket title |
Any other path ending in .md |
Read the file | ≤40-char kebab slug of the title |
inline or empty |
Ask the developer to paste the source now | ≤40-char kebab slug of the title |
| A path that doesn't exist | STOP and tell the developer | — |
Reading a Jira source via MCP: read <root>/shared/jira-source.md and follow it.
Phase 0 — Preconditions
git rev-parse --show-toplevelsucceeds (we're in a repo)- Only if the source is Jira: the Atlassian MCP resolves
- Only if the source is a GitHub issue:
gh auth statussucceeds - Do NOT check
ghotherwise. GitHub as an output is only touched in Phase 5, and only if the developer opts in.
If a needed check fails, STOP and say what to fix.
Phase 1 — Read & extract
Normalize the source into these sections (headings vary by upstream tool):
| Target section | Upstream synonyms |
|---|---|
| Title | first # …, BMAD story.title, spec-first "Feature", Jira summary |
| Problem | "Why", "Context", "Background", "Pain", the incident that triggered the ticket |
| Objective | "Goal", "Outcome", "Definition of success", BMAD story.description |
| Success signal | "How we'll know", "Metric", "KPI" — rarely present, usually to be built |
| Affected | "Users", "Personas", "Impacted teams", the caller named in a bug report |
| Business intent | the prose that connects the four above, Jira description |
| Scope IN | "In scope", "Requirements", BMAD acceptance_criteria (positive), Jira AC |
| Scope OUT | "Out of scope", "Non-goals", "Will not do" |
| Acceptance criteria | "Done when", "Success criteria", Jira AC |
| Files to touch (optional) | "Files to create/modify", "Affected files" |
| Decisions (optional) | "Q&A", "Clarifications", Jira comment thread |
Note which target sections the source is missing — a Jira US usually lacks a
real Definition of Done and often has functional gaps. Missing functional
sections are not carried forward as flags: Phase 3 grills them closed here,
while the developer is present. Only technical consequences you notice while
reading (a migration, a coupling, a contract change) are flagged, for
/goal:plan.
Size verdict — one outcome, or a chantier?
Before grilling, rule on size. A spec covers one outcome: one success signal,
one delivery. When the source carries several independent outcomes — parts
that each have their own success signal and could ship weeks apart — or no single
success signal at all, it is a chantier: STOP and route to /goal:tickets,
which cuts it into an ordered backlog and sends each ticket back here at its
turn. Grilling a chantier as one spec freezes guesses about parts nobody has
learned anything about yet.
When the verdict is borderline or the developer disputes it, load
product:vertical-slice and judge with it: parts that only mean something
together are one outcome; parts that ship alone and stay valuable alone are a
chantier's tickets.
Reproduce the problem
Before anything is graded against it, reproduce the Problem the source states — the
same commands and readings the source's own evidence points at, or the ones the
developer names when it doesn't. Record each command run and the value it produced;
that pair, verbatim, is what Phase 4 writes into ## Reproduction. A problem that
cannot be pointed at with a command is still caught by Phase 2's blocker for
unevidenced problems — this step is for one that can be pointed at, and no longer
shows what the source says it shows.
Phase 2 — Validate the need
| Issue | Severity | Action |
|---|---|---|
| Empty business intent | ❌ blocker | Ask the developer to provide it now |
| Problem asserted with no evidence ("it would be better if…") | ❌ blocker | A problem nobody can point at has no way to be closed. Ask for the incident, the measurement, the file and line. |
| Problem does not reproduce (the commands run in Phase 1 now show something the source didn't claim, or already fixed) | ❌ blocker | STOP. Say what changed since the source was written, and do not write a contract against a premise that no longer holds. |
| Objective written as a solution ("rewrite X in Y") | ⚠️ warn | Ask what must become true. A solution stated as an objective freezes a design before the grill has looked at it. |
| No success signal | ⚠️ warn | Note it. Acceptance criteria prove behavior is in place; they never prove the problem is gone. |
| No acceptance criteria at all | ⚠️ warn | Fine — Phase 3 builds the functional DoD; that is this session's deliverable |
| Scope IN/OUT mixed | ⚠️ warn | Propose a split |
| Criteria not observable ("code is clean") | ℹ️ info | Rewrite as observable behavior in Phase 3; /goal:plan maps it to a command later |
Resolve blockers with the developer before Phase 3.
Phase 3 — Functional grill (one question at a time)
Goal: close every functional gap, so that /goal:plan never has to ask a
functional question — once decomposition starts, a functional hole is rework.
Technical consequences are NOT grilled here: when one surfaces, write it under
## Flags for /goal:plan and move on.
Pick the best available interview approach: read <root>/shared/grill-approach.md, then:
3. common:spec-first-dev Phase 1 questions if common is installed.
4. Inlined baseline (below) — always works.
Inlined baseline questions (fallback)
Ask one at a time, each with your recommended answer. Skip anything the source already answers.
- Who triggers this? (end user / admin / batch / external system)
- Precise end-to-end happy path, step by step?
- Which business rules apply? (validations, computations, conditions, limits)
- Vocabulary: project-specific terms to align with? Where do they live?
- Business edge cases (not technical — real domain cases)?
- What is explicitly out of scope for this delivery?
- For each business rule: what would you observe to know it holds?
Behavior, not a command — naming the command is
/goal:plan's job.
Stop condition
Stop grilling when: every functional gap is closed, every business rule carries
an observable acceptance criterion, and no "it depends" remains. Typical: 5–15
questions for a small US, more for a feature. One question per message —
batch questionnaires reduce signal. A question the developer explicitly defers
is recorded under ## Deferred decisions with what was assumed instead — never
silently dropped.
Phase 3b — Adversarial grill (opt-in, runs HERE)
The baseline grill resolves the branches that get raised. It does not
enumerate the interaction state space — so broken invariants and broken
execution schemas (interface/usage incoherence) slip through and surface later
as rework. The goal:grill-adversarial skill closes that gap, and this is the
one place it runs: before anything downstream freezes. It is heavy — so it is
opt-in.
Ask the developer with AskUserQuestion:
Run the adversarial grill on this spec now?
- yes (recommended for front / interactive work, or when unsure) — enumerate states, extract invariants, build the
(state × action)transition matrix, and turn every hole into an owned rule with an observable criterion. Take altitude and question everything, including what was never defined.- no — small US whose scenarios you fully enumerate and are confident about; or pure back / transactional work where actions are coarse and server-owned.
Recommend yes whenever the feature is front / interactive, state lives
client-side, or Phase 1 flagged functional gaps. If yes → load
goal:grill-adversarial and run it to termination; its states, invariants
and transition matrix land in the contract below. Either way, record the
outcome on the contract's Adversarial grill: line (ran / skipped — <why>),
so /goal:plan and any later reader know whether the state space was
enumerated or waved through.
Phase 4 — Write the functional contract
Write a new contract at <plans>/<work-id>-spec.md. When updating an explicitly
selected existing contract, keep its path and update it in place:
# Spec: <title>
Source: <Jira CT-1234 | gh issue #42 | spec file path | ticket <rank> of <chantier-id> | inline>
Work-id: <work-id>
Status: spec — functional contract settled; /goal:plan builds the executable plan.
Adversarial grill: <ran | skipped — why>
## Business intent
**Problem.** <what fails today, with the evidence you can point at — an incident, a
measurement, a file and a line. Never "it would be better if".>
**Objective.** <what must become *true*. An outcome, not a solution: "a locked plan runs
overnight and I wake up to PRs or a diagnosis", not "replace the loop with a script".>
**Success signal.** <how you would know it worked out in the real world. Distinct from the
acceptance criteria, which only ever prove that behavior is in place.>
**Affected.** <who lives with the problem today, and what changes for them.>
<Then 1–3 paragraphs connecting the problem to the objective, in the developer's own domain
vocabulary.>
## Reproduction
<the exact commands run in Phase 1, each followed by the value it produced — the same evidence
the Problem line points at, kept alongside it so /goal:plan and any later reader can re-run it>
## Scope IN
- <items>
## Scope OUT
- <items>
## Business rules (functional DoD — one observable criterion per rule)
- <rule> → observed when: <the behavior a human or a test can watch. No command named here — /goal:plan maps each criterion to one>
## States, invariants & transitions (only if the adversarial grill ran)
- **States:** <S1…Sk>
- **Invariants:** <I1…In — each with its owner; front-only ⇒ the front owns it>
- **Transition matrix:** <(state × action) → resulting state / blocked / invariant preserved>
## Deferred decisions
- <question the developer chose not to settle now, and what was assumed instead — or "none">
## Files (as found in source)
- `<path>` — <role — only when the source named them; suspecting files is /goal:plan's job>
## Flags for /goal:plan
- <technical consequence noticed while reading — a migration, a cross-repo coupling, a contract change>
## Notes / decisions from source
- <Q/A or comment-thread decision worth preserving>
Show the title and the contract to the developer, and read the Business
intent block back to them explicitly. It is the one section nothing downstream
can reconstruct: /goal:plan can map rules to commands, but it cannot invent
why the work matters or how anyone would know it worked. WAIT for their
confirmation before Phase 5.
Phase 5 — Ask: create a GitHub issue? (opt-in)
Ask explicitly:
Do you also want a GitHub issue mirroring this contract?
- no (default) — keep it local; go straight to
/goal:plan.- yes — I'll
gh issue createfrom it. (Optional: labels, milestone, assignee — I create nothing you didn't name.)
WAIT for the answer.
If no → skip to Phase 6 with no GitHub involvement.
If yes:
-
Now verify
gh auth statusand thatgh repo viewworks. If either fails, STOP, report, and fall back to the local-only handoff. -
Render the intent projection — and only it — into a temp file. The projection is
## Business intent,## Scope IN,## Scope OUTand the flags: the sections that will still be true in three weeks. Add one closing line saying the executable plan lives locally (versioned according to Git ignore rules) and that the pull request body is where the delivered work is read.Everything
/goal:planwill later produce — the command-mapped Definition of Done, the iterations, thegateblocks — stays out. Those change at every slice, and an issue holding a copy of them is wrong by the second commit while looking authoritative. The issue is the log; the PR is the deliverable.TMP=$(mktemp -t goal-spec-XXXXXX.md) -
Create it:
gh issue create --title "<title>" --body-file "$TMP" \ [--label "<labels>"] [--milestone "<milestone>"] [--assignee "<assignee>"] -
Capture the issue number
<N>. From now on the work-id isissue-<N>; rename the newly created spec to<plans>/issue-<N>-spec.mdso/goal:plan <N>finds it. An explicitly selected existing contract stays in place; hand off its full path instead.
Do not auto-create labels/milestones/assignees — only what the developer named.
Phase 6 — Handoff
The developer has to be able to tell, from this output alone, what now exists, what deliberately does not yet, and the single next command. Print exactly that shape, and make the command the last thing on screen — nothing after it, no commentary, no summary.
✓ Functional contract written — <plans>/<work-id>-spec.md
✓ GitHub issue created — <URL> (omit this line when local-only)
This contract has
· the need — problem, objective, success signal, who is affected
· scope IN and scope OUT
· <R> business rules, each with an observable acceptance criterion
· states, invariants & transitions (omit when the adversarial grill was skipped)
· <D> deferred decisions and <F> flags for /goal:plan
It deliberately does NOT have yet
· no command-mapped Definition of Done — /goal:plan gives every criterion its command
· no iterations, no branch, no code
· nothing is committed and nothing was pushed
Next
/goal:plan <N or work-id>
Fill <R>, <D> and <F> with the real counts, and name the two or three flags that will
cost the most to resolve — a developer who reads "4 flags" learns nothing, one who reads
"the export coupling is unconfirmed, and the flag mechanism is undecided" knows what the next
session will ask them.
If the source was local-only, drop the issue line and use the work-id in the command.
When an existing contract was selected explicitly, print its actual path and pass that
full path to /goal:plan, rather than an identifier or a current-root substitute.
Rules
- GitHub as output is opt-in. Never call
ghto create anything unless the developer said yes in Phase 5. - Do not write production code, do not create branches — everything here is contract.
- The functional DoD is built HERE. Every business rule leaves this session with an
observable acceptance criterion. What is NOT built here: the command that proves it —
naming project commands is
/goal:plan's verifiability round. - No technical grilling. A coupling, a migration, a data source you notice goes to
## Flags for /goal:plan, never into a question round. - One spec = one outcome. Several independent outcomes are a chantier — route to
/goal:ticketsinstead of writing a contract no single plan can execute. - The adversarial grill runs here or not at all. Downstream never re-opens the state space; an unenumerated interaction discovered later is a spec fault that comes back to this file.
- Standalone — needs only a git repo, plus the Atlassian MCP for a Jira source and
ghonly for a GitHub-issue source or the opt-in mirror. - Never assert a problem the source did not evidence. If it says "improve X", the problem is missing and Phase 2 blocks. Inventing a plausible one puts a fabricated premise at the head of a chain that will never re-examine it.
- One question at a time.
- The handoff command is the last line you print. Anything printed after it buries the one thing the developer came for.
04 同仓库更多 Skills
vitest-test-conventions
ACTIVATE when writing Vitest tests, creating test files, using vi.fn()/vi.mock()/vi.spyOn(), or test factories. ACTIVATE for 'Vitest', 'it.each', 'NestJS testing module', 'supertest', 'vi.fn', 'vi.mock', 'spyOn'. Provides Vitest/NestJS-specific testing patterns; cross-language testing principles (DAMP, AAA, spy over mock, what NOT to test) live in craft:testing-principles. DO NOT use for: node:test files (a file importing node:test) or a project whose tests run under node --test (see node-test:node-test-conventions), TDD workflow/iterations (see vitest:vitest-tdd-workflow), PHP/PHPUnit tests (see phpunit:php-test-conventions).
vitest-tdd-workflow
ACTIVATE when building new features, endpoints, or user stories using TDD with Vitest in a TypeScript/NestJS context. ACTIVATE for '/feature-tdd-dev', 'TDD', 'red-green-refactor' in TS context. Provides Vitest/NestJS-specific TDD examples and commands; cross-language TDD workflow (cross-layer iterations, RED-GREEN-REFACTOR, working app, bug-fix-first-test, mocks-hiding-bugs) lives in craft:tdd-workflow-principles. DO NOT use for: node:test files (a file importing node:test) or a project whose tests run under node --test (see node-test:node-test-conventions), test writing conventions (see vitest:vitest-test-conventions), PHP TDD (see phpunit:php-tdd-workflow).
ts-refactoring
ACTIVATE when refactoring TypeScript code. ACTIVATE for 'refactor', 'extract', 'redesign', 'simplify', or 'clean up' in a TypeScript / Node / NestJS context. This skill provides TS-specific examples for the cross-language refactoring rules defined in craft:refactoring-principles. DO NOT use for: writing new features from scratch, general OOP patterns (see ts-oop).
ts-ports-adapters
ACTIVATE when a TypeScript codebase with no dependency-injection container has to declare a port and wire its adapter — a repository, a clock, an id generator, an event dispatcher the domain depends on. ACTIVATE for 'port', 'adapter', 'SPI', 'dependency inversion' or 'composition root' in a codebase that wires its dependencies by hand. Covers: the port as a type of functions in the domain layer, the adapter as an object literal annotated with it, the handler that takes its dependencies first and its data second, the composition root in the infrastructure layer, and the in-memory stub that substitutes for the adapter in a unit test. DO NOT use for: NestJS, or any container-based DI with decorators and tokens (see nest-ddd-conventions), aggregate operations and smart constructors (see ddd-ts-fp), the Result type itself (see ts-functional).
ts-oop
ACTIVATE when designing TypeScript classes, and the value objects or collections modelled as classes, or when the user asks about object design, encapsulation, Tell Don't Ask, or Symbol.iterator. Provides TS-specific examples for the cross-language OOP rules defined in craft:oop-principles, plus TS-specific patterns (Symbol.iterator, branded types for primitive identifiers). DO NOT use for: a value object or an aggregate modelled as an immutable record rather than a class (see ddd-ts-fp), refactoring methodology (see ts-refactoring).
ts-layer-boundaries
ACTIVATE when wiring a Worker entry point, a Router, a controller, or a domain event consumer in a TypeScript CQRS codebase, or when deciding where a port belongs. ACTIVATE for 'composition root', 'layer boundary', 'ESLint zones', 'domain must not import infrastructure'. Covers: the *Worker.ts composition root, the curried consumer, Router wiring of command endpoints, the five-gesture controller, the domain-to-infrastructure import boundary enforced by ESLint, infrastructure-only ports, in-memory doubles for every port, the read-can-reach-write asymmetry, and read endpoints going through a domain handler. DO NOT use for: the port's own shape and adapter binding (see ts-ports-adapters), the Result type (see ts-functional), domain event structure (see ts-ddd-events).