kataster 0.3.1 → 0.4.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.
Files changed (53) hide show
  1. package/.claude/agents/acceptance-tester.md +7 -2
  2. package/.claude/agents/code-reviewer.md +7 -2
  3. package/.claude/agents/ux-designer.md +42 -47
  4. package/.claude/hooks/validate-docs.ts +2 -1
  5. package/.claude/lib/config.ts +1 -0
  6. package/.claude/lib/discovery.ts +129 -31
  7. package/.claude/lib/engineering.ts +12 -6
  8. package/.claude/package.json +1 -0
  9. package/.claude/settings.json +1 -2
  10. package/.claude/skills/adopt/SKILL.md +5 -3
  11. package/.claude/skills/backlog/SKILL.md +2 -2
  12. package/.claude/skills/bootstrap/SKILL.md +9 -7
  13. package/.claude/skills/domain-model/SKILL.md +1 -1
  14. package/.claude/skills/feature/SKILL.md +9 -9
  15. package/.claude/skills/feature/scripts/intake.ts +1 -1
  16. package/.claude/skills/groom-backlog/SKILL.md +19 -17
  17. package/.claude/skills/implement/SKILL.md +163 -78
  18. package/.claude/skills/implement/scripts/check-scenarios.ts +1 -1
  19. package/.claude/skills/implement/scripts/story-context.ts +1 -1
  20. package/.claude/skills/implement/scripts/verify.ts +1 -1
  21. package/.claude/skills/prototype/SKILL.md +1 -1
  22. package/.claude/skills/review-stories/SKILL.md +39 -19
  23. package/.claude/skills/shape/SKILL.md +2 -2
  24. package/.claude/skills/user-stories/SKILL.md +109 -51
  25. package/.claude/skills/user-stories/schema/story.schema.json +6 -3
  26. package/.claude/skills/user-stories/scripts/lint-story.ts +56 -0
  27. package/.claude/skills/user-stories/templates/story-template.md +5 -2
  28. package/.claude/skills/ux-design/SKILL.md +9 -7
  29. package/.opencode/agents/acceptance-tester.md +8 -3
  30. package/.opencode/agents/code-reviewer.md +8 -3
  31. package/.opencode/agents/ux-designer.md +42 -52
  32. package/.opencode/commands/bootstrap.md +1 -1
  33. package/.opencode/commands/feature.md +1 -1
  34. package/.opencode/commands/groom-backlog.md +1 -1
  35. package/.opencode/commands/implement.md +1 -1
  36. package/.opencode/commands/review-stories.md +1 -1
  37. package/AGENTS.md +17 -0
  38. package/CHANGELOG.md +24 -0
  39. package/README.md +31 -6
  40. package/guide/brownfield.md +2 -2
  41. package/guide/concepts.md +11 -5
  42. package/guide/greenfield.md +4 -3
  43. package/package.json +1 -1
  44. package/src/cli.mjs +42 -4
  45. package/src/install.mjs +62 -6
  46. package/templates/docs/stories/README.md +10 -3
  47. package/templates/docs/workflow.md +27 -11
  48. package/.claude/agents/lead-dev.md +0 -52
  49. package/.claude/agents/product-owner.md +0 -39
  50. package/.claude/agents/requirements-engineer.md +0 -44
  51. package/.opencode/agents/lead-dev.md +0 -69
  52. package/.opencode/agents/product-owner.md +0 -59
  53. package/.opencode/agents/requirements-engineer.md +0 -58
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Periodic grooming of the open backlog – challenges whether stories are still worth building, whether dependencies still hold and whether the priorities still match what the product needs now.
2
+ description: Periodic grooming of the open backlog – challenges whether stories are still worth building, whether dependencies still hold and whether the priorities still match what the product needs now. Runs in the main session; the UX utility is on demand only.
3
3
  ---
4
4
 
5
5
  Load the `groom-backlog` skill with the `skill` tool and follow it exactly. Do not restate it.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Engineering workflow – implements one story from docs/stories/ test-first on its own branch, verifies, has it reviewed by code-reviewer and acceptance-tester, refactors, and sets it done on the acceptance tester's verdict and hands the pull request to the user to merge; `--prototype` prototypes an open UI question first.
2
+ description: Engineering workflow – implements one story from docs/stories/ on its own branch, verifies, reviews scaled to the diff, refactors and hands the pull request to the user; `type: chore`/`task` (or `--fast`) takes the fast lane without Gherkin matching or formal reviews; `--prototype` settles an open UI question first.
3
3
  ---
4
4
 
5
5
  Load the `implement` skill with the `skill` tool and follow it exactly. Do not restate it.
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Has stories checked in one lead-dev pass (value, build, UX), applies the small fixes and guides the user to approval (status ready).
2
+ description: Runs the deterministic spec linter on finished stories, fixes mechanical gaps, moves green stories to ready-for-human-review and guides the user to approval (status ready). No subagent review pass.
3
3
  ---
4
4
 
5
5
  Load the `review-stories` skill with the `skill` tool and follow it exactly. Do not restate it.
package/AGENTS.md CHANGED
@@ -19,3 +19,20 @@ Rules:
19
19
  - A file the installer manages is tracked in `.claude/install.json`; project content (`CONTEXT.md`, `docs/`,
20
20
  `project.json`, `AGENTS.md` block) is not. Never make an upgrade overwrite user content.
21
21
  - Run `npm test` and `npm run typecheck --prefix .claude` before committing.
22
+
23
+ ## Workflow contract (0.4)
24
+
25
+ The workflow is spec-driven and mechanical, not a simulated organisation:
26
+
27
+ - **No product-owner, requirements-engineer or lead-dev subagents.** The main session writes stories as a spec
28
+ engineer with three lenses (domain, architecture, testability). Only the `ux-designer` remains on demand
29
+ (`ui: true` / prototyping), and it never gates.
30
+ - **1-command rule:** one `type: domain` story = one command + its events on one aggregate, at most 4 Gherkin
31
+ scenarios. `aggregate` and `command` are required frontmatter and are cross-checked against `docs/domain/events.yaml`.
32
+ - **Spec linter:** `.claude/skills/user-stories/scripts/lint-story.ts` (also `kataster lint-story`) is the gate.
33
+ Green → `ready-for-human-review`; the user approves `ready`. No LLM pre-implementation review.
34
+ - **Fast lane:** `type: chore` / `type: task`; `/implement` skips Gherkin matching and formal reviews.
35
+ - **Reviewers report, never plan:** `code-reviewer` and `acceptance-tester` create no stories. Findings are fixed in
36
+ the story or handed back as open questions.
37
+ - The installer prunes the retired workflow files named in `OBSOLETE` (only manifest-tracked, unmodified ones), so a
38
+ removed agent disappears on upgrade without touching optional extras or user files.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 – 2026-11-03
4
+
5
+ - **Spec-driven instead of corporate roleplay.** The `product-owner`, `requirements-engineer` and `lead-dev`
6
+ subagents are gone (Claude Code and OpenCode). The main session writes stories as a spec engineer with three
7
+ lenses – domain, architecture, testability – instead of handing them to simulated roles. The `ux-designer` stays
8
+ as an on-demand utility for `ui: true` stories and the prototyping phase; it never gates.
9
+ - **1-command rule.** One `type: domain` story = one command + its events on one aggregate, at most 4 Gherkin
10
+ scenarios. `aggregate` and `command` are required frontmatter and are cross-checked against
11
+ `docs/domain/events.yaml`; the events must be exactly what the command produces. `type: story` remains valid as a
12
+ legacy alias and is reported as a warning.
13
+ - **Deterministic spec linter.** `node .claude/skills/user-stories/scripts/lint-story.ts [ST-NNN|file]` and
14
+ `kataster lint-story` check frontmatter, required fields, referential integrity, `Given/When/Then`, the scenario
15
+ limit and unique titles – no LLM, no tokens. The same rules run in the PostToolUse hook, `story-status.ts` and
16
+ `verify.ts`. Green → `ready-for-human-review`; only the user approves `ready`.
17
+ - **Fast lane.** `type: chore` and `type: task` for UI tweaks, refactorings, dependencies and setup.
18
+ `/implement --fast` (or automatic detection) does branch → implement → lint/typecheck/unit tests → pull request,
19
+ without Gherkin matching or formal subagent reviews. `type: tech-task` and `type: spike` keep their behaviour.
20
+ - **Reviews report, they do not plan.** `code-reviewer` and `acceptance-tester` no longer create, split or revise
21
+ stories; a finding is fixed in the story or handed back as an open question. Review effort follows the diff, not a
22
+ T-shirt size. `size` is no longer a gate.
23
+ - **Installer prunes retired workflow files.** `upgrade` deletes the manifest-tracked files named in `OBSOLETE`
24
+ (the removed agents). Locally modified files are kept and reported; `--force` backs them up first. Optional
25
+ extras and user-added agents are never pruned.
26
+
3
27
  ## 0.3.1 – 2026-11-02
4
28
 
5
29
  - **Optional prototyping, owned by the UX designer.** New `prototype` skill (adapted from Matt Pocock's, MIT) builds
package/README.md CHANGED
@@ -78,7 +78,7 @@ npx kataster init . --yes --name "Acme" --ui-language de --artifacts en
78
78
  skills/ the workflow skills (grilling, event-storming, domain-model, architect, backlog, …)
79
79
  skills/adopt/scripts/ repository map and per-package docs for brownfield adoption
80
80
  skills/feature/scripts/ read-only intake: story, GitHub/Jira ticket or URL → route
81
- agents/ review agents for Claude Code
81
+ agents/ review agents and the on-demand UX utility (Claude Code)
82
82
  hooks/ plain-Node guardrails and validators
83
83
  lib/ deterministic domain/story/backlog logic
84
84
  .opencode/
@@ -118,13 +118,37 @@ Existing `CONTEXT.md` and `docs/` are never touched.
118
118
  | `/event-storming [process]` | Big-picture or single-process event storming |
119
119
  | `/feature <idea \| ST-NNN \| #123 \| PROJ-123>` | New feature from an idea, an existing story or a ticket: intake and routing, grilling, focused storming, model, stories, review; bugs become a regression story; `--prototype` adds a throwaway prototype before the stories |
120
120
  | `/shape <idea \| ticket> [--dry-run]` | Before a large feature: frame, place (existing/new context, shared library), impact, slices, rough cost range, recommendation; creates no stories |
121
- | `/review-stories [ST-…]` | One lead-dev pass (value, build, UX) before a story is `ready` |
121
+ | `/review-stories [ST-…]` | Spec linter gate → `ready-for-human-review`; the user approves `ready` |
122
122
  | `/backlog-sync [status\|pull\|push]` | Keep `docs/stories/` in sync with GitHub Issues or Jira |
123
123
  | `/groom-backlog [scope]` | Periodic challenge: is this backlog still worth building? |
124
- | `/implement [ST-NNN]` | Test-first implementation of one story, review, refactor, PR; `--prototype` prototypes an open UI question first |
124
+ | `/implement [ST-NNN] [--fast]` | One story on its branch: domain = test-first, diff-scaled review, acceptance; `chore`/`task` or `--fast` = fast lane without Gherkin matching or formal reviews; `--prototype` settles an open UI question first |
125
125
  | `/improve-codebase-architecture` | Scan for deepening opportunities, present them, grill one |
126
126
  | `/retro` | Retrospective on a session, proposing rule changes |
127
127
 
128
+ ### Spec-driven stories, fast lane
129
+
130
+ Stories are written by the main session as a **spec engineer**, not by simulated roles. Three lenses at once –
131
+ domain, architecture, testability – and one mechanical rule: **one story = one command + its events on one
132
+ aggregate**, at most four Gherkin scenarios. `aggregate` and `command` are required frontmatter and are cross-checked
133
+ against `docs/domain/events.yaml`.
134
+
135
+ The **spec linter** is the gate and costs no LLM tokens:
136
+
137
+ ```bash
138
+ kataster lint-story # all stories
139
+ kataster lint-story ST-012 # one story
140
+ node .claude/skills/user-stories/scripts/lint-story.ts docs/stories/ST-012-*.md
141
+ ```
142
+
143
+ Frontmatter and required fields, aggregate/command/event references, Gherkin `Given/When/Then`, the scenario limit
144
+ and unique titles. Green → `ready-for-human-review`; only you approve `ready`. The same rules run in the PostToolUse
145
+ hook and in `verify.ts`.
146
+
147
+ Non-domain work takes the **fast lane**: `type: chore` (UI tweak, refactor, dependency) or `type: task` (setup).
148
+ `/implement` detects it (or takes `--fast`) and does branch → implement → lint/typecheck/unit tests → PR, without
149
+ Gherkin matching or formal review agents. `type: tech-task` keeps its checklist and acceptance; `type: spike` is a
150
+ timeboxed question.
151
+
128
152
  ### Starting from a ticket
129
153
 
130
154
  `/feature` also accepts a reference: `/feature #123` (GitHub), `/feature PROJ-123` (Jira, with the issue fetched
@@ -170,7 +194,7 @@ open UI question during engineering; if the prototype changes the story, the sto
170
194
 
171
195
  1. `npx kataster init --greenfield` → review `.claude/project.json`.
172
196
  2. `/bootstrap` → vision grilled, event storming, contexts/aggregates/data model, tech-stack ADR, UX guidelines.
173
- 3. `/review-stories` → the first foundation tech tasks and the first vertical slice become `ready`.
197
+ 3. `/review-stories` → the spec linter turns finished stories into `ready-for-human-review`; you approve `ready`.
174
198
  4. `/implement` → one story at a time, test-first, reviewed, merged by you.
175
199
  5. `/feature <idea>` for everything after that.
176
200
 
@@ -304,8 +328,9 @@ For Jira, `jira-plan.ts` prints the exact MCP calls, `jira-record.ts` writes the
304
328
  ```
305
329
  - **`verify.ts`** is the one gate: project checks, artifact validity, scenario ↔ test traceability, domain IDs in
306
330
  the code, glossary language.
307
- - **Review agents** never write code: `code-reviewer` and `acceptance-tester` report, the acceptance verdict decides
308
- whether a story is `done`.
331
+ - **Review agents** never write code or stories: `code-reviewer` and `acceptance-tester` report findings, the
332
+ acceptance verdict decides whether a domain story is `done`, and a finding is fixed in the story or handed back
333
+ as an open question. `ux-designer` is an on-demand utility (`ui: true`), never a gate.
309
334
 
310
335
  ## Upgrading
311
336
 
@@ -103,8 +103,8 @@ that fails on existing behaviour – that would be a story.
103
103
 
104
104
  **The first adoption pass creates no stories.** It records the gap in `docs/domain/adoption-report.md` (and open
105
105
  questions in `OPEN_QUESTIONS.md`), so the model can be reviewed before anyone commits to work. Only
106
- `/adopt <scope> --stories` (or an explicit "turn these into stories") delegates the selected gaps to
107
- `requirements-engineer`.
106
+ `/adopt <scope> --stories` (or an explicit "turn these into stories") turns the selected gaps into stories in the
107
+ main session (spec engineer, spec linter gate).
108
108
 
109
109
  **Behaviour that already works does not become a story.** That turns the backlog into a description of the past.
110
110
 
package/guide/concepts.md CHANGED
@@ -26,7 +26,7 @@ Two rules make it work:
26
26
  | `docs/architecture/data-model.md` | Logical, stack-neutral model per aggregate; lifecycle and ER diagrams | domain-architect |
27
27
  | `docs/engineering/conventions.md` | How code is built here, including the seam catalog | user |
28
28
  | `docs/adr/` | Architecture decisions. `proposed` until the user accepts; accepted ones are superseded, never edited | user |
29
- | `docs/stories/` | The backlog. One Markdown file per story; `BACKLOG.md` is generated. With a tracker, this is the local mirror | requirements-engineer, PO, lead-dev; `/backlog-sync` with a tracker |
29
+ | `docs/stories/` | The backlog. One Markdown file per story; `BACKLOG.md` is generated. With a tracker, this is the local mirror | main session as spec engineer; spec linter; `/backlog-sync` with a tracker |
30
30
  | `docs/product/vision.md` | Problem, user groups, goals, non-goals, success criteria | user |
31
31
  | `docs/product/ux-guidelines.md` | The interface yardstick, as numbered rules with date and reason | ux-designer, approved by user |
32
32
  | `docs/prototypes/<slug>/` | Throwaway prototypes on their own branch; only the README stays on the default branch | ux-designer; verdict in the story and the guidelines |
@@ -46,15 +46,21 @@ The code carries the same IDs, which is what makes traceability checkable.
46
46
 
47
47
  ## Stories
48
48
 
49
- A story is a vertical slice of observable behaviour: `draft → review → ready → in-progress → done` (plus
50
- `superseded`).
49
+ A story is a vertical slice of observable behaviour: `draft → review → ready-for-human-review → ready →
50
+ in-progress → done` (plus `superseded`).
51
51
 
52
+ - **The linter is the gate.** `lint-story.ts` checks the spec deterministically. Green → `ready-for-human-review`;
53
+ the user approves `ready`.
52
54
  - **Discovery ends at `ready`.** Engineering moves `ready → in-progress → done`.
55
+ - **1-command rule.** One domain story = one command + its events on one aggregate, at most 4 scenarios. A second
56
+ command is a second story.
57
+ - **Fast lane.** `type: chore`/`task` for UI tweaks, refactors, dependencies and setup – no Gherkin matching, no
58
+ formal reviews.
53
59
  - **A story that turns out wrong goes back to `review`** with an `[OPEN]` and a row in `OPEN_QUESTIONS.md` – never
54
60
  guessed into shape during implementation.
55
61
  - **`done` is final.** A change to finished behaviour is a new story.
56
- - **At most 10 scenarios.** Each is one red → green cycle. More means split.
57
- - **A scenario title becomes a test title**: `ST-NNN: <scenario title>`. `verify.ts` checks the link.
62
+ - **A scenario title becomes a test title**: `ST-NNN: <scenario title>`. `verify.ts` checks the link for domain
63
+ stories.
58
64
 
59
65
  ### Stories from tickets
60
66
 
@@ -62,12 +62,13 @@ Two kinds of work, kept apart:
62
62
  - **The first vertical slice** – the smallest set of stories that delivers one real end-to-end path from the
63
63
  vision, each with Gherkin scenarios.
64
64
 
65
- At most 10 scenarios per story; no implementation plan inside a domain story; every story links its `events`.
65
+ One story = one command on one aggregate, at most 4 scenarios; no implementation plan inside a domain story; every domain story links `aggregate`, `command` and its `events`.
66
66
 
67
67
  ## Phase 6 – Review
68
68
 
69
- `/review-stories` – one lead-dev pass over value, buildability and UX. The user approves every `ready`. With a tracker,
70
- pull before the review and push the approved stories afterwards (`/backlog-sync`).
69
+ `/review-stories` – the spec linter checks frontmatter, aggregate/command/events, Gherkin and the scenario limit.
70
+ Green → `ready-for-human-review`; you approve every `ready`. With a tracker, pull before the review and push the
71
+ approved stories afterwards (`/backlog-sync`).
71
72
 
72
73
  ## Phase 7 – Build
73
74
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kataster",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "Agent workflow for domain-driven discovery and delivery – drop it into a greenfield or brownfield repository and build the domain by grilling.",
5
5
  "keywords": [
6
6
  "agents",
package/src/cli.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { existsSync, readFileSync } from "node:fs";
3
- import { basename, join, resolve } from "node:path";
3
+ import { basename, dirname, join, resolve } from "node:path";
4
4
  import { createInterface } from "node:readline/promises";
5
5
  import { stdin, stdout } from "node:process";
6
6
  import { Installer, VERSION } from "./install.mjs";
@@ -12,6 +12,8 @@ const HELP = `Kataster ${VERSION} – agent workflow for domain-driven discovery
12
12
  Usage:
13
13
  kataster init [dir] Install the workflow into a repository (default: .)
14
14
  kataster upgrade [dir] Update the workflow, keep project content and local changes
15
+ kataster lint-story [ST-NNN|file]
16
+ Deterministic spec linter for stories (no LLM); empty = all stories
15
17
  kataster doctor [dir] Check the installation
16
18
  kataster --version
17
19
  kataster --help
@@ -99,8 +101,8 @@ Useful commands afterwards:
99
101
  /grill-me /grill-with-docs sharpen a plan and write the docs as you go
100
102
  /event-storming explore a process or the whole domain
101
103
  /feature <idea> add a feature through grilling, storming, stories
102
- /review-stories one lead-dev pass before a story is ready
103
- /implement [ST-NNN] test-first implementation of one story
104
+ /review-stories linter gate + human approval before a story is ready
105
+ /implement [ST-NNN] test-first implementation of one story (--fast for chore/task)
104
106
  /backlog-sync pull/push the backlog mirror (Jira or GitHub Issues)
105
107
  /retro retrospective on a session
106
108
  `);
@@ -219,6 +221,36 @@ async function upgrade(options) {
219
221
  console.log("\nUpgrade complete. Project content (CONTEXT.md, docs/domain, docs/stories, ADRs) was not touched.");
220
222
  }
221
223
 
224
+ /** Nearest directory that has the story linter installed (the repository of the given file, or an ancestor). */
225
+ function findInstallRoot(start) {
226
+ let dir = resolve(start);
227
+ if (dir.endsWith(".md")) dir = dirname(dir);
228
+ for (;;) {
229
+ if (existsSync(join(dir, ".claude", "skills", "user-stories", "scripts", "lint-story.ts"))) return dir;
230
+ const parent = dirname(dir);
231
+ if (parent === dir) return null;
232
+ dir = parent;
233
+ }
234
+ }
235
+
236
+ /**
237
+ * `kataster lint-story [ST-NNN|file]` – delegates to the installed spec linter. Deterministic: the same checks
238
+ * the hook, `/review-stories` and `verify.ts` run, without an LLM.
239
+ */
240
+ function lintStory(options) {
241
+ const arg = options._[1];
242
+ const root = findInstallRoot(arg && arg.endsWith(".md") ? arg : (arg && /^ST-/i.test(arg) ? process.cwd() : (arg || process.cwd())));
243
+ if (!root) {
244
+ throw new Error("No Kataster installation found (missing .claude/skills/user-stories/scripts/lint-story.ts) – run `kataster init` first.");
245
+ }
246
+ const script = join(root, ".claude", "skills", "user-stories", "scripts", "lint-story.ts");
247
+ const args = [script];
248
+ if (arg) args.push(arg.endsWith(".md") ? resolve(arg) : arg);
249
+ if (options.json) args.push("--json");
250
+ const r = spawnSync(process.execPath, args, { cwd: root, stdio: "inherit" });
251
+ process.exitCode = r.status ?? 1;
252
+ }
253
+
222
254
  function doctor(options) {
223
255
  const root = resolve(options._[1] ?? ".");
224
256
  const checks = [];
@@ -230,6 +262,11 @@ function doctor(options) {
230
262
  add("Install manifest", Boolean(manifest), manifest ? `version ${manifest.version}` : "missing – run init");
231
263
  const leftovers = existsSync(join(root, ".laguardia")) || existsSync(join(root, ".opencode", "plugins", "laguardia"));
232
264
  add("No leftovers from the old name", !leftovers, leftovers ? "run upgrade to migrate them" : "");
265
+ const retired = [
266
+ ".claude/agents/product-owner.md", ".claude/agents/requirements-engineer.md", ".claude/agents/lead-dev.md",
267
+ ".opencode/agents/product-owner.md", ".opencode/agents/requirements-engineer.md", ".opencode/agents/lead-dev.md",
268
+ ].filter((file) => existsSync(join(root, file)));
269
+ add("No retired role-play agents", retired.length === 0, retired.join(", ") || "clean");
233
270
  const profile = readJson(join(root, ".claude", "project.json"));
234
271
  add("Project profile", Boolean(profile?.name), profile?.name ?? "missing");
235
272
 
@@ -238,7 +275,7 @@ function doctor(options) {
238
275
  add("Backlog provider", ["local", "github", "jira"].includes(provider), provider);
239
276
  if (provider === "jira") {
240
277
  add("Backlog: Jira project", Boolean(backlog.jira?.project), backlog.jira?.project || "backlog.jira.project is missing");
241
- const statuses = ["draft", "review", "ready", "in-progress", "done", "superseded"];
278
+ const statuses = ["draft", "review", "ready-for-human-review", "ready", "in-progress", "done", "superseded"];
242
279
  const missingStatuses = statuses.filter((status) => !backlog.jira?.statusMap?.[status]);
243
280
  add("Backlog: Jira status map", missingStatuses.length === 0, missingStatuses.join(", ") || "all statuses mapped");
244
281
  const priorities = ["must", "should", "could", "wont"];
@@ -296,6 +333,7 @@ export async function main(argv) {
296
333
  const command = options._[0];
297
334
  if (command === "init") return init(options);
298
335
  if (command === "upgrade") return upgrade(options);
336
+ if (command === "lint-story") return lintStory(options);
299
337
  if (command === "doctor") return doctor(options);
300
338
  throw new Error(`Unknown command '${command}'. Try --help.`);
301
339
  }
package/src/install.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { spawnSync } from "node:child_process";
2
- import { existsSync, mkdirSync, readdirSync, renameSync } from "node:fs";
2
+ import { existsSync, mkdirSync, readdirSync, renameSync, rmSync } from "node:fs";
3
3
  import { dirname, join, relative, resolve } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import {
@@ -58,8 +58,7 @@ const HOOK_EVENTS = {
58
58
  const ALLOW = [
59
59
  "Bash(node .claude/skills/event-storming/scripts/validate-events.ts)",
60
60
  "Bash(node .claude/skills/event-storming/scripts/render-event-storming.ts)",
61
- "Bash(node .claude/skills/user-stories/scripts/validate-stories.ts)",
62
- "Bash(node .claude/skills/user-stories/scripts/render-backlog.ts)",
61
+ "Bash(node .claude/skills/user-stories/scripts/*)",
63
62
  "Bash(node .claude/skills/adopt/scripts/*)",
64
63
  "Bash(node .claude/skills/backlog/scripts/*)",
65
64
  "Bash(node .claude/skills/implement/scripts/*)",
@@ -70,6 +69,21 @@ const ALLOW = [
70
69
  "Bash(git log *)",
71
70
  ];
72
71
 
72
+ /**
73
+ * Files earlier versions shipped and 0.4 removed. `upgrade` deletes them from an existing installation – but only
74
+ * when the manifest tracks them and the local copy is unmodified, so a project that customised one keeps it.
75
+ * Add a path here whenever a workflow file is retired; never prune by "not in the payload", because files from
76
+ * optional extras and user-added agents are not the installer's to delete.
77
+ */
78
+ const OBSOLETE = [
79
+ ".claude/agents/product-owner.md",
80
+ ".claude/agents/requirements-engineer.md",
81
+ ".claude/agents/lead-dev.md",
82
+ ".opencode/agents/product-owner.md",
83
+ ".opencode/agents/requirements-engineer.md",
84
+ ".opencode/agents/lead-dev.md",
85
+ ];
86
+
73
87
  export class Installer {
74
88
  constructor(root, { dryRun = false, force = false, log = () => {} } = {}) {
75
89
  this.root = resolve(root);
@@ -165,17 +179,52 @@ export class Installer {
165
179
  }
166
180
 
167
181
  copyWorkflowDirs() {
182
+ const present = new Set();
168
183
  for (const dir of WORKFLOW_DIRS) {
169
184
  const source = join(PKG, dir);
170
185
  if (!existsSync(source)) continue;
171
186
  for (const file of walk(source)) {
172
187
  const name = toPosix(relative(source, file));
173
188
  if (name.split("/").some((part) => SKIP.has(part))) continue;
174
- const content = read(file);
189
+ const key = relPosix(this.root, join(this.root, dir, name));
190
+ present.add(key);
175
191
  // settings.json and opencode.jsonc are merged, not replaced
176
192
  if (file === join(source, "settings.json") || file === join(source, "opencode.jsonc")) continue;
177
- this.put(join(this.root, dir, name), content);
193
+ this.put(join(this.root, dir, name), read(file));
194
+ }
195
+ }
196
+ this.pruneObsolete(present);
197
+ }
198
+
199
+ /**
200
+ * Removes the retired workflow files listed in OBSOLETE (e.g. the deleted corporate-roleplay agents).
201
+ *
202
+ * Only files recorded in the manifest are candidates, so project content and files from other tools are never
203
+ * touched. A file the user edited is kept and reported as a conflict – unless `--force` is given, which backs it
204
+ * up first. This is what makes a removed agent actually disappear from an existing installation on upgrade.
205
+ */
206
+ pruneObsolete(present) {
207
+ for (const key of OBSOLETE) {
208
+ if (present.has(key)) continue;
209
+ if (!(key in (this.manifest?.files ?? {}))) continue;
210
+
211
+ const path = join(this.root, key);
212
+ if (!existsSync(path)) {
213
+ delete this.files[key];
214
+ continue;
215
+ }
216
+
217
+ const known = this.manifest.files[key];
218
+ const local = sha(read(path));
219
+ if (local !== known && !this.force) {
220
+ this.conflicts.push(key);
221
+ this.record("conflict", path, "retired, but locally modified – kept");
222
+ continue;
178
223
  }
224
+ if (this.force) this.backup(path);
225
+ if (!this.dryRun) rmSync(path, { force: true });
226
+ delete this.files[key];
227
+ this.record("remove", path, "retired workflow file");
179
228
  }
180
229
  }
181
230
 
@@ -391,9 +440,16 @@ This repository runs the Kataster workflow. Discovery lives in \`.claude/skills/
391
440
  - **Project checks:** \`docs/engineering/checks.yaml\` – what runs at edit time and Stop/CI; architecture boundaries
392
441
  in \`docs/architecture/boundaries.yaml\` with existing debt in the baseline (only new violations fail).
393
442
  - **Language:** domain terms exactly as in \`CONTEXT.md\`; UI words from its \`_UI (<lang>)_\` lines.
394
- - **Traceability:** every scenario has a test titled \`ST-NNN: <scenario title>\`; domain IDs come from
443
+ - **1-command rule:** one story = one command + its events on one aggregate, at most 4 Gherkin scenarios. Full
444
+ domain stories are \`type: domain\`; UI tweaks, refactors, dependencies and setup use the fast lane
445
+ (\`type: chore\` or \`type: task\`).
446
+ - **Spec gate:** \`node .claude/skills/user-stories/scripts/lint-story.ts <file>\` must be green. A story the linter
447
+ accepts becomes \`ready-for-human-review\`; only the user approves \`ready\`.
448
+ - **Traceability:** every domain scenario has a test titled \`ST-NNN: <scenario title>\`; domain IDs come from
395
449
  \`docs/domain/events.yaml\`.
396
450
  - **ADRs:** new ones are \`proposed\`; only the user sets \`accepted\`. A \`done\` story is never reopened.
451
+ - **Reviews:** \`/implement\` scales review to the diff. \`code-reviewer\` and \`acceptance-tester\` report findings and
452
+ never create stories; fast-lane stories skip the formal review pass.
397
453
  - **Gate:** \`node .claude/skills/implement/scripts/verify.ts\`.
398
454
 
399
455
  Keep this block short: it is loaded into every session. Put rules into the skill that owns them, not here.
@@ -4,9 +4,16 @@ One Markdown file per story: `ST-NNN-short-title.md`. `BACKLOG.md` is generated
4
4
  Rules, format and splitting: `.claude/skills/user-stories/SKILL.md`.
5
5
  Template: `.claude/skills/user-stories/templates/story-template.md`.
6
6
 
7
- Workflow: `draft → review → ready → in-progress → done` (plus `superseded`).
8
-
9
- - Discovery ends at `ready`; `/implement` moves a story to `in-progress` and `done`.
7
+ Workflow: `draft → review → ready-for-human-review → ready → in-progress → done` (plus `superseded`).
8
+
9
+ - The **spec linter** is the gate: `node .claude/skills/user-stories/scripts/lint-story.ts <file>` (or
10
+ `kataster lint-story`). It checks frontmatter, `aggregate`/`command`/events against `docs/domain/events.yaml`,
11
+ Gherkin `Given/When/Then` and the scenario limit – deterministically, no LLM.
12
+ - Green → `ready-for-human-review`; **the user approves `ready`**. `/implement` only picks up `ready`.
13
+ - **1-command rule:** one `type: domain` story = one command + its events on one aggregate, at most 4 scenarios.
14
+ - **Fast lane:** `type: chore` or `type: task` for UI tweaks, refactorings, dependencies and setup – no Gherkin
15
+ matching, no formal reviews. `type: tech-task` keeps its checklist; `type: spike` is a timeboxed question.
16
+ - `ui: true` marks a story with a frontend part; it triggers the on-demand UX utility, never a gate.
10
17
  - A story that turns out wrong goes back to `review` with an `[OPEN]` and a row in `OPEN_QUESTIONS.md`.
11
18
  - `done` is final: a change to finished behaviour is a new story.
12
19
 
@@ -14,7 +14,7 @@ is what to read when you are unsure where something belongs.
14
14
 
15
15
  | Artifact | File | Owner |
16
16
  |---|---|---|
17
- | Vision, user groups, scope | `docs/product/vision.md` | user + `product-owner` |
17
+ | Vision, user groups, scope | `docs/product/vision.md` | user + main session |
18
18
  | Initiative briefs (proposals, no stories) | `docs/product/initiatives/<slug>.md` | `/shape`; user accepts |
19
19
  | UI/UX decisions (yardstick) | `docs/product/ux-guidelines.md` | `ux-designer` (user approves) |
20
20
  | Prototypes (throwaway, on branch `prototype/<slug>`) | `docs/prototypes/<slug>/` | `ux-designer`; verdict in the story and `ux-guidelines.md` |
@@ -30,12 +30,12 @@ is what to read when you are unsure where something belongs.
30
30
  | Existing boundary debt (generated) | `docs/architecture/boundaries.baseline.json` | `boundary-check.ts --update-baseline` |
31
31
  | Project checks (what runs when) | `docs/engineering/checks.yaml` | `/adopt`, user approves |
32
32
  | Architecture decisions (ADRs) | `docs/adr/` | `architect` skill / `domain-architect` (user accepts) |
33
- | Stories | `docs/stories/ST-NNN-*.md` | `requirements-engineer`; status by `/implement`; with a tracker this is the mirror |
33
+ | Stories | `docs/stories/ST-NNN-*.md` | main session as spec engineer; linter gate; status by `/implement`; with a tracker this is the mirror |
34
34
  | Backlog backend | `.claude/project.json` → `backlog.provider` – `local`, `github` or `jira` | user |
35
35
  | Backlog mirror (with a tracker) | `docs/stories/` | `/backlog-sync` |
36
36
  | Backlog (generated!) | `docs/stories/BACKLOG.md` | hook – never edit manually |
37
37
  | Open questions | `docs/stories/OPEN_QUESTIONS.md` | everyone |
38
- | Reviews | `docs/reviews/` | `review-stories`, `acceptance-tester`, `code-reviewer` |
38
+ | Reviews | `docs/reviews/` | spec linter and `acceptance-tester`/`code-reviewer` (domain lane only; findings, no stories) |
39
39
  | Engineering conventions, seam catalog | `docs/engineering/conventions.md` | written after the foundation, user approves |
40
40
  | Application code and tests | `repo.codeRoots` in `.claude/project.json` | `/implement` |
41
41
 
@@ -51,12 +51,12 @@ is what to read when you are unsure where something belongs.
51
51
  | `grilling` (`/grill-me`) | relentless interview in rounds with recommended answers – how we ask questions |
52
52
  | `/grill-with-docs` | grilling + `domain-model` + `architect`: writes glossary and ADRs as it goes |
53
53
  | `/event-storming [process]`, `/feature <idea \| ST-NNN \| ticket>` | discovery entry points; both start by grilling. `/feature` resolves a story or ticket read-only and routes bugs to a regression story; `--prototype` adds a throwaway prototype before the stories |
54
- | `user-stories`, `/review-stories` | story format, validation, review |
54
+ | `user-stories`, `/review-stories` | story format, 1-command rule, spec linter, human approval |
55
55
  | `backlog`, `/backlog-sync` | local / GitHub Issues / Jira mirror, sync and conflict rules |
56
56
  | `ux-design` | how we decide the interface: the UX frontier, what a story says about a screen, review order |
57
57
  | `prototype` | throwaway HTML prototype for a UI or logic question; the verdict goes into the story and the guidelines |
58
58
  | `/groom-backlog` | periodic challenge of the open backlog |
59
- | `/implement [ST-NNN]` | one story test-first, verify, review, refactor, acceptance, pull request; `--prototype` prototypes an open UI question first |
59
+ | `/implement [ST-NNN] [--fast]` | one story on its branch: domain = test-first, diff-scaled review, acceptance; chore/task or `--fast` = fast lane without Gherkin matching or formal reviews; `--prototype` settles an open UI question first |
60
60
  | `tdd` | red → green loop, good tests, seams |
61
61
  | `codebase-design` | vocabulary for deep modules |
62
62
  | `/improve-codebase-architecture` | periodic scan for deepening opportunities, then grilling |
@@ -79,8 +79,22 @@ is what to read when you are unsure where something belongs.
79
79
  `backlog.provider` = `jira` or `github` in `.claude/project.json`, the tracker is the source of truth and
80
80
  `docs/stories/` is its local mirror; sync through `/backlog-sync` and never edit `BACKLOG.md` by hand. Reviews and
81
81
  open questions stay local. `ST-NNN` stays the stable id; the tracker key lives in `external`.
82
- - **Traceability in the code**: every Gherkin scenario has a test titled exactly `ST-NNN: <scenario title>`;
83
- commands, events, read models and policies carry their ID from `events.yaml`. `verify.ts` checks both.
82
+ - **1-command rule.** One story = one command + its events on one aggregate, at most 4 Gherkin scenarios. A second
83
+ command is a second story, never a second scenario set. `type: domain` requires `aggregate` and `command` and the
84
+ linter cross-checks them against `events.yaml`.
85
+ - **Fast lane.** `type: chore` and `type: task` cover UI tweaks, refactorings, dependencies and setup. `/implement`
86
+ takes the fast lane automatically (or with `--fast`): branch → implement → lint/typecheck/tests → pull request, no
87
+ Gherkin matching and no formal review pass. `type: tech-task` keeps its checklist and acceptance; `type: spike` is
88
+ a timeboxed question.
89
+ - **The spec linter is the gate.** `node .claude/skills/user-stories/scripts/lint-story.ts <file>` (or
90
+ `kataster lint-story`) checks frontmatter, required fields, aggregate/command/event references, Gherkin and the
91
+ scenario limit – deterministically, without an LLM. Green → `ready-for-human-review`; the user approves `ready`.
92
+ - **Traceability in the code**: every Gherkin scenario of a `domain` story has a test titled exactly
93
+ `ST-NNN: <scenario title>`; commands, events, read models and policies carry their ID from `events.yaml`.
94
+ `verify.ts` checks both. Chore/task stories are covered by their checklist and unit tests.
95
+ - **Reviews report, they do not plan.** `code-reviewer` and `acceptance-tester` find problems; they never create,
96
+ split or revise stories. A finding is fixed in the story or becomes an open question to the developer. Review
97
+ effort follows the diff (light lane for texts/layout, normal lane otherwise).
84
98
  - **Status changes** go through `node .claude/skills/implement/scripts/story-status.ts`. A `done` story is never
85
99
  reopened – changes become a new story.
86
100
  - **Never weaken or delete a test to get green.** One branch and one pull request per story; the user merges.
@@ -120,10 +134,12 @@ larger than one feature goes through `/shape` first and then `/feature` per slic
120
134
  question cannot be settled on paper, `/feature --prototype` adds a throwaway prototype between grilling and the
121
135
  stories; the verdict goes into the story and the UX guidelines.
122
136
 
123
- Engineering, story by story: `/implement [ST-NNN]` (empty = next ready story whose dependencies are done):
124
- prepare → test plan → red/green per scenario → `verify.ts` → review lane scaled to risk → refactor → acceptance
125
- tester's verdict → `done` + pull request; **the user only merges**. `--prototype` settles an open UI question
126
- first; if it changes the story, the story goes back to `review`. Start the next story in a fresh session.
137
+ Engineering, story by story: `/implement [ST-NNN]` (empty = next ready story whose dependencies are done). Domain
138
+ lane: prepare → test plan → red/green per scenario → `verify.ts` → review lane scaled to the diff → refactor →
139
+ acceptance tester's verdict → `done` + pull request. Fast lane (`chore`/`task`, or `--fast`): prepare → implement →
140
+ lint/typecheck/tests → `verify.ts` → pull request. **The user only merges.** `--prototype` settles an open UI
141
+ question first; if it changes the story, the story goes back to `review`, through the linter and the user again.
142
+ Start the next story in a fresh session.
127
143
 
128
144
  With a tracker: pull before `/review-stories`, `/groom-backlog` and `/implement`; push after changing or approving
129
145
  stories and after `done`. The tracker leads on conflict; `docs/stories/` is the mirror and `BACKLOG.md` stays
@@ -1,52 +0,0 @@
1
- ---
2
- name: lead-dev
3
- description: Lead developer. Story review in one pass – value (PO lens), testability, slicing, effort (T-shirt), risks, dependencies (dev lens) and the UX guidelines (UX lens). Use before setting status ready.
4
- tools: Read, Glob, Grep, Write, Edit
5
- model: sonnet
6
- color: green
7
- skills:
8
- - user-stories
9
- hooks:
10
- PreToolUse:
11
- - matcher: "Write|Edit"
12
- hooks:
13
- - type: command
14
- command: 'node "$CLAUDE_PROJECT_DIR/.claude/hooks/path-guard.ts" docs/stories/ docs/reviews/'
15
- ---
16
-
17
- You are the lead developer of this project and assess stories from the perspective of the later engineering workflow.
18
- You have NO context from the main conversation. Write in the artifact language (`languages.artifacts` in `.claude/project.json`, default English).
19
-
20
- ## Sources
21
- Stories in `docs/stories/`, `docs/domain/events.yaml`, `docs/architecture/data-model.md`, ADRs in `docs/adr/`,
22
- glossary in `CONTEXT.md`. For a story review also `docs/product/vision.md` and, for stories labelled `ui`,
23
- `docs/product/ux-guidelines.md`.
24
-
25
- ## Review Criteria per Story
26
- 1. **Testability**: Can every scenario be translated 1:1 into an automated test? Unambiguous outcomes, no soft wording ("fast", "user-friendly")?
27
- 2. **Slicing**: Vertical (through all layers, delivers value)? Larger than M, or more scenarios than the validator allows → make a concrete splitting proposal.
28
- 3. **Effort**: `size` as a T-shirt size (XS–XL) relative to the other stories.
29
- 4. **Risk**: `risk` (low/medium/high) – unknown technology, integrations, data migration, offline/mobile.
30
- 5. **Dependencies**: add `depends_on` when a story requires another one, functionally or technically.
31
- 6. **Gaps**: name missing spikes (uncertainty) or tech tasks (e.g. scaffold, auth, CI) as proposals.
32
-
33
- In a **story review** (task from `/review-stories`) add two lenses:
34
- 7. **Value (PO lens)**: a real user role, fits the vision, touches no non-goal. Set `priority` (`must | should | could | wont`).
35
- 8. **UX lens** (label `ui` only): the screen's job and entry point are named; the empty, rejected and
36
- no-permission cases have a scenario; UI wording matches the glossary's `_UI (<lang>)_`. A pattern the
37
- guidelines already define is referenced by its G-number, not restated as scenarios – name restated ones.
38
- A screen whose structural shape is still open is not `ready`: recommend a prototype (`/feature --prototype`
39
- or a spike) instead of approving it.
40
-
41
- ## Boundaries
42
- - In stories you only change `size`, `risk`, `depends_on` and – in a story review – `priority`. Return everything else as a recommendation.
43
- - Don't write implementation details into stories (framework, tables) – stories stay domain-level.
44
- - Don't make stack decisions – phrase concerns as input for an ADR.
45
- - Never edit `docs/stories/BACKLOG.md`; it is generated.
46
- - With a tracker, the main session pushes your field changes; never set `external` by hand.
47
-
48
- ## Output (reply to the main session)
49
- | Story | Testable | size | risk | priority | Split/recommendation |
50
- Below: findings per story as concrete edits (the scenario, Context line or G-reference to add, change or drop),
51
- proposed spikes/tech tasks, technical risks for ADRs, at most 3 questions for the user with a recommended answer.
52
- In a story review, write the same into the review file you were given.