kataster 0.0.0-stage → 0.2.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 (162) hide show
  1. package/.claude/agents/acceptance-tester.md +53 -0
  2. package/.claude/agents/code-reviewer.md +56 -0
  3. package/.claude/agents/domain-architect.md +40 -0
  4. package/.claude/agents/lead-dev.md +50 -0
  5. package/.claude/agents/product-owner.md +39 -0
  6. package/.claude/agents/repo-analyst.md +78 -0
  7. package/.claude/agents/requirements-engineer.md +44 -0
  8. package/.claude/agents/ux-designer.md +64 -0
  9. package/.claude/hooks/bash-guard.ts +92 -0
  10. package/.claude/hooks/code-check.ts +37 -0
  11. package/.claude/hooks/path-guard.ts +34 -0
  12. package/.claude/hooks/path-rules.ts +66 -0
  13. package/.claude/hooks/project-check.ts +108 -0
  14. package/.claude/hooks/protect-files.ts +55 -0
  15. package/.claude/hooks/session-start.ts +213 -0
  16. package/.claude/hooks/stop-gate.ts +56 -0
  17. package/.claude/hooks/validate-docs.ts +58 -0
  18. package/.claude/lib/architecture.ts +736 -0
  19. package/.claude/lib/backlog-github.ts +454 -0
  20. package/.claude/lib/backlog-jira.ts +347 -0
  21. package/.claude/lib/backlog-sync.ts +42 -0
  22. package/.claude/lib/backlog.ts +396 -0
  23. package/.claude/lib/bazel.ts +212 -0
  24. package/.claude/lib/config.ts +122 -0
  25. package/.claude/lib/discovery.ts +821 -0
  26. package/.claude/lib/engineering.ts +406 -0
  27. package/.claude/lib/nx.ts +129 -0
  28. package/.claude/lib/repo.ts +1194 -0
  29. package/.claude/lib/toolgraph.ts +74 -0
  30. package/.claude/lib/turbo.ts +166 -0
  31. package/.claude/package-lock.json +427 -0
  32. package/.claude/package.json +21 -0
  33. package/.claude/settings.json +82 -0
  34. package/.claude/skills/adopt/SKILL.md +283 -0
  35. package/.claude/skills/adopt/scripts/boundary-check.ts +156 -0
  36. package/.claude/skills/adopt/scripts/package-doc.ts +71 -0
  37. package/.claude/skills/adopt/scripts/repo-map.ts +123 -0
  38. package/.claude/skills/adopt/scripts/rule-doc.ts +80 -0
  39. package/.claude/skills/architect/ADR-FORMAT.md +97 -0
  40. package/.claude/skills/architect/SKILL.md +64 -0
  41. package/.claude/skills/architect/TECH-STACK.md +21 -0
  42. package/.claude/skills/backlog/SKILL.md +198 -0
  43. package/.claude/skills/backlog/scripts/backlog-pull.ts +45 -0
  44. package/.claude/skills/backlog/scripts/backlog-push.ts +38 -0
  45. package/.claude/skills/backlog/scripts/backlog-status.ts +32 -0
  46. package/.claude/skills/backlog/scripts/jira-import.ts +40 -0
  47. package/.claude/skills/backlog/scripts/jira-plan.ts +26 -0
  48. package/.claude/skills/backlog/scripts/jira-record.ts +41 -0
  49. package/.claude/skills/bootstrap/SKILL.md +119 -0
  50. package/.claude/skills/codebase-design/DEEPENING.md +37 -0
  51. package/.claude/skills/codebase-design/DESIGN-IT-TWICE.md +44 -0
  52. package/.claude/skills/codebase-design/SKILL.md +114 -0
  53. package/.claude/skills/domain-model/CONTEXT-FORMAT.md +47 -0
  54. package/.claude/skills/domain-model/SKILL.md +64 -0
  55. package/.claude/skills/domain-model/templates/data-model.md +29 -0
  56. package/.claude/skills/engineering-conventions/SKILL.md +31 -0
  57. package/.claude/skills/event-storming/SKILL.md +44 -0
  58. package/.claude/skills/event-storming/schema/events.schema.json +124 -0
  59. package/.claude/skills/event-storming/scripts/render-event-storming.ts +11 -0
  60. package/.claude/skills/event-storming/scripts/validate-events.ts +11 -0
  61. package/.claude/skills/event-storming/templates/events.example.yaml +91 -0
  62. package/.claude/skills/feature/SKILL.md +86 -0
  63. package/.claude/skills/feature/scripts/intake.ts +356 -0
  64. package/.claude/skills/grill-me/SKILL.md +7 -0
  65. package/.claude/skills/grill-with-docs/SKILL.md +7 -0
  66. package/.claude/skills/grilling/SKILL.md +30 -0
  67. package/.claude/skills/groom-backlog/SKILL.md +93 -0
  68. package/.claude/skills/groom-backlog/scripts/backlog-health.ts +238 -0
  69. package/.claude/skills/implement/SKILL.md +129 -0
  70. package/.claude/skills/implement/scripts/affected.ts +245 -0
  71. package/.claude/skills/implement/scripts/check-commands.ts +37 -0
  72. package/.claude/skills/implement/scripts/check-language.ts +15 -0
  73. package/.claude/skills/implement/scripts/check-scenarios.ts +59 -0
  74. package/.claude/skills/implement/scripts/next-story.ts +23 -0
  75. package/.claude/skills/implement/scripts/story-context.ts +135 -0
  76. package/.claude/skills/implement/scripts/story-status.ts +47 -0
  77. package/.claude/skills/implement/scripts/verify.ts +92 -0
  78. package/.claude/skills/improve-codebase-architecture/HTML-REPORT.md +123 -0
  79. package/.claude/skills/improve-codebase-architecture/SKILL.md +71 -0
  80. package/.claude/skills/retro/SKILL.md +53 -0
  81. package/.claude/skills/review-stories/SKILL.md +31 -0
  82. package/.claude/skills/shape/SKILL.md +193 -0
  83. package/.claude/skills/tdd/SKILL.md +38 -0
  84. package/.claude/skills/tdd/mocking.md +59 -0
  85. package/.claude/skills/tdd/tests.md +77 -0
  86. package/.claude/skills/user-stories/SKILL.md +138 -0
  87. package/.claude/skills/user-stories/schema/story.schema.json +39 -0
  88. package/.claude/skills/user-stories/scripts/render-backlog.ts +4 -0
  89. package/.claude/skills/user-stories/scripts/validate-stories.ts +13 -0
  90. package/.claude/skills/user-stories/templates/story-template.md +38 -0
  91. package/.claude/skills/ux-design/SKILL.md +107 -0
  92. package/.claude/skills/writing-for-agents/SKILL-MECHANICS.md +22 -0
  93. package/.claude/skills/writing-for-agents/SKILL.md +81 -0
  94. package/.claude/tsconfig.json +15 -0
  95. package/.opencode/agents/acceptance-tester.md +64 -0
  96. package/.opencode/agents/code-reviewer.md +69 -0
  97. package/.opencode/agents/domain-architect.md +59 -0
  98. package/.opencode/agents/lead-dev.md +67 -0
  99. package/.opencode/agents/product-owner.md +59 -0
  100. package/.opencode/agents/repo-analyst.md +86 -0
  101. package/.opencode/agents/requirements-engineer.md +58 -0
  102. package/.opencode/agents/ux-designer.md +83 -0
  103. package/.opencode/commands/adopt.md +7 -0
  104. package/.opencode/commands/backlog-sync.md +7 -0
  105. package/.opencode/commands/bootstrap.md +7 -0
  106. package/.opencode/commands/event-storming.md +7 -0
  107. package/.opencode/commands/feature.md +7 -0
  108. package/.opencode/commands/grill-me.md +7 -0
  109. package/.opencode/commands/grill-with-docs.md +7 -0
  110. package/.opencode/commands/groom-backlog.md +7 -0
  111. package/.opencode/commands/implement.md +7 -0
  112. package/.opencode/commands/improve-codebase-architecture.md +7 -0
  113. package/.opencode/commands/retro.md +7 -0
  114. package/.opencode/commands/review-stories.md +7 -0
  115. package/.opencode/commands/shape.md +7 -0
  116. package/.opencode/opencode.jsonc +15 -0
  117. package/.opencode/plugins/kataster/index.ts +283 -0
  118. package/AGENTS.md +21 -0
  119. package/CHANGELOG.md +82 -0
  120. package/LICENSE +21 -0
  121. package/README.md +324 -2
  122. package/bin/kataster.mjs +7 -0
  123. package/extras/skills/shadcn/SKILL.md +277 -0
  124. package/extras/skills/shadcn/agents/openai.yml +5 -0
  125. package/extras/skills/shadcn/assets/shadcn-small.png +0 -0
  126. package/extras/skills/shadcn/assets/shadcn.png +0 -0
  127. package/extras/skills/shadcn/cli.md +290 -0
  128. package/extras/skills/shadcn/customization.md +209 -0
  129. package/extras/skills/shadcn/evals/evals.json +77 -0
  130. package/extras/skills/shadcn/mcp.md +105 -0
  131. package/extras/skills/shadcn/registry.md +277 -0
  132. package/extras/skills/shadcn/rules/base-vs-radix.md +306 -0
  133. package/extras/skills/shadcn/rules/chat.md +250 -0
  134. package/extras/skills/shadcn/rules/composition.md +213 -0
  135. package/extras/skills/shadcn/rules/forms.md +192 -0
  136. package/extras/skills/shadcn/rules/icons.md +101 -0
  137. package/extras/skills/shadcn/rules/styling.md +185 -0
  138. package/guide/README.md +9 -0
  139. package/guide/brownfield.md +242 -0
  140. package/guide/concepts.md +109 -0
  141. package/guide/greenfield.md +84 -0
  142. package/guide/install.md +144 -0
  143. package/guide/release.md +62 -0
  144. package/package.json +56 -4
  145. package/src/cli.mjs +302 -0
  146. package/src/detect.mjs +237 -0
  147. package/src/install.mjs +427 -0
  148. package/src/repo.mjs +129 -0
  149. package/src/util.mjs +110 -0
  150. package/templates/CONTEXT.md +21 -0
  151. package/templates/conventions.md +63 -0
  152. package/templates/docs/adr/README.md +7 -0
  153. package/templates/docs/architecture/data-model.md +31 -0
  154. package/templates/docs/domain/context-map.md +21 -0
  155. package/templates/docs/domain/events.yaml +23 -0
  156. package/templates/docs/product/initiatives/README.md +41 -0
  157. package/templates/docs/product/ux-guidelines.md +27 -0
  158. package/templates/docs/product/vision.md +36 -0
  159. package/templates/docs/reviews/.gitkeep +0 -0
  160. package/templates/docs/stories/OPEN_QUESTIONS.md +8 -0
  161. package/templates/docs/stories/README.md +26 -0
  162. package/templates/docs/workflow.md +135 -0
@@ -0,0 +1,31 @@
1
+ ---
2
+ name: review-stories
3
+ description: Has stories checked in one lead-dev pass (value, build, UX), applies the small fixes and guides the user to approval (status ready).
4
+ disable-model-invocation: true
5
+ argument-hint: "[ST-IDs, empty = all with status review]"
6
+ ---
7
+
8
+ # Story Review
9
+
10
+ The perspectives of product owner, lead dev and UX were settled with the user in the grilling. This review checks
11
+ that the stories carry them – one pass, not a second discovery (user, 2026-10-26).
12
+
13
+ 1. **Selection**: Stories from the invocation arguments, or all with `status: review` in `docs/stories/`. With a
14
+ tracker (`backlog.provider` ≠ `local`), pull first (`/backlog-sync pull`) so the review sees the tracker's state.
15
+ None found → report and stop.
16
+ 2. **Delegate once** to `lead-dev` with the full file paths of the stories, the review file path
17
+ `docs/reviews/YYYY-MM-DD-story-review.md` and the task *story review: all three lenses*. It sets `size`, `risk`,
18
+ `depends_on` and `priority` and writes the findings into the review file.
19
+ 3. **Let the user decide** only what the findings leave open: conflicts with a decision from the grilling, a
20
+ proposed split, a proposed new story (max. 3 questions at a time, each with a recommended answer). Adopt nothing
21
+ that changes a grilling decision without consent.
22
+ 4. **Apply**:
23
+ - Small content changes (a scenario, a wording, a Context line, a G-reference) → edit the story yourself.
24
+ - A split or a restructured story → delegate to `requirements-engineer` (referencing the review file).
25
+ - With a tracker, push the selected stories that are still unlinked (`/backlog-sync push ST-001 …`) **before**
26
+ setting `ready` – the push creates the issue and writes `external`; without it the validator refuses `ready`.
27
+ - Approved stories: `node .claude/skills/implement/scripts/story-status.ts ST-NNN ready` (only if `size` is set
28
+ and no `[OPEN]` remains – the script checks this). GitHub follows the status change directly; for Jira, run the
29
+ transition the script prints.
30
+ - Push the changed and approved stories again so body, labels and status agree in the tracker.
31
+ 5. Wrap-up: run `node .claude/skills/user-stories/scripts/validate-stories.ts`, show the status overview and point to `docs/stories/BACKLOG.md` (regenerated automatically), where `ready` stories are queued for the engineering workflow.
@@ -0,0 +1,193 @@
1
+ ---
2
+ name: shape
3
+ description: Grill a large idea before it becomes features – frame the problem, decide where it belongs (existing or new bounded context, shared library), map what changes vs. what is new, slice it, and produce a rough cost range with confidence. Creates no stories; ends with a go / slice / spike / discover / park / no-go recommendation.
4
+ disable-model-invocation: true
5
+ argument-hint: "<idea | ST-NNN | #issue | PROJ-123 | URL> [--dry-run]"
6
+ ---
7
+
8
+ # Shape a large idea
9
+
10
+ This is the step between an idea and `/feature`. It answers four questions and then stops:
11
+
12
+ 1. **Should we do this at all?** Problem, outcome, fit with the vision, non-goals.
13
+ 2. **Where does it belong?** Existing context, new context, shared library, or cross-cutting.
14
+ 3. **What does it touch?** What changes, and what is genuinely new.
15
+ 4. **What does it roughly cost?** Scope, range, risk, confidence – not a single number.
16
+
17
+ It creates **no stories**. Stories remain `/feature`'s job, one slice at a time.
18
+
19
+ ## When to use it
20
+
21
+ Use `/shape` when the idea is larger than one feature: it crosses contexts, needs a new context or service, a
22
+ migration, an integration, or the question "should we, and what does it cost?" is still open. A normal story goes
23
+ straight to `/feature`; don't turn this into ceremony.
24
+
25
+ A **new service in an existing monorepo** is not greenfield. It inherits the stack, the shared packages, the
26
+ deployment and the identity model – treat those as constraints, not as a blank page.
27
+
28
+ ## 0. Intake and mode
29
+
30
+ A reference (`ST-NNN`, `#123`, `PROJ-123`, URL) is resolved with the read-only intake:
31
+
32
+ ```bash
33
+ node .claude/skills/feature/scripts/intake.ts <reference> --json
34
+ # Jira: fetch the issue with jira_get_issue, write it as JSON, then
35
+ node .claude/skills/feature/scripts/intake.ts PROJ-123 --from <issue.json> --json
36
+ ```
37
+
38
+ Summarize the source in 3–5 lines and have the user confirm it. A ticket is **evidence, not a specification**.
39
+
40
+ `--dry-run` – or the user saying "just walk me through it" – means: grill, show the brief in the conversation,
41
+ write **nothing**. Without it, write the artifact only after the user confirms the summary.
42
+
43
+ ## 1. Read and scan
44
+
45
+ Read `docs/product/vision.md`, `CONTEXT.md`, `docs/domain/events.yaml`, `docs/domain/context-map.md`,
46
+ `docs/architecture/data-model.md`, `docs/architecture/repo-map.md`/`.json`, `docs/engineering/conventions.md`,
47
+ `docs/adr/`, `docs/stories/BACKLOG.md`, `docs/product/ux-guidelines.md`.
48
+
49
+ Also scan the **configuration and integration points** for systems that already own the data the idea is about:
50
+ `.env.example`, `configuration.ts`, config DTOs, API clients, external ids in entities. A name that appears only
51
+ there – a store, an ERP, a marketplace – is evidence of a system the brief must place. Report what you found and
52
+ what it implies; do not turn a config key into an architectural decision.
53
+
54
+ If an analogue exists (a sibling service the idea resembles), use it as evidence. If it is large and not adopted,
55
+ delegate one `repo-analyst` lens with a clear task instead of reading all of it yourself. Its model is evidence,
56
+ **never** the new context's model.
57
+
58
+ Three starting points:
59
+
60
+ - **Domain model exists** (adopted, or after `/bootstrap`): full placement, impact and cost.
61
+ - **No domain model**: frame and slice are still useful; placement stays `unknown`; recommend `/bootstrap` or
62
+ `/adopt`. Do not invent contexts from the idea.
63
+ - **Partially adopted / an analogue exists**: use the sibling's model where it exists and its code/config where it
64
+ does not. Confidence is `medium` at best; say what is inferred. If the sibling is central, recommend
65
+ `/adopt <sibling>` as the first slice before committing to a cost range.
66
+
67
+ ## 2. Frame – product owner lens
68
+
69
+ Call the `grilling` skill. The frontier:
70
+
71
+ - Which problem, for which user group, and why now?
72
+ - What is the observable outcome, and how would we know it worked?
73
+ - Must / should / could / won't – what ships first, what is explicitly out?
74
+ - Does it fit `vision.md`, or does it change the vision? A vision change is the user's decision.
75
+ - What is the cost of **not** doing it – opportunity or fear?
76
+ - Constraints: deadline, regulation, budget, team, existing commitments.
77
+ - At least one non-goal: what this initiative will not become.
78
+
79
+ Don't move on before the user confirms a shared understanding.
80
+
81
+ ## 3. Place – architect lens
82
+
83
+ Call the `architect` skill (and `domain-model` where the language is unclear). For each capability:
84
+
85
+ | If … | Then |
86
+ |---|---|
87
+ | same language, same invariants, additive change | **existing context** |
88
+ | a term means something else, or a new invariant must be immediately consistent | **new context** |
89
+ | different rate of change, team, regulation or external model | **new context** |
90
+ | pure logic used by two or more contexts, no lifecycle of its own | **shared library** |
91
+ | notifications, audit, auth, files – a generic capability | prefer an **existing generic context** over a new one |
92
+
93
+ A new **service** is not automatically a new **domain**, and a new domain is not automatically a new service.
94
+ Separate contexts can share infrastructure packages; they must not share a domain model just because the words
95
+ look alike. Same word, different meaning (Product as dish vs. Product as sellable variant) is the classic reason
96
+ to split.
97
+
98
+ Produce a recommendation **with at least one alternative and its consequence**, plus a confidence:
99
+
100
+ > Reuse `BC-Delivery` for rescheduling (cheap, but the aggregate grows) vs. a new `BC-Delivery-Planning` (cleaner
101
+ > consistency boundary, one more integration). Recommendation: existing context for slice 1, revisit if carriers
102
+ > get their own scheduling model.
103
+
104
+ The user decides.
105
+
106
+ ## 4. Impact – lead-dev lens
107
+
108
+ Split the answer into **change** and **build new**. Leave nothing implicit.
109
+
110
+ | Dimension | Questions | Evidence |
111
+ |---|---|---|
112
+ | Domain | which aggregates, events, commands, policies, read models? new or changed? | `events.yaml` (IDs, counts) |
113
+ | Code | which packages and consumers? which new packages? | `repo-map`, dependency edges |
114
+ | Interfaces | screens/forms/flows, API endpoints, jobs, webhooks, integrations | `ux-guidelines`, conventions |
115
+ | Data | schema change, migration, backfill, retention, reporting | `data-model.md`, schema |
116
+ | Rules | boundaries (`boundaries.yaml`), permissions, checks (`checks.yaml`) | architecture checks |
117
+ | Tests | which seams – command, policy, read model, page? | seam catalog |
118
+ | Ops | config, deployment, monitoring, support | profile, ADRs |
119
+
120
+ ## 5. Slice
121
+
122
+ Propose 2–5 independently valuable slices, walking skeleton first. Each slice should be a `/feature` run, not a
123
+ project. For each: outcome, contexts touched, dependencies, rough size, confidence.
124
+
125
+ If the idea cannot be sliced, say so – that is a finding, not a failure of the format.
126
+
127
+ ## 6. Cost
128
+
129
+ Never one number. Give **scope**, **range** and **risk**, plus the basis.
130
+
131
+ - **Scope:** number of slices and stories, with sizes (XS–XL). The most defensible unit the toolkit has.
132
+ - **Range:** e.g. "3–5 stories, mostly S/M" or "2–4 weeks with one developer". Person-days only if the user has a
133
+ calibration ("1 M ≈ 3 days"); otherwise keep story sizes.
134
+ - **Risk:** what could blow the estimate up – unknown integration, migration, compliance, missing language.
135
+ - **Confidence:** low/medium/high, and **what would raise it** (spike, prototype, measurement, talking to the
136
+ carrier).
137
+ - **Basis:** the counts behind the estimate (e.g. 1 new command, 1 event, 1 policy, 1 read model, 1 page, 1
138
+ migration, 2 packages, 1 integration).
139
+ - **Analogue:** if a similar service exists, use it as the calibration anchor – modules, LOC, endpoints, entities,
140
+ specs, and which parts are domain-specific vs. generic. Say what can be reused and what is genuinely new. The
141
+ analogue is evidence, not a template; a copy is not automatically the right design.
142
+
143
+ Rules: an unknown is a line in the brief, not a silent +20 %. A spike is a cost, not a gap – give it a question and
144
+ a timebox. Estimate the **delta** to today's system, not the whole system. Without evidence, say `unknown` and
145
+ recommend discovery.
146
+
147
+ ## 7. Decide and write
148
+
149
+ End with exactly one recommendation:
150
+
151
+ `go` · `go with slice 1` · `spike first` · `needs discovery (/bootstrap or /adopt)` · `park` · `no-go`
152
+
153
+ The user decides. Write `docs/product/initiatives/<slug>.md` after the user confirms the summary of steps 2–6.
154
+ Set `status: proposed`; only the user sets `accepted`. On handoff, set `promoted: <slug>` and send the first slice
155
+ to `/feature`. **Do not create stories here and do not touch `docs/stories/`.**
156
+
157
+ With `--dry-run`, print the same brief in the conversation and write no file.
158
+
159
+ ### Artifact
160
+
161
+ ```markdown
162
+ ---
163
+ status: proposed # proposed | accepted | parked | rejected
164
+ created: 2026-10-31
165
+ source: PROJ-123 # optional
166
+ promoted: "" # feature:<slug> once slices are handed to /feature
167
+ ---
168
+
169
+ # Initiative: <title>
170
+
171
+ ## Problem & outcome
172
+ ## Fit with the vision
173
+ ## Placement
174
+ | Capability | Context | New/existing | Why | Confidence |
175
+ ## Impact – change vs. build new
176
+ | Dimension | Change | New | Evidence |
177
+ ## Slices
178
+ 1. …
179
+ ## Rough cost
180
+ | Currency | Estimate | Confidence | Basis |
181
+ ## Risks & open questions
182
+ ## Recommendation
183
+ ```
184
+
185
+ ## Guardrails
186
+
187
+ - The brief is a proposal. The user sets `accepted`.
188
+ - Do not let the estimate become a commitment. It is a range with a basis.
189
+ - Every brief ends in a recommendation; do not use `/shape` to avoid a decision.
190
+ - Do not turn initiatives into a shadow backlog. They have no `ready`/`done`; only stories do.
191
+ - Do not write an artifact when the user only wants to test the method.
192
+ - Keep the artifact project-neutral except for the project's own terms from `CONTEXT.md`.
193
+ - Never present an analogue's domain model as the new context's model.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: tdd
3
+ description: Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
4
+ ---
5
+
6
+ # Test-Driven Development
7
+
8
+ TDD is the red → green loop. This skill is the reference that makes that loop produce tests worth keeping: what a good test is, where tests go, the anti-patterns, and the rules of the loop. Every section applies on every cycle: consult them before and during the loop, not after.
9
+
10
+ When exploring the codebase, read `CONTEXT.md` (if it exists) so test names and interface vocabulary match the project's domain language, and respect ADRs in the area you're touching.
11
+
12
+ ## What a good test is
13
+
14
+ Tests verify behavior through public interfaces, not implementation details. Code can change entirely; tests shouldn't. A good test reads like a specification: "user can checkout with valid cart" tells you exactly what capability exists, and it survives refactors because it doesn't care about internal structure.
15
+
16
+ See [tests.md](tests.md) for examples and [mocking.md](mocking.md) for mocking guidelines.
17
+
18
+ ## Seams: where tests go
19
+
20
+ A **seam** is the public boundary you test at: the interface where you observe behavior without reaching inside. Tests live at seams, never against internals.
21
+
22
+ **Test only at pre-agreed seams.** Before writing any test, write down the seams under test and confirm them with the user – the seam catalog in `docs/engineering/conventions.md` is confirmed already, so only a seam outside it goes to the user. No test is written at an unconfirmed seam. You can't test everything, so agreeing the seams up front is how testing effort lands on the critical paths and complex logic instead of every edge case.
23
+
24
+ Ask: "What's the public interface, and which seams should we test?" Give each proposed seam a one-line note on what it catches and what it misses.
25
+
26
+ When the shape of that interface is itself in question (how deep the module is, where the seam belongs, what the interface should expose), call the `skill` tool with `codebase-design` for the vocabulary. It is the shared source of the module, interface, depth, seam, adapter, leverage and locality terms, and it is a reference to consult, not a session to run.
27
+
28
+ ## Anti-patterns
29
+
30
+ - **Implementation-coupled**: mocks internal collaborators, tests private methods, or verifies through a side channel (querying the database instead of using the interface). The tell: the test breaks when you refactor but behavior hasn't changed.
31
+ - **Tautological**: the assertion recomputes the expected value the way the code does (`expect(add(a, b)).toBe(a + b)`, a snapshot derived by hand the same way, a constant asserted equal to itself), so it passes by construction and can never disagree with the code. Expected values must come from an independent source of truth: a known-good literal, a worked example, the spec.
32
+ - **Horizontal slicing**: writing all tests first, then all implementation. Bulk tests verify _imagined_ behavior: you test the _shape_ of things rather than user-facing behavior, the tests go insensitive to real changes, and you commit to test structure before understanding the implementation. Work in **vertical slices** instead: one test → one implementation → repeat, each test a **tracer bullet** that responds to what the last cycle taught you.
33
+
34
+ ## Rules of the loop
35
+
36
+ - **Red before green.** Write the failing test first, then only enough code to pass it. Don't anticipate future tests or add speculative features.
37
+ - **One slice at a time.** One seam, one test, one minimal implementation per cycle.
38
+ - **Refactoring is not part of the loop.** It belongs to the review stage (the review and refactor steps of `/implement`), not the red → green implementation cycle.
@@ -0,0 +1,59 @@
1
+ # When to Mock
2
+
3
+ Mock at **system boundaries** only:
4
+
5
+ - External APIs (payment, email, etc.)
6
+ - Databases (sometimes - prefer test DB)
7
+ - Time/randomness
8
+ - File system (sometimes)
9
+
10
+ Don't mock:
11
+
12
+ - Your own classes/modules
13
+ - Internal collaborators
14
+ - Anything you control
15
+
16
+ ## Designing for Mockability
17
+
18
+ At system boundaries, design interfaces that are easy to mock:
19
+
20
+ **1. Use dependency injection**
21
+
22
+ Pass external dependencies in rather than creating them internally:
23
+
24
+ ```typescript
25
+ // Easy to mock
26
+ function processPayment(order, paymentClient) {
27
+ return paymentClient.charge(order.total);
28
+ }
29
+
30
+ // Hard to mock
31
+ function processPayment(order) {
32
+ const client = new StripeClient(process.env.STRIPE_KEY);
33
+ return client.charge(order.total);
34
+ }
35
+ ```
36
+
37
+ **2. Prefer SDK-style interfaces over generic fetchers**
38
+
39
+ Create specific functions for each external operation instead of one generic function with conditional logic:
40
+
41
+ ```typescript
42
+ // GOOD: Each function is independently mockable
43
+ const api = {
44
+ getUser: (id) => fetch(`/users/${id}`),
45
+ getOrders: (userId) => fetch(`/users/${userId}/orders`),
46
+ createOrder: (data) => fetch('/orders', { method: 'POST', body: data }),
47
+ };
48
+
49
+ // BAD: Mocking requires conditional logic inside the mock
50
+ const api = {
51
+ fetch: (endpoint, options) => fetch(endpoint, options),
52
+ };
53
+ ```
54
+
55
+ The SDK approach means:
56
+ - Each mock returns one specific shape
57
+ - No conditional logic in test setup
58
+ - Easier to see which endpoints a test exercises
59
+ - Type safety per endpoint
@@ -0,0 +1,77 @@
1
+ # Good and Bad Tests
2
+
3
+ ## Good Tests
4
+
5
+ **Integration-style**: Test through real interfaces, not mocks of internal parts.
6
+
7
+ ```typescript
8
+ // GOOD: Tests observable behavior
9
+ test("user can checkout with valid cart", async () => {
10
+ const cart = createCart();
11
+ cart.add(product);
12
+ const result = await checkout(cart, paymentMethod);
13
+ expect(result.status).toBe("confirmed");
14
+ });
15
+ ```
16
+
17
+ Characteristics:
18
+
19
+ - Tests behavior users/callers care about
20
+ - Uses public API only
21
+ - Survives internal refactors
22
+ - Describes WHAT, not HOW
23
+ - One logical assertion per test
24
+
25
+ ## Bad Tests
26
+
27
+ **Implementation-detail tests**: Coupled to internal structure.
28
+
29
+ ```typescript
30
+ // BAD: Tests implementation details
31
+ test("checkout calls paymentService.process", async () => {
32
+ const mockPayment = jest.mock(paymentService);
33
+ await checkout(cart, payment);
34
+ expect(mockPayment.process).toHaveBeenCalledWith(cart.total);
35
+ });
36
+ ```
37
+
38
+ Red flags:
39
+
40
+ - Mocking internal collaborators
41
+ - Testing private methods
42
+ - Asserting on call counts/order
43
+ - Test breaks when refactoring without behavior change
44
+ - Test name describes HOW not WHAT
45
+ - Verifying through external means instead of interface
46
+
47
+ ```typescript
48
+ // BAD: Bypasses interface to verify
49
+ test("createUser saves to database", async () => {
50
+ await createUser({ name: "Alice" });
51
+ const row = await db.query("SELECT * FROM users WHERE name = ?", ["Alice"]);
52
+ expect(row).toBeDefined();
53
+ });
54
+
55
+ // GOOD: Verifies through interface
56
+ test("createUser makes user retrievable", async () => {
57
+ const user = await createUser({ name: "Alice" });
58
+ const retrieved = await getUser(user.id);
59
+ expect(retrieved.name).toBe("Alice");
60
+ });
61
+ ```
62
+
63
+ **Tautological tests**: Expected value restates the implementation, so the test passes by construction.
64
+
65
+ ```typescript
66
+ // BAD: Expected value is recomputed the way the code computes it
67
+ test("calculateTotal sums line items", () => {
68
+ const items = [{ price: 10 }, { price: 5 }];
69
+ const expected = items.reduce((sum, i) => sum + i.price, 0);
70
+ expect(calculateTotal(items)).toBe(expected);
71
+ });
72
+
73
+ // GOOD: Expected value is an independent, known literal
74
+ test("calculateTotal sums line items", () => {
75
+ expect(calculateTotal([{ price: 10 }, { price: 5 }])).toBe(15);
76
+ });
77
+ ```
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: user-stories
3
+ description: Rules, format and splitting patterns for user stories, spikes and tech tasks in docs/stories/ – frontmatter, INVEST, Gherkin, status workflow, and the local mirror when a Jira or GitHub tracker is configured.
4
+ ---
5
+
6
+ # User Stories
7
+
8
+ The backlog is **local by default**: every story is a Markdown file in `docs/stories/`, and there is no external
9
+ issue tracker. With `backlog.provider` set to `github` or `jira` in `.claude/project.json`, the tracker is the source
10
+ of truth and `docs/stories/` is its local mirror – the `backlog` skill owns the sync. In both cases the story file is
11
+ the artifact this skill describes, and `docs/stories/BACKLOG.md` is generated from it by a hook – never edit it
12
+ manually.
13
+
14
+ ## Derivation from the Event Storming
15
+ | Source in `events.yaml` | typically becomes |
16
+ |---|---|
17
+ | Actor + command → event | Story "As a <actor>, I want to <command>, so that <effect>" |
18
+ | Policy (automatic) | Story from the beneficiary's perspective ("… I am notified when …") |
19
+ | Read model | Story about finding/seeing information |
20
+ | Hotspot | Spike (question + timebox) or `[OPEN]` in the story |
21
+ | External system | Story or tech task for the integration |
22
+
23
+ Every story links the affected events (`events: [EVT-…]`). Commands without a story = gap → report it.
24
+
25
+ ## File & Frontmatter
26
+ File: `docs/stories/ST-NNN-short-title.md` (three-digit number, consecutive, never reused).
27
+ Template: `templates/story-template.md`. Schema: `schema/story.schema.json`.
28
+
29
+ | Field | Required | Who sets it |
30
+ |---|---|---|
31
+ | `id`, `title`, `type`, `context`, `events` | yes | requirements-engineer |
32
+ | `priority` (`must/should/could/wont`) | yes | product-owner (initially `should`) |
33
+ | `size` (`XS–XL`), `risk`, `depends_on` | before `ready` | lead-dev |
34
+ | `labels` | no | anyone (lowercase, e.g. `mvp`, `feature:<slug>`) |
35
+ | `status` | yes | see workflow |
36
+ | `external` | with a tracker | backlog sync – never by hand |
37
+
38
+ With `backlog.provider` = `github` or `jira`, `external` holds the tracker key (`external.key`) and URL. The sync writes
39
+ it; a story must have it before it reaches `ready`. `ST-NNN` stays the stable id – never renumber a story to follow an
40
+ issue key, and never use a label that starts with the management prefix (`kataster-` by default).
41
+
42
+ **No implementation foundation inside a domain story** (user, 2026-10-01). A story describes behaviour a person
43
+ can observe; technical groundwork that several stories need is its own **tech task**, with its own `depends_on`.
44
+ A checklist of files to create, layers to convert or configs to set is an implementation plan, not acceptance
45
+ criteria – it is neither the user's language nor a testable outcome, and it hides the real size of the story.
46
+ *Why this is a rule:* a story once carried a seven-item checklist that converted **every command built so far** to an
47
+ event catalogue. That made an M story an L, put a retrofit nine dependencies deep, and the cost grew with every
48
+ command built before it. The grooming pulled it out as its own tech task and the story went back to M.
49
+ The exception is a tech task or a spike – there a checklist *is* the acceptance criteria.
50
+
51
+ **The `ui` label** marks every story with a frontend part – a screen, a form, a flow a person walks through.
52
+ `/review-stories` and `/groom-backlog` route those to the `ux-designer`, so a missing label means a story nobody
53
+ reviews from that side. What such a story has to say about the interface (and what it must not) is in the
54
+ `ux-design` skill; the decisions it is measured against are `docs/product/ux-guidelines.md`.
55
+
56
+ ## Backlog Order
57
+
58
+ `docs/stories/BACKLOG.md` is generated (`scripts/render-backlog.ts`) and the order is **derived on every render,
59
+ never stored**. `backlogOrder` in `.claude/lib/discovery.ts` sorts:
60
+
61
+ 1. by `priority` – `must`, `should`, `could`, `wont`;
62
+ 2. within the same priority **by story ID, ascending**;
63
+ 3. then dependencies first – a story that a listed story depends on is pulled in front of it.
64
+
65
+ What follows from that, and has caught us out before (story review 2026-09-29):
66
+
67
+ - **Numbers decide within a priority.** A story with a higher ID never overtakes a lower one, so a new tech task
68
+ always lands *behind* the older stories it is meant to precede. Raising it to `must` does not help when those
69
+ are `must` too.
70
+ - **`depends_on` is the only thing that really moves a story forward** – and it is for real dependencies, not
71
+ for planning wishes. Do not invent one to force an order.
72
+ - **So say it out loud when a story is built out of turn**: `/implement ST-NNN` with the ID, never the bare
73
+ `/implement` (which follows this very order via `next-story.ts`), and record the intended order in the
74
+ story's Notes so the next person sees why.
75
+
76
+ There is no script that re-sorts the backlog, because there is nothing to sort – only story files and this rule.
77
+
78
+ ## Status Workflow
79
+ `draft` (in progress) → `review` (finished, waiting for the story review) → `ready` (after user approval; `size` set, no `[OPEN]`) → `in-progress` → `done`.
80
+
81
+ Discovery ends at `ready`. The engineering workflow (`/implement`) sets `in-progress` and `done` with `node .claude/skills/implement/scripts/story-status.ts`; the validation hook enforces the same rules when the file is edited directly:
82
+ - `in-progress` and `done` need all `depends_on` stories `done`.
83
+ - `done` needs a test titled `ST-NNN: <scenario title>` for every scenario (story), or every checklist item ticked `- [x]` (spike, tech task).
84
+ - A story that turns out wrong during implementation goes from `in-progress` (or `ready`) back to `review`, with `[OPEN]` + an entry in `OPEN_QUESTIONS.md`; it is revised and approved again.
85
+ - `done` is final. A change to finished behaviour is a new story.
86
+ - `superseded` – a story that is not built as written, before it was started: its work was done elsewhere (a direct change, another story), or a new planning replaces it. Set from `draft`, `review` or `ready`, never from `in-progress`. Its first paragraph says where the work went or what replaces it; the file stays as the record other documents link to. Final like `done`, listed in its own section of the backlog and counted in no progress (user, 2026-10-25). Set it with `node .claude/skills/implement/scripts/story-status.ts ST-NNN superseded`.
87
+
88
+ ## Body Structure
89
+ ```
90
+ ## Story
91
+ As a <role from CONTEXT.md>, I want <goal>, so that <business benefit>.
92
+
93
+ ## Context
94
+ Briefly: trigger, preconditions, business rules – from the command's `rules` **and from the `invariants` of its
95
+ aggregate**. An invariant is stated once, on the aggregate, and a command's `rules` list only what
96
+ it adds beyond them (`docs/domain/events.yaml`). So a command with few rules is not a command with
97
+ few rules to test: read its aggregate's `invariants` and give every one the command can violate its own rejection
98
+ scenario. "A product always has a name, a price and exactly one category" is why
99
+ `CMD-CorrectProduct` needs a scenario for an emptied name, although its `rules` do not mention it.
100
+
101
+ ## Acceptance Criteria
102
+ Scenario: <Happy path>
103
+ Given …
104
+ When …
105
+ Then …
106
+
107
+ Scenario: <Error or edge case>
108
+ …
109
+
110
+ ## Out of Scope
111
+ ## Open Questions
112
+ ```
113
+ Scenario titles are unique within a story and become test titles (`ST-NNN: <scenario title>`). Renaming a scenario of an `in-progress` or `done` story breaks that link – the test must be renamed too (`verify.ts` reports it).
114
+
115
+ Spikes: `## Question`, `## Timebox`, `## Acceptance Criteria` (checklist `- [ ] …`).
116
+ Tech tasks: `## Task`, `## Acceptance Criteria` (checklist).
117
+
118
+ ## Quality Rules
119
+ - **INVEST**: independent, negotiable, valuable, estimable, small, testable.
120
+ - Gherkin in English (`Given / When / Then / And / But`). At least a happy path + one error/edge case.
121
+ - Scenarios describe **behavior**, not UI details ("clicks the blue button" ✗) and not technology.
122
+ - **At most 10 scenarios** per story – each one is a red → green cycle in `/implement`. Spend them on domain behavior and real rejections. A pattern `docs/product/ux-guidelines.md` already defines (grouping, collapsing, kept choices after a rejection) is a G-number in the Context, tested once where the pattern is built, not restated as scenarios. More left → split (patterns below). The validator warns in `draft` and refuses `review`.
123
+ - Measurable outcomes: "Then the item has the status 'out of order'" instead of "Then it works".
124
+ - Non-functional requirements as concrete scenarios or checklist items with a number.
125
+ - Terms exactly as in the glossary `CONTEXT.md`; never use an `_Avoid_` synonym.
126
+
127
+ ## Splitting Patterns (if larger than M)
128
+ By workflow step · by business rule · happy path first, special cases later · by data variant · by role · "manual first, automated later".
129
+
130
+ ## Validation
131
+ A hook checks every written story automatically and regenerates `BACKLOG.md`. Full check of all stories:
132
+ `node .claude/skills/user-stories/scripts/validate-stories.ts`
133
+ After deleting or renaming story files, regenerate the backlog:
134
+ `node .claude/skills/user-stories/scripts/render-backlog.ts`
135
+
136
+ With a tracker, validation also refuses a `ready`, `in-progress` or `done` story without `external`, and warns about
137
+ labels reserved for the sync. Pull before validating a mirror that may be stale (`/backlog-sync pull`) and push after
138
+ changing it (`/backlog-sync push`).
@@ -0,0 +1,39 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "Story frontmatter (docs/stories/ST-NNN-*.md)",
4
+ "type": "object",
5
+ "additionalProperties": false,
6
+ "required": ["id", "title", "type", "context", "priority", "status"],
7
+ "properties": {
8
+ "id": { "type": "string", "pattern": "^ST-[0-9]{3,}$" },
9
+ "title": { "type": "string", "minLength": 5, "maxLength": 120 },
10
+ "type": { "enum": ["story", "spike", "tech-task"] },
11
+ "context": { "type": "string", "pattern": "^BC-[A-Za-z0-9]+$" },
12
+ "priority": { "enum": ["must", "should", "could", "wont"] },
13
+ "size": { "enum": ["XS", "S", "M", "L", "XL", null] },
14
+ "risk": { "enum": ["low", "medium", "high", null] },
15
+ "events": {
16
+ "type": "array", "uniqueItems": true,
17
+ "items": { "type": "string", "pattern": "^EVT-[A-Za-z0-9]+$" }
18
+ },
19
+ "depends_on": {
20
+ "type": "array", "uniqueItems": true,
21
+ "items": { "type": "string", "pattern": "^ST-[0-9]{3,}$" }
22
+ },
23
+ "labels": {
24
+ "type": "array", "uniqueItems": true,
25
+ "items": { "type": "string", "pattern": "^[a-z0-9][a-z0-9:_-]*$" }
26
+ },
27
+ "external": {
28
+ "type": "object",
29
+ "additionalProperties": false,
30
+ "required": ["provider", "key"],
31
+ "properties": {
32
+ "provider": { "enum": ["github", "jira"] },
33
+ "key": { "type": "string", "minLength": 1 },
34
+ "url": { "type": "string" }
35
+ }
36
+ },
37
+ "status": { "enum": ["draft", "review", "ready", "in-progress", "done", "superseded"] }
38
+ }
39
+ }
@@ -0,0 +1,4 @@
1
+ /** Regenerates docs/stories/BACKLOG.md from the story files (also after deleting or renaming stories). */
2
+ import * as d from "../../../lib/discovery.ts";
3
+
4
+ console.log(`Written: ${d.rel(d.writeBacklogMd())}`);
@@ -0,0 +1,13 @@
1
+ /** Validates all stories in docs/stories/ (frontmatter, references, Gherkin, status). Exit 1 on errors. */
2
+ import * as d from "../../../lib/discovery.ts";
3
+
4
+ const report = new d.Report();
5
+ const stories = d.validateStories(report);
6
+ for (const line of report.lines()) console.log(line);
7
+ if (report.ok) {
8
+ const byStatus = new Map<string, number>();
9
+ for (const s of stories.values()) byStatus.set(s.meta.status, (byStatus.get(s.meta.status) ?? 0) + 1);
10
+ const summary = [...byStatus].map(([st, n]) => `${st}: ${n}`).join(", ");
11
+ console.log(`OK – ${stories.size} stories` + (summary ? ` (${summary})` : ""));
12
+ }
13
+ process.exit(report.ok ? 0 : 1);
@@ -0,0 +1,38 @@
1
+ ---
2
+ id: ST-001
3
+ title: Short, domain-level title
4
+ type: story
5
+ context: BC-Example
6
+ priority: should
7
+ size: null
8
+ risk: null
9
+ events: [EVT-Example]
10
+ depends_on: []
11
+ labels: []
12
+ status: draft
13
+ # external: { provider: github|jira, key: "…", url: "…" } – written by /backlog-sync, never by hand
14
+ ---
15
+
16
+ ## Story
17
+ As a <role>, I want <goal>, so that <benefit>.
18
+
19
+ ## Context
20
+ <Trigger, preconditions, business rules>
21
+
22
+ ## Acceptance Criteria
23
+
24
+ Scenario: <Happy path>
25
+ Given <initial situation>
26
+ When <action>
27
+ Then <observable outcome>
28
+
29
+ Scenario: <Error or edge case>
30
+ Given <initial situation>
31
+ When <action>
32
+ Then <observable outcome>
33
+
34
+ ## Out of Scope
35
+ - …
36
+
37
+ ## Open Questions
38
+ - none