@benjaminthomas/spec-driven-dev 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -93,6 +93,23 @@ words in quotes.
93
93
  /spec-ship "ship it"
94
94
  ```
95
95
 
96
+ **The right model for each task.** `spec-create` rates every task **light**, **standard**, or
97
+ **deep**, based on how much it touches and how risky it is (data, security, concurrency, public
98
+ contracts, unclear requirements). `spec-implement` then runs each task on the model for its tier:
99
+
100
+ | Tier | Typical task | Claude Code default |
101
+ |---|---|---|
102
+ | light | copy change, config value, small UI tweak following an existing pattern | `haiku` |
103
+ | standard | a normal feature slice: route + service + tests | `sonnet` |
104
+ | deep | migrations with backfills, auth/permissions, concurrency, cross-repo contracts | `opus` |
105
+
106
+ - **Reviews:** each batch's review runs on at least `standard`, and on the batch's highest tier.
107
+ - **Escalation:** a task that still fails after a fix round is retried one tier up.
108
+ - **Tools that run one model per session** (Cursor, Codex) don't switch silently. Before a heavier
109
+ batch, they pause and tell you which model to switch to.
110
+ - **Changing the models:** edit the `models:` section of `.spec-driven.yml`, for example to use a
111
+ newer model for `deep`.
112
+
96
113
  **Fewer stops:** after reviewing the spec, say **"approved, run it through"**. The workflow then
97
114
  implements, verifies, and commits in one go. It stops at the first failure, and it always asks
98
115
  before pushing or opening a PR. To make this the default after approval, choose `run-through` during
@@ -139,6 +156,7 @@ straight away, so you can fix it before moving on. Press Enter on an empty line
139
156
  | Lint / typecheck / test / build / dev commands | `package.json` scripts and lockfile (per repo or package) | `pnpm run lint` |
140
157
  | Where specs go | (you choose) | `specs/` |
141
158
  | After you approve a spec | (you choose) | `stop` (wait at each stage) or `run-through` |
159
+ | Model per task tier | written with defaults: Claude Code `haiku`/`sonnet`/`opus`, others `default` | edit `models:` later |
142
160
 
143
161
  **Where the file lives, by layout:**
144
162
 
@@ -542,15 +560,15 @@ skips that step rather than failing.
542
560
 
543
561
  | Skill | Stage | SKILL.md lines | Words | Supporting files |
544
562
  |---|---|---:|---:|---:|
545
- | `spec-setup` | 0 · configure | 114 | 1,022 | 1 |
546
- | `spec-investigate` | 1 · understand | 325 | 2,741 | 1 |
547
- | `spec-create` | 2 · plan | 157 | 1,415 | 4 |
548
- | `spec-implement` | 3 · build | 273 | 2,109 | 3 |
563
+ | `spec-setup` | 0 · configure | 117 | 1,068 | 1 |
564
+ | `spec-investigate` | 1 · understand | 330 | 2,794 | 1 |
565
+ | `spec-create` | 2 · plan | 166 | 1,503 | 5 |
566
+ | `spec-implement` | 3 · build | 298 | 2,375 | 3 |
549
567
  | `spec-verify` | 4 · prove | 155 | 1,492 | 1 |
550
568
  | `spec-checkpoint` | 5 · commit | 108 | 795 | 0 |
551
569
  | `spec-ship` | 6 · PR | 220 | 1,268 | 0 |
552
570
  | `generative-seo` | standalone | 255 | 2,209 | 7 |
553
- | **Total** | | **1,607** | **13,051** | **17** |
571
+ | **Total** | | **1,649** | **13,504** | **18** |
554
572
 
555
573
  - **8** skills: **1** setup + **6** workflow stages + **1** standalone
556
574
  - **4** tools supported natively (Claude Code, Codex CLI, Cursor, Antigravity), plus an MCP fallback
package/bin/cli.mjs CHANGED
@@ -235,11 +235,22 @@ function toYaml(c) {
235
235
  out += `tracker:\n type: ${c.tracker.type}\n key: ${y(c.tracker.key)}\n url: ${y(c.tracker.url)}\n`;
236
236
  out += `git:\n${cmds(c.git, " ")}\n`;
237
237
  out += `commands:\n${cmds(c.commands, " ")}\n dev_url: ${y(c.devUrl)}\n`;
238
+ out += "models: # model per task tier (light / standard / deep); \"default\" = the session's model\n" + Object.entries(MODEL_DEFAULTS)
239
+ .map(([tool, m]) => ` ${tool}:\n` + TIERS.map((t) => ` ${t}: ${m[t]}\n`).join("")).join("");
238
240
  out += `pipeline:\n after_approval: ${c.afterApproval || "stop"} # stop | run-through (implement → verify → checkpoint; ship always asks)\n`;
239
241
  return out + `specs_dir: ${y(c.specsDir)}\n`;
240
242
  }
241
243
 
242
244
  const LAYOUTS = ["single", "monorepo", "multi-repo"];
245
+ const TIERS = ["light", "standard", "deep"];
246
+ // Aliases, not dated model IDs, so they keep pointing at the current models. Other tools default to
247
+ // the session's own model; spec-implement then tells you when to switch.
248
+ const MODEL_DEFAULTS = {
249
+ claude: { light: "haiku", standard: "sonnet", deep: "opus" },
250
+ cursor: { light: "default", standard: "default", deep: "default" },
251
+ codex: { light: "default", standard: "default", deep: "default" },
252
+ antigravity: { light: "default", standard: "default", deep: "default" },
253
+ };
243
254
  const splitList = (v) => (v || "").split(",").map((x) => x.trim()).filter(Boolean);
244
255
  // Paths are stored relative to the config's folder when inside it, absolute otherwise.
245
256
  const real = (p) => { try { return fs.realpathSync(p); } catch { return p; } }; // /var vs /private/var etc.
@@ -466,7 +477,8 @@ function parseYaml(text) {
466
477
  }
467
478
  const m = line.match(/^([\w-]+):\s*(.*)$/);
468
479
  if (!m) continue;
469
- const [, key, val] = m;
480
+ const [, key, rawVal] = m;
481
+ const val = rawVal.replace(/(^|\s+)#.*$/, "").trim(); // `key: # comment` opens a block
470
482
  if (val === "") {
471
483
  const next = text.split("\n").slice(text.split("\n").indexOf(raw) + 1).find((l) => l.trim() && !l.trim().startsWith("#"));
472
484
  parent[key] = next && next.trim().startsWith("- ") ? [] : {};
@@ -499,6 +511,11 @@ function doctor() {
499
511
  if (!["single", "monorepo", "multi-repo"].includes(c.layout)) problems.push(`layout "${c.layout}" must be single, monorepo, or multi-repo`);
500
512
  if (c.tracker && !["jira", "github", "linear", "none"].includes(c.tracker.type)) problems.push(`tracker.type "${c.tracker.type}" must be jira, github, linear, or none`);
501
513
  if (c.git?.commit_style && !["conventional", "ticket", "freeform"].includes(c.git.commit_style)) problems.push(`git.commit_style "${c.git.commit_style}" must be conventional, ticket, or freeform`);
514
+ for (const [tool, m] of Object.entries(c.models || {})) {
515
+ if (typeof m !== "object") { problems.push(`models.${tool} must list light / standard / deep`); continue; }
516
+ for (const t of TIERS) if (!m[t]) problems.push(`models.${tool}.${t} is missing (use "default" for the session's model)`);
517
+ for (const k of Object.keys(m)) if (!TIERS.includes(k)) problems.push(`models.${tool}.${k}: unknown tier (use light, standard, deep)`);
518
+ }
502
519
  if (c.pipeline?.after_approval && !["stop", "run-through"].includes(c.pipeline.after_approval)) problems.push(`pipeline.after_approval "${c.pipeline.after_approval}" must be stop or run-through`);
503
520
  if (c.git?.branch_name && !/\{(ticket|slug)\}/.test(c.git.branch_name)) problems.push(`git.branch_name "${c.git.branch_name}" needs {ticket} or {slug}`);
504
521
  const repos = c.layout === "single" ? [{ path: "." }] : Array.isArray(c.repos) ? c.repos : [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@benjaminthomas/spec-driven-dev",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Spec-driven AI development workflow (investigate → spec → implement → verify → ship) as Agent Skills for Claude Code, Cursor, Codex CLI and Antigravity.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -68,6 +68,11 @@ Think carefully about granularity. Too coarse and agents can't work in parallel.
68
68
 
69
69
  Tests ship with the code they cover: fill in each task's **Tests** section (see the task template) instead of creating separate "write tests" tasks. Only add a standalone testing task for cross-cutting suites (e.g. a new E2E harness) or when the user asks for one.
70
70
 
71
+ **Rate each task's complexity.** Read `references/complexity-rubric.md` and give every task a tier
72
+ (`light`, `standard`, or `deep`) with the signals that justify it. `spec-implement` uses the tier to
73
+ pick the model for that task, so a risky task under-rated as `light` is the costly mistake. Round up
74
+ when unsure. If one task scores `deep` mainly because it's doing too much, split it first.
75
+
71
76
  ### Step 4: Build the Dependency Graph
72
77
 
73
78
  For each task, identify:
@@ -130,6 +135,10 @@ Files created:
130
135
  - action-required.md
131
136
  - tasks/ ({T} task files)
132
137
 
138
+ Model plan: {n} light · {n} standard · {n} deep
139
+ (models from .spec-driven.yml → light: {model}, standard: {model}, deep: {model})
140
+ Deep tasks: {task-nn — main reason}
141
+
133
142
  Batch breakdown:
134
143
  - Batch 1: {count} tasks (parallel) — {brief description}
135
144
  - Batch 2: {count} tasks (parallel) — {brief description}
@@ -0,0 +1,57 @@
1
+ # Complexity Rubric
2
+
3
+ Use this to give every task a **tier**: `light`, `standard`, or `deep`. `spec-implement` runs the
4
+ task on the model mapped to that tier in `.spec-driven.yml` (`models:`). Rate from the evidence in
5
+ the task file and investigation, not from how long the description is.
6
+
7
+ ## Contents
8
+ - Scoring
9
+ - Tier from score
10
+ - Overrides
11
+ - Examples
12
+ - What to write in the task file
13
+
14
+ ## Scoring
15
+
16
+ Add one point for each signal that applies:
17
+
18
+ | Signal | Point when… |
19
+ |---|---|
20
+ | Spread | the task changes more than 3 files, or files in more than one module/package/repo |
21
+ | New design | there's no existing pattern in the repo to copy (new abstraction, algorithm, or integration) |
22
+ | Data | it changes a schema, migration, persisted format, or data backfill |
23
+ | Concurrency | it involves async ordering, queues, retries, locking, caching, or idempotency |
24
+ | Security | it touches auth, permissions, tenant isolation, secrets, payments, or untrusted input |
25
+ | Public contract | it changes an API, event, or interface that other code or other repos depend on |
26
+ | Ambiguity | acceptance criteria rely on judgement, or the investigation left UNKNOWNs in this area |
27
+ | Hard to verify | correctness can't be fully shown by the tests listed (timing, performance, UI feel) |
28
+
29
+ ## Tier from score
30
+
31
+ | Score | Tier | Typical work |
32
+ |---|---|---|
33
+ | 0–1 | `light` | copy change, config value, small UI tweak, adding a field following an existing pattern |
34
+ | 2–3 | `standard` | a normal feature slice: a route + service + tests following existing patterns |
35
+ | 4+ | `deep` | cross-cutting design, migrations with backfills, auth/permission logic, concurrency fixes |
36
+
37
+ ## Overrides
38
+
39
+ - **Security or Data alone make a task at least `standard`**, even if nothing else applies.
40
+ - **Security + Data, or Security + Concurrency, make it `deep`**, whatever the score.
41
+ - If the score is borderline, round up: a tier too high costs money, a tier too low costs rework.
42
+
43
+ ## Examples
44
+
45
+ - "Rename the Export button to Download CSV" → 0 → `light`
46
+ - "Add `exported_at` column and show it on the orders page" → Data, Spread → 2 → `standard`
47
+ - "Make the CSV export stream rows instead of loading 10k into memory" → New design, Concurrency, Hard to verify → 3 → `standard`
48
+ - "Enforce tenant isolation on all export endpoints and backfill missing tenant IDs" → Security + Data → `deep`
49
+
50
+ ## What to write in the task file
51
+
52
+ ```markdown
53
+ ## Complexity
54
+
55
+ **Tier:** standard
56
+ **Signals:** Data (adds `exported_at` migration), Spread (api + web)
57
+ ```
@@ -29,10 +29,12 @@ graph TD
29
29
 
30
30
  ## Batches
31
31
 
32
- | Batch | Tasks | Description |
33
- |------|-------|-------------|
34
- | 1 | task-01, task-02 | {Brief description of what this batch accomplishes} |
35
- | 2 | task-03 | {Brief description} |
32
+ | Batch | Tasks | Tiers | Description |
33
+ |------|-------|-------|-------------|
34
+ | 1 | task-01, task-02 | light, standard | {Brief description of what this batch accomplishes} |
35
+ | 2 | task-03 | deep | {Brief description} |
36
+
37
+ **Model plan:** {n} light · {n} standard · {n} deep. Deep tasks: {task-03 — reason}.
36
38
 
37
39
  ## Task Status
38
40
 
@@ -15,6 +15,11 @@ pending
15
15
 
16
16
  {N}
17
17
 
18
+ ## Complexity
19
+
20
+ **Tier:** {light | standard | deep}
21
+ **Signals:** {which rubric signals applied, with the evidence — or "none"}
22
+
18
23
  ## Description
19
24
 
20
25
  {2-5 sentences describing what this task accomplishes and why it matters in the context of the overall feature. Include enough context that an agent unfamiliar with the planning conversation understands the purpose.}
@@ -82,5 +87,6 @@ pending
82
87
  - The **Dependencies** section includes prose context (not just filenames) so the agent understands its inputs without reading other files. This is the most important part of making task files self-contained.
83
88
  - **Files to Create** and **Files to Modify** make the task's scope explicit. Tasks in the same batch should not overlap on these lists.
84
89
  - **Technical Details** captures everything from the planning conversation — CLI commands, schemas, code snippets, file paths, env vars, API endpoints. If it was discussed during planning and is relevant to this task, it belongs here.
90
+ - **Complexity** comes from `complexity-rubric.md`. `spec-implement` picks the coder's model from this tier, so rate honestly: under-rating a risky task is the expensive mistake.
85
91
  - **Tests** names the tests the coder writes alongside the code. A behavioural acceptance criterion without a matching test is a gap — add the test or explain why it can only be checked by `spec-verify`.
86
92
  - **Acceptance Criteria** should be specific enough that completion can be verified objectively (by a code review agent or a human).
@@ -108,6 +108,25 @@ Read `references/coder-prompt-template.md` and construct each agent's prompt by
108
108
 
109
109
  The coder agents should NOT commit their changes — the orchestrator handles commits after review.
110
110
 
111
+ **Pick each coder's model from the task's tier.** Read the task's `## Complexity` tier (treat a
112
+ missing tier as `standard`) and look up the model in `.spec-driven.yml` → `models.<tool>.<tier>`,
113
+ where `<tool>` is the host you're running in (`claude`, `codex`, `cursor`, `antigravity`). Without a
114
+ `models` section, use `models.claude` defaults: `light → haiku`, `standard → sonnet`,
115
+ `deep → opus`.
116
+
117
+ - **Host can set a model per agent** (e.g. Claude Code's Agent tool `model` parameter): dispatch
118
+ each coder with its tier's model.
119
+ - **Host can't** (one model per session, as in Cursor or Codex): don't silently run deep tasks on a
120
+ light model. Before dispatching a batch, compare its highest tier with the model you're running
121
+ on. If the batch needs a stronger one, stop and say:
122
+ `Batch {N} has deep tasks ({task-nn: reason}). Switch to {models.<tool>.deep} for this batch, then
123
+ say "continue".` If the batch only has light tasks and you're on the deep model, mention that a
124
+ cheaper model would do, but carry on.
125
+ - If the mapped value is `default`, use whatever model the session already runs, without
126
+ recommending a switch.
127
+
128
+ Report the model used for each task in the batch summary.
129
+
111
130
  **Isolate parallel coders in git worktrees when the host allows it.** Coders sharing one working
112
131
  tree can clobber each other's edits and each other's lint/test runs. If your host can run an agent
113
132
  in its own worktree (e.g. Claude Code's `isolation: "worktree"`), use it for every coder in a
@@ -143,6 +162,9 @@ Read `references/review-prompt-template.md` and construct the review prompt by f
143
162
  - **{task_summaries}**: for each task in this batch, the task title and the coder agent's completion summary
144
163
  - **{verification_commands}**: `commands.lint`/`typecheck`/`test` from `.spec-driven.yml` for each repo or package touched, or the project's own commands if there's no config (e.g., `pnpm lint && pnpm typecheck && pnpm test`)
145
164
 
165
+ Run the reviewer on the model for the **highest tier in the batch**, and never below `standard`: the
166
+ review is where deep mistakes get caught.
167
+
146
168
  The review agent should:
147
169
  1. Run lint, typecheck, and tests
148
170
  2. Verify acceptance criteria and the tests named in each task file
@@ -160,6 +182,9 @@ If the review returns **FAIL**:
160
182
  - **{issues}**: the specific issues for this task group
161
183
  - **{task_content}**: the original task file for context
162
184
  4. After fix agents complete, re-run the review (Step 6)
185
+ 5. **Escalate on repeat failure.** If a task's issues survive a fix cycle, run its next fix one tier
186
+ higher (`light → standard → deep`). Note the escalation in the batch report. When a task needed
187
+ escalating, its tier was probably wrong, which is useful feedback for future specs.
163
188
 
164
189
  **Cap at 3 review cycles per batch.** If the third review still fails, stop and report to the user:
165
190
 
@@ -288,6 +288,11 @@ gitignores `specs/`, matching whatever this repo already does there). Use the st
288
288
  Every section should be specific and evidence-backed, or explicitly say "None found" /
289
289
  "Not applicable to this ticket" rather than being left looking like an oversight.
290
290
 
291
+ **Complexity Estimate** uses the same signals and tiers `spec-create` will apply per task: spread,
292
+ new design, data, concurrency, security, public contract, ambiguity, hard to verify. Security plus
293
+ data, or security plus concurrency, means `deep`. Say which model is worth planning this on, since a
294
+ deep ticket deserves the strongest model for `spec-create` too.
295
+
291
296
  **Recommended Implementation Direction** describes the technically-supported direction the
292
297
  evidence points to — reusing existing patterns the investigation actually found, not a novel
293
298
  architecture invented on the spot. It is not permission to start building; flag any business
@@ -48,6 +48,11 @@ Copy this structure into `specs/{feature-slug}/investigation.md`.
48
48
  ### Proposed Solutions Discussed
49
49
  ### Open Questions
50
50
  ### Conflicts / Discrepancies
51
+ ## Complexity Estimate
52
+ - Overall tier: (light / standard / deep): the tier of the hardest part of the work
53
+ - Signals: (which of spread, new design, data, concurrency, security, public contract, ambiguity, hard to verify apply, each with its evidence)
54
+ - Suggested model for planning this: (the `deep` model if the tier is deep; otherwise the session's model is fine)
55
+
51
56
  ## Recommended Implementation Direction
52
57
  ## References
53
58
  ```
@@ -69,7 +69,10 @@ Read, don't guess. Collect a draft of every field in `references/config-template
69
69
  steps (CI is often the most truthful source). Dev URL from the dev script, `.env.example`, or
70
70
  framework defaults.
71
71
 
72
- 6. **After approval.** Ask whether an approved spec should stop after each stage (`stop`), or
72
+ 6. **Models per tier.** Write the `models:` block from the template. Ask which models the user
73
+ has for each tool they installed, only if they want to tune it: the Claude defaults (haiku /
74
+ sonnet / opus) work as-is, and `default` is safe for the rest.
75
+ 7. **After approval.** Ask whether an approved spec should stop after each stage (`stop`), or
73
76
  run implement → verify → checkpoint in one go (`run-through`), stopping at any failure and never
74
77
  pushing. Store it as `pipeline.after_approval`.
75
78
 
@@ -42,6 +42,28 @@ commands:
42
42
  dev: "" # how spec-verify starts the app
43
43
  dev_url: "" # where it answers, e.g. http://localhost:3000
44
44
 
45
+ # Which model runs each task tier (spec-create rates every task light / standard / deep).
46
+ # Values are whatever your tool accepts; "default" means "whatever model the session is using".
47
+ # Hosts that can pick a model per agent (Claude Code) switch automatically; others (Cursor, Codex)
48
+ # tell you which model to switch to before a heavier batch.
49
+ models:
50
+ claude:
51
+ light: haiku
52
+ standard: sonnet
53
+ deep: opus
54
+ cursor:
55
+ light: default
56
+ standard: default
57
+ deep: default
58
+ codex:
59
+ light: default
60
+ standard: default
61
+ deep: default
62
+ antigravity:
63
+ light: default
64
+ standard: default
65
+ deep: default
66
+
45
67
  pipeline:
46
68
  # stop — every stage waits for you (default)
47
69
  # run-through — after you approve a spec: implement → verify → checkpoint in one go,