kataster 0.3.0 → 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 (59) hide show
  1. package/.claude/THIRD_PARTY_NOTICES.md +69 -0
  2. package/.claude/agents/acceptance-tester.md +7 -2
  3. package/.claude/agents/code-reviewer.md +7 -2
  4. package/.claude/agents/ux-designer.md +43 -38
  5. package/.claude/hooks/validate-docs.ts +2 -1
  6. package/.claude/lib/config.ts +1 -0
  7. package/.claude/lib/discovery.ts +129 -31
  8. package/.claude/lib/engineering.ts +12 -6
  9. package/.claude/package.json +1 -0
  10. package/.claude/settings.json +1 -2
  11. package/.claude/skills/adopt/SKILL.md +5 -3
  12. package/.claude/skills/backlog/SKILL.md +2 -2
  13. package/.claude/skills/bootstrap/SKILL.md +9 -7
  14. package/.claude/skills/domain-model/SKILL.md +1 -1
  15. package/.claude/skills/feature/SKILL.md +37 -11
  16. package/.claude/skills/feature/scripts/intake.ts +1 -1
  17. package/.claude/skills/groom-backlog/SKILL.md +19 -16
  18. package/.claude/skills/implement/SKILL.md +169 -63
  19. package/.claude/skills/implement/scripts/check-scenarios.ts +1 -1
  20. package/.claude/skills/implement/scripts/story-context.ts +1 -1
  21. package/.claude/skills/implement/scripts/verify.ts +1 -1
  22. package/.claude/skills/prototype/LOGIC.md +101 -0
  23. package/.claude/skills/prototype/SKILL.md +85 -0
  24. package/.claude/skills/prototype/UI.md +130 -0
  25. package/.claude/skills/review-stories/SKILL.md +39 -19
  26. package/.claude/skills/shape/SKILL.md +2 -2
  27. package/.claude/skills/user-stories/SKILL.md +109 -51
  28. package/.claude/skills/user-stories/schema/story.schema.json +6 -3
  29. package/.claude/skills/user-stories/scripts/lint-story.ts +56 -0
  30. package/.claude/skills/user-stories/templates/story-template.md +5 -2
  31. package/.claude/skills/ux-design/SKILL.md +30 -5
  32. package/.opencode/agents/acceptance-tester.md +8 -3
  33. package/.opencode/agents/code-reviewer.md +8 -3
  34. package/.opencode/agents/ux-designer.md +44 -39
  35. package/.opencode/commands/bootstrap.md +1 -1
  36. package/.opencode/commands/feature.md +1 -1
  37. package/.opencode/commands/groom-backlog.md +1 -1
  38. package/.opencode/commands/implement.md +1 -1
  39. package/.opencode/commands/review-stories.md +1 -1
  40. package/AGENTS.md +17 -0
  41. package/CHANGELOG.md +38 -0
  42. package/README.md +64 -7
  43. package/guide/brownfield.md +2 -2
  44. package/guide/concepts.md +26 -5
  45. package/guide/greenfield.md +4 -3
  46. package/guide/release.md +1 -1
  47. package/package.json +1 -1
  48. package/src/cli.mjs +42 -4
  49. package/src/install.mjs +62 -6
  50. package/templates/docs/product/ux-guidelines.md +2 -0
  51. package/templates/docs/prototypes/README.md +9 -0
  52. package/templates/docs/stories/README.md +10 -3
  53. package/templates/docs/workflow.md +38 -13
  54. package/.claude/agents/lead-dev.md +0 -50
  55. package/.claude/agents/product-owner.md +0 -39
  56. package/.claude/agents/requirements-engineer.md +0 -44
  57. package/.opencode/agents/lead-dev.md +0 -67
  58. package/.opencode/agents/product-owner.md +0 -59
  59. package/.opencode/agents/requirements-engineer.md +0 -58
@@ -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,43 @@
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
+
27
+ ## 0.3.1 – 2026-11-02
28
+
29
+ - **Optional prototyping, owned by the UX designer.** New `prototype` skill (adapted from Matt Pocock's, MIT) builds
30
+ a throwaway self-contained HTML prototype for one design question: several structurally different UI variants
31
+ (`ui`) or a clickable state model (`logic`). `/feature --prototype[=ui|logic]` – or a proposal from the UX frontier
32
+ during grilling – runs it between grilling and the stories; `/implement --prototype` settles an open UI question
33
+ before the test plan. The verdict goes into the story (`Prototype: …`) and, when it is a recurring rule, into
34
+ `docs/product/ux-guidelines.md` as a G-number. The prototype lives under `docs/prototypes/<slug>/`, is captured on
35
+ a throwaway branch `prototype/<slug>` and is never merged.
36
+ - **Credit where the workflow comes from.** `.claude/THIRD_PARTY_NOTICES.md` lists which skills are derived from
37
+ [Matt Pocock's skills](https://github.com/mattpocock/skills) – some adapted, some copied verbatim – and carries his
38
+ MIT license text. The file ships with the payload and lands in every installation; the README has a Credits
39
+ section.
40
+
3
41
  ## 0.3.0 – 2026-10-31
4
42
 
5
43
  - **Removed the optional extras and the vendored `shadcn` skill.** `--with` is gone; the CLI fails with a clear
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/
@@ -92,6 +92,8 @@ docs/
92
92
  workflow.md the map: sources of truth, commands, phases, rules
93
93
  product/vision.md the vision, grilled with you
94
94
  product/initiatives/ proposals for large ideas from /shape (no stories)
95
+ product/ux-guidelines.md the interface yardstick
96
+ prototypes/ throwaway prototypes on their own branch; the default branch keeps the README
95
97
  domain/events.yaml the event storming model (empty is valid)
96
98
  domain/context-map.md
97
99
  architecture/data-model.md
@@ -114,15 +116,39 @@ Existing `CONTEXT.md` and `docs/` are never touched.
114
116
  | `/grill-me` | Relentless interview on a plan, decision or idea |
115
117
  | `/grill-with-docs` | The same, writing glossary and ADRs as you go |
116
118
  | `/event-storming [process]` | Big-picture or single-process event storming |
117
- | `/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 |
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 |
118
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 |
119
- | `/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` |
120
122
  | `/backlog-sync [status\|pull\|push]` | Keep `docs/stories/` in sync with GitHub Issues or Jira |
121
123
  | `/groom-backlog [scope]` | Periodic challenge: is this backlog still worth building? |
122
- | `/implement [ST-NNN]` | Test-first implementation of one story, review, refactor, PR |
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 |
123
125
  | `/improve-codebase-architecture` | Scan for deepening opportunities, present them, grill one |
124
126
  | `/retro` | Retrospective on a session, proposing rule changes |
125
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
+
126
152
  ### Starting from a ticket
127
153
 
128
154
  `/feature` also accepts a reference: `/feature #123` (GitHub), `/feature PROJ-123` (Jira, with the issue fetched
@@ -149,11 +175,26 @@ The brief lands in `docs/product/initiatives/<slug>.md` (`proposed` until you ac
149
175
  existing monorepo is treated as a new context in an existing landscape, not as a greenfield project; a similar
150
176
  service is used as evidence and as the cost anchor, never as a template.
151
177
 
178
+ ### Prototyping an open question
179
+
180
+ When a UI, flow or state question cannot be settled on paper, `/feature --prototype` (or a proposal from the UX
181
+ lens during grilling) runs a throwaway prototype before the stories are written. The `ux-designer` builds one
182
+ self-contained HTML file under `docs/prototypes/<slug>/` – several structurally different UI variants or a
183
+ clickable state model. The verdict goes into the story and, when it is a recurring rule, into the UX guidelines;
184
+ the prototype is captured on a throwaway branch and never merged. `/implement --prototype` does the same for an
185
+ open UI question during engineering; if the prototype changes the story, the story goes back to `review`.
186
+
187
+ ```bash
188
+ /feature "checkout with saved cards" --prototype=ui
189
+ /feature PROJ-123 --prototype=logic
190
+ /implement ST-042 --prototype
191
+ ```
192
+
152
193
  ## Greenfield, end to end
153
194
 
154
195
  1. `npx kataster init --greenfield` → review `.claude/project.json`.
155
196
  2. `/bootstrap` → vision grilled, event storming, contexts/aggregates/data model, tech-stack ADR, UX guidelines.
156
- 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`.
157
198
  4. `/implement` → one story at a time, test-first, reviewed, merged by you.
158
199
  5. `/feature <idea>` for everything after that.
159
200
 
@@ -287,8 +328,9 @@ For Jira, `jira-plan.ts` prints the exact MCP calls, `jira-record.ts` writes the
287
328
  ```
288
329
  - **`verify.ts`** is the one gate: project checks, artifact validity, scenario ↔ test traceability, domain IDs in
289
330
  the code, glossary language.
290
- - **Review agents** never write code: `code-reviewer` and `acceptance-tester` report, the acceptance verdict decides
291
- 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.
292
334
 
293
335
  ## Upgrading
294
336
 
@@ -323,6 +365,21 @@ The toolkit is maintained in the open: bug reports, a skill that fills a real ga
323
365
  [SECURITY.md](SECURITY.md) is the way to report a vulnerability privately. Participation is covered by the
324
366
  [Code of Conduct](CODE_OF_CONDUCT.md).
325
367
 
368
+ ## Credits
369
+
370
+ Kataster stands on other people's work:
371
+
372
+ - **Matt Pocock** – [skills](https://github.com/mattpocock/skills), [aihero.dev](https://www.aihero.dev). The grilling
373
+ interview (`grilling`, `grill-me`, `grill-with-docs`), test-driven development (`tdd`), the deep-module vocabulary
374
+ (`codebase-design`), the architecture survey (`improve-codebase-architecture`), the throwaway prototype
375
+ (`prototype`), the retro, the domain-modeling discipline and `writing-for-agents` come from his skill repository –
376
+ partly adapted, partly copied verbatim under MIT. The per-file mapping and the license text are in
377
+ [.claude/THIRD_PARTY_NOTICES.md](.claude/THIRD_PARTY_NOTICES.md).
378
+ - **John Ousterhout**, *A Philosophy of Software Design* – "Design It Twice" and the depth, seam and deletion-test
379
+ vocabulary.
380
+ - **Eric Evans**, *Domain-Driven Design* – ubiquitous language, bounded contexts, aggregates.
381
+ - **Martin Fowler** – the smell baseline the review agents work from.
382
+
326
383
  ## License
327
384
 
328
385
  MIT
@@ -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,9 +26,10 @@ 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
+ | `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 |
32
33
 
33
34
  ## Events, commands, aggregates
34
35
 
@@ -45,15 +46,21 @@ The code carries the same IDs, which is what makes traceability checkable.
45
46
 
46
47
  ## Stories
47
48
 
48
- A story is a vertical slice of observable behaviour: `draft → review → ready → in-progress → done` (plus
49
- `superseded`).
49
+ A story is a vertical slice of observable behaviour: `draft → review → ready-for-human-review → ready →
50
+ in-progress → done` (plus `superseded`).
50
51
 
52
+ - **The linter is the gate.** `lint-story.ts` checks the spec deterministically. Green → `ready-for-human-review`;
53
+ the user approves `ready`.
51
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.
52
59
  - **A story that turns out wrong goes back to `review`** with an `[OPEN]` and a row in `OPEN_QUESTIONS.md` – never
53
60
  guessed into shape during implementation.
54
61
  - **`done` is final.** A change to finished behaviour is a new story.
55
- - **At most 10 scenarios.** Each is one red → green cycle. More means split.
56
- - **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.
57
64
 
58
65
  ### Stories from tickets
59
66
 
@@ -75,6 +82,20 @@ An initiative is **not** a work item: no `ready`/`done`, no stories, no second b
75
82
  with a basis and a confidence – an analogue service is evidence, not a template. Each accepted slice then goes
76
83
  through `/feature`, which creates the stories and runs the review.
77
84
 
85
+ ## Prototypes
86
+
87
+ A **prototype** is throwaway code that answers one design question. It is not a first draft: no tests, no
88
+ persistence, no production path. The question decides the branch – `ui` (what should it look like) or `logic`
89
+ (does the state model feel right). In discovery it is one self-contained HTML file under
90
+ `docs/prototypes/<slug>/`, built by the `ux-designer`; a question that needs the real application page is built
91
+ by the main session during `/implement`.
92
+
93
+ `/feature --prototype` (or a proposal from the UX frontier during grilling) adds the phase between grilling and
94
+ the stories. The verdict goes into the story (`Prototype: prototype/<slug> – <question> – <verdict>`) and, when
95
+ it is a recurring rule, into `ux-guidelines.md` as a G-number. The code is captured on a throwaway branch
96
+ `prototype/<slug>` and never merged. `/implement --prototype` does the same for an open UI question during
97
+ engineering; if it changes the story, the story goes back to `review`.
98
+
78
99
  ## The seam catalog
79
100
 
80
101
  A **seam** is where a test attaches. Testing everything everywhere is not affordable, so the project agrees up front
@@ -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/guide/release.md CHANGED
@@ -11,7 +11,7 @@
11
11
  npm install --prefix .claude --no-audit --no-fund
12
12
  npm run typecheck --prefix .claude
13
13
  npm test
14
- npm pack --dry-run # 147 files, 215 kB – no examples/, no node_modules, no state/
14
+ npm pack --dry-run # 148 files, 218 kB – no examples/, no node_modules, no state/
15
15
  ```
16
16
 
17
17
  4. Install the tarball once and start a throwaway project with it – a tarball that installs is not the same as a
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kataster",
3
- "version": "0.3.0",
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,6 +4,8 @@
4
4
  The yardstick, not the method. The method is `.claude/skills/ux-design/SKILL.md`.
5
5
  Every guideline is a rule someone can point at, with the date and the reason it was decided.
6
6
  `ux-designer` maintains this file; the user approves a guideline before it is written here.
7
+ A prototype verdict that becomes a recurring rule lands here as a G-number; the prototype itself stays on
8
+ its throwaway branch (`docs/prototypes/<slug>/`).
7
9
 
8
10
  Format:
9
11
  ## G1 – <short rule>
@@ -0,0 +1,9 @@
1
+ # Prototypes
2
+
3
+ Throwaway code that answers one design question. The method is `.claude/skills/prototype/SKILL.md`.
4
+
5
+ - A prototype lives in `docs/prototypes/<slug>/` on a throwaway branch `prototype/<slug>`; it is never merged.
6
+ - The default branch keeps this README only. The prototype itself is a primary source on its branch.
7
+ - The decision it settled goes into the story (`Prototype: prototype/<slug> – <question> – <verdict>` in
8
+ `## Context`) and, when it is a recurring rule, into `docs/product/ux-guidelines.md` as a G-number.
9
+ - One question, one self-contained HTML file, one timebox. No tests, no persistence, no production code.
@@ -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