speckit-companion-specify
Companion specify โ spec.md with prioritized user stories
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Menu
Companion specify โ spec.md with prioritized user stories
Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
Based on SOC occupation classification
Perform a non-destructive cross-artifact consistency and quality analysis
Identify underspecified areas in the current feature spec by asking up
Companion implement โ execute tasks.md in dependency order, then mark complete
Mark the active spec completed โ the Companion workflow's terminal step (writes status: completed)
Companion plan โ implementation plan with research & design artifacts
Companion tasks โ user-story phased task list
| name | speckit-companion-specify |
| description | Companion specify โ spec.md with prioritized user stories |
| compatibility | Requires spec-kit project structure with .specify/ directory |
| metadata | {"author":"github-spec-kit","source":"companion:commands/speckit.companion.specify.md"} |
$ARGUMENTS
Companion runs on top of stock spec-kit, so a project's installed spec-kit extensions (git, and any others registered in .specify/extensions.yml) must still fire on a Companion run exactly as they do on a stock /speckit.* run. This is separate from Companion's own node-hooks (.specify/companion.yml): both fire. Like the rest of the pipeline, checking these hooks must never fail the host command โ if anything is missing or malformed, skip silently and continue.
Let <step> be this command's phase: specify, plan, tasks, or implement.
Before-hooks โ run these now, before any of the work below.
.specify/extensions.yml exists in the project root. If it does not, skip silently โ there are no hooks.hooks.before_<step>. If the YAML cannot be parsed, skip hook checking silently and continue normally.enabled is explicitly false. A hook with no enabled field is enabled by default.condition expression yourself: a hook with no condition (or a null/empty one) is executable; a hook with a non-empty condition is left to the HookExecutor โ skip it here.optional flag:
optional: true):
## Extension Hooks
**Optional Pre-Hook**: {extension}
Command: `/{command}`
Description: {description}
Prompt: {prompt}
To execute: `/{command}`
optional: false):
## Extension Hooks
**Automatic Pre-Hook**: {extension}
Executing: `/{command}`
EXECUTE_COMMAND: {command}
Wait for the result of the hook command before proceeding to the Outline.
After-hooks โ run these once this command's work is fully reported, before handing off.
.specify/extensions.yml; if absent or unparseable, skip silently. Look under hooks.after_<step>, applying the same enabled / condition filtering as above.optional: true):
## Extension Hooks
**Optional Hook**: {extension}
Command: `/{command}`
Description: {description}
Prompt: {prompt}
To execute: `/{command}`
optional: false):
## Extension Hooks
**Automatic Hook**: {extension}
Executing: `/{command}`
EXECUTE_COMMAND: {command}
For specify, branch creation is normally one of these before_specify hooks (the git extension); spec directory and file creation are always handled by the command body itself.
Produce a feature specification: prioritized user stories with acceptance scenarios, functional requirements, key entities, edge cases, and measurable success criteria, then a quality checklist.
Resolve the feature directory โ mint a fresh dir for new work. .specify/feature.json is an output of this step, not an input to reuse: it points at the previous spec (frequently already completed), so reusing it would clobber finished work. Pick the target:
SPECIFY_FEATURE_DIRECTORY is set), use it.specs/ for the highest NNN-โฆ prefix, derive a 2โ4 word short-name from the description, and use specs/<NNN+1>-<short-name>/. Never write into a directory that already contains a spec.md โ that's a stale pointer to a prior spec, not this feature.
Create <feature_directory>/, point .specify/feature.json at it, then record the specify START so the step's duration begins now (the script stamps the real clock โ do not hand-write this):python3 .specify/extensions/companion/scripts/write-context.py --feature-dir <feature_directory> --step specify --status specifying --kind start --by extension
Create <feature_directory>/spec.md with these sections, in order. Write for a business stakeholder โ plain language first, focused on what users need and why, not how to build it. Reserve inline code for literal identifiers a reader would copy (real names, routes, keys); never backtick ordinary nouns.
### User Story N - <short title> (Priority: P1|P2|P3) followed by one plain-language paragraph describing the journey.**Given** โฆ **When** โฆ **Then** โฆ cases.
Order P1 first (the MVP slice); add as many stories as the feature genuinely needs.FR-001โฆ list; each a single, testable MUST/SHOULD statement. Mark a genuinely unresolvable choice [NEEDS CLARIFICATION: โฆ] (max 3; prefer an informed default and record it under Assumptions instead).SC-001โฆ outcomes (time, count, percentage, pass/fail). No framework, API, or database names.data-testid, a route path, an API endpoint/method, a CLI flag, an env var name, a config key, exact UI copy, a column name โ record it here verbatim, in backticks, exactly as written. These are requirements the user pinned, not implementation details you may rephrase, so they are the one place exact identifiers belong in the spec. Do not paraphrase, normalize casing, pluralize, or invent a "nicer" name; downstream steps and the implementation MUST use these exact strings. If the request pins none, omit this section.Keep it business-readable. Every vague requirement should fail a "testable and unambiguous" check โ tighten it. Remove a section that genuinely does not apply rather than leaving it as "N/A". The one exception to "no implementation detail" is Verbatim Constraints: an exact value the user specified is a requirement, and dropping it (forcing a later step to guess) is a defect.
Spec quality checklist. Write <feature_directory>/checklists/requirements.md using the template below, then run a single self-check pass: grade each item pass/fail, fix obvious fails in spec.md in place, and leave any genuine ambiguity as a [NEEDS CLARIFICATION: โฆ] marker (max 3) for the clarify step. Do not run a multi-iteration rewrite loop or prompt the user with option tables โ Companion defers interactive clarification to clarify. Update the checklist to reflect the final pass/fail state.
# Specification Quality Checklist: [FEATURE NAME]
**Purpose**: Validate Companion specification completeness before planning
**Created**: [DATE]
**Feature**: [Link to spec.md]
## Content Quality
- [ ] No implementation details (languages, frameworks, APIs)
- [ ] Focused on user value and business needs
- [ ] Written for non-technical stakeholders
- [ ] All mandatory sections completed (User Scenarios, Requirements, Success Criteria)
## Requirement Completeness
- [ ] Any [NEEDS CLARIFICATION] markers are genuine ambiguities (โค3) deferred to clarify โ not unresolved guesses
- [ ] Each Functional Requirement is a single, testable MUST/SHOULD statement
- [ ] Success criteria are measurable
- [ ] Success criteria are technology-agnostic (no implementation details)
- [ ] All acceptance scenarios are defined
- [ ] Edge cases are identified
- [ ] Scope is clearly bounded
- [ ] Dependencies and assumptions identified
## Feature Readiness
- [ ] All functional requirements have clear acceptance criteria
- [ ] User scenarios cover primary flows
- [ ] Feature meets measurable outcomes defined in Success Criteria
- [ ] No implementation details leak into the specification
## Notes
- Items marked incomplete require spec updates before clarify or plan
Classify the change โ right-size the ceremony. After the spec content is drafted, decide whether this change is small enough to fast-track straight to implement, or large enough to keep the full specify โ plan โ tasks โ implement pipeline. Apply the shared size definition below โ the same one the standalone size step uses, so the small/large bar is authored in exactly one place. This is a best-effort heuristic and MUST err toward normal on weak or conflicting signals โ a change is never under-planned by accident.
The two constants (5 files / 10 tasks) are the same guardrail the old complexityFastPath used.
Estimate projectedFiles and projectedTasks for the drafted requirements, and read a scopeSignal from the wording ("larger" for rewrite | overhaul | new system | migration | redesign | โฆ; "smaller" for one-line | rename | typo | tweak | copy change | โฆ; else "none"). Then map the size definition above to a verdict:
crossedGuardrail = the change exceeds the **small** bar above (more files or tasks than it allows)
verdict = "simple" if the change is **small** by the definition above
and scopeSignal != "larger"
else "normal"
Guardrail warning. When crossedGuardrail == true OR scopeSignal == "larger", print this line verbatim, then run the normal branch (never a silent fast-track):
[companion] Change exceeds the small-change guardrail (5 files / 10 tasks) โ running the full pipeline.
Exactly-at-threshold (projectedFiles == 5 / projectedTasks == 10) is the simple ceiling โ it does not warn and stays eligible for simple.
Persist the size verdict so the later steps (plan, tasks) can right-size their output without re-deciding it. Right after classifying, record the verdict on the spec's context from the repository root:
python3 .specify/extensions/companion/scripts/write-context.py --set size=<simple|normal|oversized>
Write simple when the change is the small, fast-trackable size; oversized when it crossed the guardrail; otherwise normal. This only writes a plain size field โ it never touches the lifecycle log. Best-effort: if python3 is unavailable, skip without failing the command.
Branch on the verdict.
simple โ minimal mode. Write three lean files in this one pass so the file-driven views (top stepper, sidebar, implement progress) reconcile with the history-driven fold โ never a single combined spec.md:
spec.md โ the files to touch and any dependencies, in a few bullets (the plan content, inline; this stays the plan source-of-truth).<feature_directory>/plan.md as a short pointer to the spec's Approach (e.g. a one-line blockquote linking ./spec.md#approach and ./tasks.md). Do not duplicate the approach bullets โ plan.md references them.<feature_directory>/tasks.md carrying the real task checklist โ a dependency-ordered list, one per line as - [ ] **T001** [P?] <description> + <path> ([P] marks tasks that can run in parallel). This MUST be the actual checklist, not a pointer: implement progress counts these checkboxes, so a pointer would read 0/0.Put the task checklist only in tasks.md โ do not keep a second copy in spec.md (the duplicate would drift). spec.md keeps the Approach; tasks.md owns the tasks.
Still write <feature_directory>/checklists/requirements.md as in step 4. Do not run /speckit.companion.plan or /speckit.companion.tasks โ the three lean files plus the lifecycle fold below record those steps as satisfied.
normal โ full pipeline. Write spec.md only (no appended Approach section, no plan.md / tasks.md here, no lifecycle fold). The existing pipeline continues unchanged: plan and tasks are produced and recorded by their own /speckit.companion.plan and /speckit.companion.tasks runs.
Output: <feature_directory>/spec.md + <feature_directory>/checklists/requirements.md. In simple mode, spec.md additionally carries an Approach section, and two lean files are emitted alongside it โ plan.md (a pointer to that Approach) and tasks.md (the real - [ ] **T001** โฆ checklist; the task list lives here, not in spec.md); in normal mode, spec.md holds the four sections only and no plan.md / tasks.md are written here.
Record completion. After spec.md is written, close the specify step โ the extension stamps the real end (do not hand-write an ai complete for specify):
python3 .specify/extensions/companion/scripts/write-context.py --feature-dir <feature_directory> --step specify --status specified --kind complete --by extension
Fast-path lifecycle fold (simple mode only). When verdict == "simple", record the folded plan and tasks steps so the history-driven panels read them as satisfied-by-fast-path โ pairing with the lean plan.md / tasks.md files above, which make the file-driven stepper, sidebar, and implement progress agree โ and the spec lands ready for implement. Run these in order, after the specify completion above (each call stamps its own real clock โ do not hand-write these, and do not run them for a normal verdict):
python3 .specify/extensions/companion/scripts/write-context.py --feature-dir <feature_directory> --step plan --kind start --substep fast-path --by ai
python3 .specify/extensions/companion/scripts/write-context.py --feature-dir <feature_directory> --step plan --kind complete --substep fast-path --by ai
python3 .specify/extensions/companion/scripts/write-context.py --feature-dir <feature_directory> --step tasks --kind start --substep fast-path --by ai
python3 .specify/extensions/companion/scripts/write-context.py --feature-dir <feature_directory> --step tasks --kind complete --substep fast-path --status ready-to-implement --by ai
After the fold, the spec sits at the tasks step with status: ready-to-implement; the developer triggers implement next. Do not write a completed status โ the final completed gate stays a user action.
.spec-context.json honestThese rules apply to every Companion profile command. The extension records lifecycle timing with its own scripts wherever it can; these rules keep anything you append consistent with that and accurate for any dispatcher (terminal, IDE chat, or the GUI). The model is finish-only: each task and each substep records a single finish event, and its duration is the gap to the previous finish (or the step's start). Never a start+complete pair for a task or substep โ a pair stamped at one instant is what produces 0s ticks and bursts.
Never hand-edit .spec-context.json. Record every finish by running the writer script, never by editing the JSON file yourself โ a hand-authored edit is what corrupts the file (a duplicated status key). The script stamps the real clock, writes atomically, and is idempotent. The commands below are the only way you touch timing.
Self-close โ but not specify or implement. When your own work for plan, tasks, clarify, or analyze ends, record the step finish (feature dir from .specify/feature.json):
python3 .specify/extensions/companion/scripts/write-context.py --feature-dir <feature_dir> --step <this step> --finish --by ai
--finish appends a single step-level complete and touches nothing else (it leaves status/currentStep to the lifecycle hooks). Do NOT self-close specify or implement: the extension closes those itself (specify from its own command, implement from the end-of-step hook), so an ai complete there would duplicate it.
Substeps โ one finish each, via the script. For each substep boundary (plan: research, design; tasks: generate), the moment that substep ends, run:
python3 .specify/extensions/companion/scripts/write-context.py --feature-dir <feature_dir> --step <step> --substep <name> --finish --by ai
One call per substep, each stamped with its own real clock at the moment it finishes โ never two substeps in one batch, never a separate start. The delta between consecutive finishes is each substep's duration.
Implement โ finishing a task is logging it (finish-only). Recording a task's finish is the closing action of that task, done the instant its work is complete and before you start the next one โ not a bookkeeping pass you batch at the end of a phase. The closing action is a single append (feature dir from .specify/feature.json):
python3 .specify/extensions/companion/scripts/write-context.py --feature-dir <feature_dir> --task <TaskID> --kind complete --by ai --did "<one-line summary of what this task did>" --files "<comma,separated,files,touched>" --append
--append writes one line to .spec-context.events.jsonl and does not read or rewrite the shared .spec-context.json, so it never hits the "read the file first" retry and parallel workers can each append their own finish at the same time without contending โ the line carries its own real timestamp (date -u is stamped by the script). The --did/--files flags ride along so the Activity panel's Tasks card is populated from the script. Do NOT hand-edit the - [ ] checkbox in tasks.md โ the script owns it: materialize flips it to - [x] from your appended finish, so a fanned-out subagent only appends and never touches the shared tasks.md. Do NOT hand-author per-task JSON and do NOT write a per-task start.
Then fold the appended lines into .spec-context.json โ run this once per wave (after the wave reconciles) and again when the step ends:
python3 .specify/extensions/companion/scripts/write-context.py --feature-dir <feature_dir> --materialize
--materialize is the one read-modify-write: it folds the finishes into the panel and checks off the matching tasks.md boxes for every journaled task, idempotently (re-folding never double-counts), so running it per wave keeps the GUI current without re-serializing the work. The end-of-step hook is a backstop that materializes anything you didn't fold and fills any task you didn't journal. What's trustworthy here is the per-task summary (did/files) and the order tasks completed, plus the step-level startโcomplete span, which the scripts stamp exactly. The per-task timestamps are best-effort โ a single agent logs a task right after building it, so they reflect when you recorded it, not a precisely measured duration; that's fine, the summaries are the point. Still, record each finish as you go, wave by wave rather than dumping every task at the very end โ a per-wave cadence keeps the panel live and the ordering true.
Never write the next step's start. Only the next command appends the next step's start entry; writing it here makes the viewer render a phantom "Generating โฆ".
This is one step in the Companion pipeline. How the run continues depends on the environment you are running in; do not invoke a separate headless/deterministic run command for the everyday flow.
speckit-extension/workflows/speckit-companion.workflow.yml) to learn which step comes next, then continue into it on your own โ dispatch the next step's /speckit.companion.* command and keep going through the pipeline.gate (e.g. review-spec, review-plan), stop and wait for approval rather than running past it. Only continue once the gate is approved.mark-complete. Run it so the spec lands at status: completed. That step writes completed only through write-context.py --mark-complete, which refuses unless the spec is already implemented โ never introduce a second completed-writer.before/after insertsThis command is assembled from ordered nodes. A project can attach its own work at the boundary before or after any node by declaring it in .specify/companion.yml. You are the runtime: read that file (if present) and run those hooks at the right moments. Like the rest of the pipeline, this must never fail the host command โ degrade and continue.
Find the hooks for this command. Look up commands.<this-command>.hooks in .specify/companion.yml. It has two anchors, before and after, each keyed by a node id from this command's order. Run a node's before hooks immediately before that node's work, and its after hooks immediately after. When several hooks sit at one anchor, run them top to bottom, in declared order.
Hook types:
{ type: command, run: "<shell>" } โ run the shell command with your terminal/Bash tool, then continue. If you have no terminal tool (some chat-only providers), do not pretend to: report the command you would have run and continue.{ type: prompt, text: "<instruction>" } โ treat the text as an inline instruction and act on it before moving on.{ type: node, ref: <id> } โ read .specify/companion/nodes/<id>.md and carry out its body as if it were part of this command.Background hooks. Any hook may add background: true. Kick it off and continue the pipeline immediately without waiting for it to finish โ it must not hold the spec prisoner. Use it for slow, independent side-effects (a test run, a build, a notification): for a command, launch it detached (e.g. append & or use nohup โฆ &); for a node/prompt, do its work without blocking the next step. Report its result whenever it lands, but never block on it. Do not mark a background hook on anything that writes .spec-context.json (the timing/capture calls): those are fast already and run a read-modify-write on the shared file, so two of them racing in the background can lose an update. Background is for side-effects, not bookkeeping.
Failure handling (never abort the host command):
.specify/companion.yml โ there are no hooks; run the command exactly as written. Do not warn.type: node hook's ref file is missing โ this is a real misconfiguration: report it clearly and stop before doing damage, rather than silently skipping.If a hook's own work fails (a command exits non-zero, a node can't complete), report it and โ unless the failure clearly makes the rest unsafe โ continue the pipeline. The host command's own output is never blocked by a hook.