Use when user describes a feature to build. Triggers: 'spec this', 'create specification', 'define requirements', 'I want to build'.
Purpose: Transform feature descriptions into structured JSON specifications with Given/When/Then scenarios and comprehensive edge case coverage, creating clear acceptance criteria for implementation. A no-gaps completeness gate (Step 5) ensures the spec names every application, datastore, contract, and integration it implies — so "done" can't mean green-but-not-functional.
Output format: JSON -- structured data readable by Claude, machine-readable by other tools, and queryable by other skills.
User triggers:
Before:
/shipkit-plan (this creates the spec that planning needs)implement (no skill needed) (need spec before implementing)Workflow position:
Recommended:
.shipkit/stack.json (to understand tech constraints).shipkit/schema.json (to understand data model)Optional but helpful:
.shipkit/architecture.json.shipkit/specs/todo/*.json, .shipkit/specs/active/*.json (check for similar patterns)If missing: Infer tech stack from codebase signals (package.json, imports, config files); return gaps_found if critical context cannot be derived (fork context — no user prompt)
If $ARGUMENTS is provided (e.g. /shipkit-spec user login flow), use it as the initial feature description. Skip Question 1 (Feature Type prompt) and infer the type from the description. Proceed directly to deeper clarifying questions.
If $ARGUMENTS is empty, proceed normally from Step -1.
After clarifying the feature (Step 1), create tasks:
TaskCreate: "Read context + explore affected code (2 agents)"TaskCreate: "Archive existing spec (if overwriting)"TaskCreate: "Generate spec JSON (all 20+ fields)"TaskCreate: "Validate completeness (checklist + no-gaps gate)"TaskCreate: "Write spec to disk"TaskCreate: "Spec: {feature-name}"TaskUpdate each task to in_progress when starting it, completed when done.
Do NOT skip the validation step (Step 5) after generating the spec. In batch mode, do NOT stop after the first feature — ask to continue.
Check if a spec roadmap or product definition exists with features to spec:
.shipkit/spec-roadmap.json (if exists)specStatus is "none"specStatus in the roadmap.shipkit/product-definition.json (if exists)mvp is true and no spec exists yetdependencies field (features with no dependencies first)product-definition.json or no unspecced features: proceed to Step 0Check if sufficient context exists to propose a spec without interactive questions:
Context sufficiency: product-definition.json exists for this feature, OR (why.json exists with sufficient context)
.shipkit/product-definition.json — product blueprint: features, patterns, differentiators.shipkit/engineering-definition.json — engineering blueprint: mechanisms, components.shipkit/architecture-map.json — current-state map (applications/datastores/contracts/integrations) for the no-gaps cross-check (if exists).shipkit/goals/product.json — product success criteria (if exists).shipkit/goals/engineering.json — engineering success criteria (if exists).shipkit/product-discovery.json — persona details and user needs.shipkit/stack.json — tech constraints.shipkit/architecture.json — existing architecture decisions.shipkit/codebase-index.json — existing code patternsreferences/no-gaps-checklist.md — each implied element classified COVERED / FLAGGED / EXPLICITLY-DEFERRED, cross-checked against architecture-map.json + engineering-definition.json.shipkit/design-system/ exists and the feature has a UI component, reference design tokens in the spec's technical notes and check that scenarios respect design principlesProposed spec for: {feature name}
[Full spec JSON preview]
If insufficient context: Fall through to Step 1.
Fork context — no user prompts. Read requirements from the upstream spec-roadmap entry and product-definition features. Infer feature type and complexity from the feature description, stack.json, and engineering-definition.json. If the feature is ambiguous and the description is insufficient to proceed, return a gaps_found status pointing at the specific missing clarification and exit. The orchestrator reviewer will trigger a re-dispatch after upstream is fixed.
USE PARALLEL READS FOR CONTEXT LOADING - All context files are independent:
Read these files IN PARALLEL (single message, multiple tool calls):
1. Read: .shipkit/stack.json # Tech constraints
2. Read: .shipkit/schema.json # Data model
3. Read: .shipkit/architecture.json # Past decisions
4. Glob + Read: .shipkit/specs/active/*.json # Similar specs
Why parallel: All 4 reads are independent - no file depends on another. Parallel reads reduce context loading time by ~40%.
Token budget: Keep context reading under 1500 tokens total.
If files don't exist: Proceed without them using inferred context from codebase; return gaps_found if critical context is missing (fork context — no user prompt).
Before writing a spec, understand the actual codebase that will change.
Specs written without reading source code miss existing patterns, hidden constraints, and ripple effects. This step ensures the spec is grounded in reality.
3a. Index Lookup — Read .shipkit/codebase-index.json first:
Read: .shipkit/codebase-index.jsonconcepts to find files related to the feature areaentryPoints to understand where the feature connectscoreFiles to identify high-dependency files that must be preserved3b. Identify code areas — From the feature description + context files + index data, determine:
3c. Explore code — Use Read, Grep, and Glob directly to investigate the feature area (Agent tool is not available in fork context):
Run both explorations sequentially — direct code first, then ripple effects using findings from the first pass.
3d. Synthesize findings — Before generating the spec, note:
If exploration reveals surprises: Surface them to the user before proceeding. Example: "The codebase already has a partial implementation of X using pattern Y — should the spec build on that or replace it?"
Token budget: Each explore agent should return a focused summary (~500 tokens). Don't dump raw code into context — summarize patterns and constraints.
When to skip: If the feature is entirely greenfield (no existing related code), or the user explicitly says "I know the codebase, just spec it", this step can be abbreviated to a quick Glob search to confirm there's nothing unexpected.
Artifact strategy: archive — Before writing, if the target file already exists, move it to .shipkit/archive/{filename}.{ISO-date}.json (create the archive/ directory if needed). Then write the new artifact fresh.
Create spec file using Write tool:
Location: .shipkit/specs/todo/{feature-name}.json (new specs start in todo/)
Use kebab-case for filename: recipe-sharing.json, user-authentication.json
JSON Schema: See references/output-schema.md for complete schema definition
Example: See references/example.json for realistic feature spec
Before saving spec, verify:
as, iWant, soThat fieldsRun the four-dimension completeness pass per references/no-gaps-checklist.md. This is what stops a spec being green-but-not-functional (a UI calling an endpoint nobody specified, saving to a table nobody created).
.shipkit/architecture-map.json and .shipkit/engineering-definition.json (greenfield: derive from spec + engineering-definition + stack; record architectureMapUsed: false).when/then that displays, fetches, lists, saves, updates, or deletes data, confirm a contract serves it and a datastore holds it. If not → FLAGGED.gapReport.sharedContracts and any ownerless reads in gapReport.unbackedSurfaces.auth.uid() → profile/tenant key) must be declared before any schema or RLS is specced — it's an integrations-dimension contract resolved first. Auth present but identity key undeclared before schema/RLS elements → FLAGGED. Record it in gapReport.identityContract.technical) or deferring it with a reason (→ EXPLICITLY-DEFERRED).functionalSurface, gapReport (incl. sharedContracts, unbackedSurfaces, identityContract), and deferred onto the artifact (see references/output-schema.md).gapReport.status is clear (FLAGGED = 0, no recomputed shared contracts, no unbackedSurfaces, and if auth is present identityContract.declaredBeforeSchema: true) before saving. A spec with any FLAGGED element is not done.Use Write tool to create: .shipkit/specs/todo/{feature-name}.json
Output to user:
Specification created
Location: .shipkit/specs/todo/{feature-name}.json
Summary:
- [X] core scenarios
- [Y] edge cases identified across 6+ categories
- [Z] acceptance criteria (must/should/won't)
- [N] key test cases mapped
Completeness:
- User story: done
- Scenarios (Given/When/Then): done
- Edge cases: done (all core categories + external-service if applicable)
- Acceptance criteria: done
- Test strategy: done
Copy and track:
.shipkit/specs/todo/{name}.jsonEvery spec MUST follow the Shipkit artifact convention:
{
"$schema": "shipkit-artifact",
"type": "spec",
"version": "1.0",
"lastUpdated": "2025-01-15T10:00:00Z",
"source": "shipkit-spec",
"summary": {
"name": "Feature Name",
"status": "todo",
"featureType": "user-facing-ui",
"complexity": "medium",
"scenarioCount": 3,
"acceptanceCriteriaCount": 10,
"edgeCasesApplied": ["loading", "error", "empty", "permission", "boundary", "consistency", "external-service"]
},
"metadata": {
"id": "spec-feature-name",
"created": "2025-01-15",
"updated": "2025-01-15",
"author": "shipkit-spec"
},
"problem": {
"statement": "Clear problem statement",
"userStory": {
"as": "user type",
"iWant": "to do something",
"soThat": "I get some benefit"
}
},
"scenarios": [
{
"id": "scenario-1",
"name": "Primary happy path",
"type": "happy-path",
"given": ["Initial state 1", "Initial state 2"],
"when": "User action",
"then": ["Expected outcome 1", "Expected outcome 2"]
}
],
"edgeCases": {
"loading": ["Loading consideration 1"],
"error": ["Error handling 1"],
"empty": ["Empty state 1"],
"permission": ["Permission check 1"],
"boundary": ["Boundary condition 1"],
"consistency": ["Data consistency 1"]
},
"acceptanceCriteria": {
"mustHave": ["Critical requirement 1"],
"shouldHave": ["Nice to have 1"],
"wontHave": ["Explicitly excluded 1"]
},
"outOfScope": ["What is NOT included"],
"dependencies": ["What must be in place first"],
"technical": {
"databaseChanges": ["Table/field additions"],
"apiEndpoints": [
{ "method": "POST", "path": "/api/resource", "purpose": "Create resource" }
],
"existingCode": {
"directlyAffected": ["src/path/to/file.ts — description of what exists and how it changes"],
"rippleEffects": ["src/path/to/consumer.ts — depends on X, must update Y"],
"patternsToFollow": ["Existing codebase uses pattern Z for similar features"],
"contractsToPreserve": ["API signature of functionA() is consumed by 3 callers"]
},
"notes": ["Implementation hints"]
},
"functionalSurface": {
"applications": [
{ "name": "web-ui", "kind": "frontend", "verdict": "COVERED", "evidence": "exists in architecture-map" }
],
"datastores": [
{ "name": "share_links", "kind": "table", "verdict": "COVERED", "evidence": "technical.databaseChanges" }
],
"contracts": [
{ "name": "GET /api/share/{token}", "kind": "rest-endpoint", "verdict": "COVERED", "evidence": "technical.apiEndpoints; surfaced by no-gaps gate (frontend-implies-backend)" }
],
"integrations": [
{ "name": "Supabase Auth", "kind": "auth-provider", "verdict": "COVERED", "evidence": "dependencies" }
]
},
"gapReport": {
"status": "clear",
"dimensions": { "applications": "covered", "datastores": "covered", "contracts": "covered", "integrations": "covered" },
"flagged": [],
"sharedContracts": [
{ "field": "grade_band", "usedBy": ["student-view", "coach-dashboard"], "owner": "grade_band_v (view)", "status": "owned" }
],
"unbackedSurfaces": [],
"identityContract": { "key": "auth.uid()", "mapsTo": "profiles.id", "declaredBeforeSchema": true },
"architectureMapUsed": true,
"confidence": "high"
},
"deferred": [
{ "dimension": "datastores", "element": "share analytics store", "reason": "out of scope this iteration (acceptanceCriteria.wontHave)" }
],
"testStrategy": {
"callFlows": ["User -> Component -> API -> DB -> Response"],
"coverage": [
{ "layer": "Business logic", "testType": "Unit", "whatToTest": "..." }
],
"mocking": {
"mock": ["External services"],
"testDoubles": ["Database"],
"real": ["Fast internal services"]
},
"keyTestCases": [
{ "scenario": "Happy path", "testType": "Integration", "testName": "should..." }
]
},
"references": {
"stack": ".shipkit/stack.json",
"schema": ".shipkit/schema.json",
"architecture": ".shipkit/architecture.json",
"relatedSpecs": []
},
"nextSteps": ["/shipkit-plan to create implementation plan"]
}
Full schema: See references/output-schema.md
Example: See references/example.json
Apply ALL 6 core categories to EVERY feature (+ External Service Constraints when feature involves external APIs):
See references/best-practices.md for frontend and backend quality standards.
/shipkit-project-context - Generates stack.json and schema.json
User describes feature idea - Provides initial feature concept
/shipkit-plan - Creates implementation plan from spec
/shipkit-engineering-definition - Update engineering blueprint with decisions
Recommended (read if exist):
.shipkit/stack.json - Tech stack constraints.shipkit/schema.json - Data model.shipkit/architecture.json - Past decisions.shipkit/architecture-map.json - Current-state map (apps/datastores/contracts/integrations) for the no-gaps gate.shipkit/engineering-definition.json - Engineering blueprint (mechanisms/components) for the no-gaps gate.shipkit/goals/product.json - Product success criteria.shipkit/goals/engineering.json - Engineering success criteria.shipkit/reviews/planning-assessment.json - Reviewer feedback (on re-dispatch)Optional (read if relevant):
.shipkit/specs/active/*.json - Check for similar specs.shipkit/design-system/DIRECTION.md, .shipkit/design-system/tokens/, .shipkit/design-system/PRINCIPLES.md - Design tokens and principles for UI feature specsIf missing: Design system files are optional — proceed without if absent. Return gaps_found only if the spec explicitly requires design system context (fork context — no user prompt).
Write Strategy: CREATE (with lifecycle transitions)
Creates:
.shipkit/specs/todo/{feature-name}.json - New specification (starts in todo/)Folder Structure:
.shipkit/specs/
├── todo/ # Defined, ready to start
├── active/ # Being implemented
├── parked/ # On hold (blocked, deprioritized)
└── shipped/ # Delivered to users
Update Behavior:
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer