@eventmodelers/cli 1.0.137 → 1.0.139

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,6 +23,7 @@ npx @eventmodelers/cli init --stack umadb # UmaDB (Java)
23
23
  npx @eventmodelers/cli init --stack kurrent # Kurrent (Java, KurrentDB)
24
24
  npx @eventmodelers/cli init --stack react # React (frontend, board-polling sync) — TODO-marked, not yet filled in
25
25
  npx @eventmodelers/cli init --stack supabase-react # React + Supabase (frontend, UI-only, realtime sync)
26
+ npx @eventmodelers/cli init --stack classic # Existing codebase (any language, typically CRUD) — no scaffold
26
27
  ```
27
28
 
28
29
  The installer prompts for your API token, Organization ID, and Board ID from [app.eventmodelers.ai/account](https://app.eventmodelers.ai/account), scaffolds the stack into your project, and writes `.eventmodelers/config.json` with your credentials.
@@ -65,6 +66,15 @@ npx @eventmodelers/cli fetch --context <name>
65
66
 
66
67
  This writes the current slice/event/command detail for that context to disk and prints a summary — no agent loop, no listener, just a one-shot pull.
67
68
 
69
+ **Adding slices to an existing codebase (`classic`):**
70
+
71
+ ```bash
72
+ cd my-existing-app
73
+ npx @eventmodelers/cli init --stack classic
74
+ ```
75
+
76
+ `classic` scaffolds nothing into your project root — it assumes the code is already there, in whatever language and architecture it uses (typically a layered CRUD app, but not necessarily). Before the first slice, the coding agent (Claude, Codex, or any `--agent`) runs `learn-architecture`: it traces one or two existing features end to end and writes `.build-kit/ARCHITECTURE.md` — layout, reference implementations, test style, build/test commands, and how Event Modeling maps onto this code (COMMAND → service method + endpoint, EVENT → the persisted state change, READMODEL → query + DTO, AUTOMATION → the existing listener/consumer/scheduler). The `build-*` skills then implement every slice by copying those reference features, never by introducing a new framework or event store. Review and commit `ARCHITECTURE.md` after the first run — it's the one file that steers every later slice.
77
+
68
78
  **Checking what's installed or which credentials are active:**
69
79
 
70
80
  ```bash
@@ -93,6 +103,8 @@ your-project/
93
103
 
94
104
  The six backend stacks (`node`, `supabase`, `axon`, `opencqrs`, `umadb`, `kurrent`) also scaffold a real project skeleton into your project root (`templates/root/`) — source layout, build files, migrations, etc.
95
105
 
106
+ `classic` ships no `templates/root/` at all — it installs only `.build-kit/` and the skills into an existing codebase (see "Adding slices to an existing codebase" above).
107
+
96
108
  `react` and `supabase-react` are two more registered stacks (installable the same way). `supabase-react` is real, filled-in content — a Vite + React 19 + TypeScript scaffold that authenticates and issues command POSTs via a Supabase session (`src/lib/api.ts`/`src/lib/supabase.ts`), plus `init-style-guide`/`learn-styleguide` skills so generated UI stays on-brand. It's UI-only: `.build-kit/CLAUDE.md` only routes `STATE_CHANGE`/`STATE_VIEW` slices to `build-state-change`/`build-state-view` — an `AUTOMATION` slice has no UI counterpart and gets flagged via `request-feedback` instead, since it belongs to whichever backend stack is installed alongside this one. It needs no overrides at all and uses `shared/build-kit`'s realtime agent as-is.
97
109
 
98
110
  `react` (the plain-REST/board-polling variant, no Supabase) is still in the same state as a fresh `init --build-kit` scaffold — CLAUDE.md, the `build-*` skills, and `templates/root/` are all TODO-marked placeholders, not real content, pending an equivalent reference implementation. It overrides `lib/ralph.js` (+ `ralph-claude.js`/`ralph-local-ai.js`/`package.json`/`README.md`) for board-polling sync. Fill in the TODOs (and add a real `templates/root/` scaffold) against an actual project before relying on it.
package/cli.js CHANGED
@@ -197,6 +197,17 @@ const STACKS = {
197
197
  useShared: true,
198
198
  needsBoardId: true,
199
199
  },
200
+ // Installs into an existing codebase instead of scaffolding one — it ships no
201
+ // templates/root, so nothing is spread into the project root. Its learn-architecture
202
+ // skill analyzes the code once into .build-kit/ARCHITECTURE.md, and the build-* skills
203
+ // implement each slice the way that codebase already works (typically CRUD).
204
+ classic: {
205
+ label: 'Classic — existing codebase (any language, typically CRUD), slices built to match its architecture',
206
+ kitSubdir: 'build-kit',
207
+ kitDirName: '.build-kit',
208
+ useShared: true,
209
+ needsBoardId: true,
210
+ },
200
211
  };
201
212
 
202
213
  // Not a stack — no backend scaffold, just skills + the agent loop. Installed via
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "1.0.137",
3
+ "version": "1.0.139",
4
4
  "description": "Eventmodelers CLI — real-time Claude agent + skills for Claude Code, for any stack (Node, Supabase, Axon, OpenCQRS, UmaDB, Kurrent, or modeling-only)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -710,10 +710,12 @@ Append Given-When-Then scenario(s) to a column's spec node — the node is creat
710
710
  A step given as `{title, type}` is resolved server-side, so the names you already know need **no `get_spec_info`/`get_nodes` call first**. A title matching more than one element is rejected (`SCENARIO_ITEM_AMBIGUOUS`) with the candidates rather than guessed at, and resolution runs before anything is written — a bad name leaves no empty spec node behind.
711
711
 
712
712
  **Validation rules**:
713
+ - `context`: only READMODELs (the state the command decides against)
713
714
  - `given`: only EVENTs from same timeline
714
715
  - `when`: max one COMMAND; must be empty when `then` contains a READMODEL (use an inline QUERY item for a state-view)
715
716
  - `then`: all EVENTs OR exactly one READMODEL — never mixed
716
717
  - All referenced nodes must belong to the same chapter/timeline
718
+ - Each section is checked against the node's real type: a COMMAND lives only in `when`, a READMODEL only in `then`/`context`, an EVENT only in `given`/`then`. A step's `type`, when sent, must match the node (`SCENARIO_ITEM_TYPE_MISMATCH`); when omitted, the node's type is stored on the step
717
719
 
718
720
  **Response**:
719
721
  - `201` — `{ specNodeId, scenarios, added, isNewNode: boolean }`
@@ -0,0 +1,94 @@
1
+ ---
2
+ name: build-automation
3
+ description: Implements an automation slice (react to a state change, issue a command) inside an existing codebase, using whatever reactive mechanism it already has per .build-kit/ARCHITECTURE.md — listener, after-commit hook, message consumer or scheduled job
4
+ ---
5
+
6
+ # Build Automation Slice
7
+
8
+ > Before doing anything else, read the slice definition from `.slices/{Context}/{slicename}/slice.json`. This file is the **source of truth** for which trigger drives the automation and what command it fires — never invent fields not defined there.
9
+
10
+ > Then read `.build-kit/ARCHITECTURE.md` (run `/learn-architecture` if it is missing). Its **AUTOMATION** mapping decides which reactive mechanism this codebase uses.
11
+
12
+ ---
13
+
14
+ ## What an Automation Slice is
15
+
16
+ An automation reacts to something that happened (a trigger event, or entries showing up in a todo-list read model) and issues a command in response — without a user doing it.
17
+
18
+ In a CRUD codebase there is usually no event stream to subscribe to. The automation is built with whatever already reacts to state changes there: an in-process domain/application event listener, an after-commit hook, a message consumer, or a scheduled job that queries for pending work. An automation slice is a **state-change slice with a trigger**: build the command first, then wire the trigger.
19
+
20
+ ---
21
+
22
+ ## Step 1 — Read the slice.json
23
+
24
+ From the slice definition, extract:
25
+ - **sliceName** — the command being fired by the automation
26
+ - **context** — bounded context
27
+ - **processors[]** — the reactions this slice implements: the trigger event (or todo-list read model) and the command each one issues
28
+ - **commands[]** / **events[]** — the command fired and what it records
29
+ - **specifications[]** — test scenarios (given/when/then)
30
+ - **storylines[]** (optional) — present only when the board author built an explicit walkthrough for this flow; most slices have none. See "Storyline-derived tests" under Step 2.
31
+
32
+ > **Comments & description**: each element carries a `comments: string[]` array (board comments) and a `description` field — use them as implementation hints, and resolve consumed comments via `POST <BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/<nodeId>/comments/<commentId>/resolve`.
33
+
34
+ ---
35
+
36
+ ## Step 2 — Build the command
37
+
38
+ Follow `/build-state-change` for this slice's command and events — all of its steps, tests and checks apply. If the command already exists (built by another slice), reuse it; do not duplicate it.
39
+
40
+ ### Storyline-derived tests
41
+
42
+ If `storylines[]` includes a beat sequence running through this automation's trigger → this slice's command, the command test for that segment is already covered by build-state-change's own "Storyline-derived tests" step.
43
+
44
+ ## Step 3 — Find the trigger point
45
+
46
+ Locate where the trigger event's state change happens in the code — the **Slice map** in `ARCHITECTURE.md` names the code of the Done slice that produces it. If the map has a Done automation slice, build this one the way that one is built (same mechanism, registration and test style); otherwise pick the mechanism from `ARCHITECTURE.md`:
47
+
48
+ | Codebase has | Build the automation as |
49
+ |---|---|
50
+ | in-process application/domain events (e.g. Spring `ApplicationEventPublisher`, MediatR notifications, Node `EventEmitter`) | a listener on the existing event for the trigger — or publish a new one from the trigger's service method the same way others are published |
51
+ | transactional outbox / message broker | a consumer on the existing topic/queue, registered like the reference consumer |
52
+ | scheduler / job framework | a scheduled job that queries the todo-list read model (or the trigger's persisted state) for pending items and fires the command per item |
53
+ | none of the above | the least invasive option consistent with the codebase: a direct call from the trigger's service method after it commits, or a scheduled job if the reaction must be decoupled/retried. Record the choice in `progress.txt` and in `ARCHITECTURE.md` so later automations reuse it |
54
+
55
+ Rules:
56
+ - Map trigger fields → command fields exactly as slice.json defines; no extra fields.
57
+ - The reaction must be **idempotent**: reprocessing the same trigger must not issue the command twice (check the resulting state, a processed-marker, or the todo-list query condition — whatever the codebase already uses).
58
+ - Follow the codebase's transaction boundaries: the command must not run inside a transaction that can still roll back the trigger unless the reference does exactly that.
59
+ - Never add a new messaging/scheduling library just for this slice.
60
+ - **Isolated and deletable**: the listener/consumer/job is its own class in the slice's own place (see *New slices — isolation*). Prefer subscribing to an existing event or querying for pending work over editing the trigger's service method; if a hook in that method is unavoidable (no event mechanism exists), keep it to a single call and list it under `Shared seams`.
61
+
62
+ ## Step 4 — Register it
63
+
64
+ Register the listener/consumer/job the same way the reference one is (annotation, DI registration, scheduler config, worker entry point). Do not touch application bootstrap beyond what that registration requires.
65
+
66
+ ## Step 5 — Write the tests
67
+
68
+ Besides the command tests from Step 2, write one test per `specifications[]` entry that exercises the **trigger**: set up the given state, fire the trigger the way it happens in production (publish the event, run the job once, call the consumer), and assert the command's effect — or that nothing happened when the spec expects no reaction. Follow the reference test style.
69
+
70
+ ## Step 6 — Quality gate
71
+
72
+ Run the build command and the single-test command from `ARCHITECTURE.md` for the tests this slice added or changed. Do not run the full suite.
73
+
74
+ ## Step 7 — Record the slice in the Slice map
75
+
76
+ Add (or update) this slice's row in the **Slice map** of `ARCHITECTURE.md`, with the trigger and every shared file it touched under `Shared seams`. Commit it with the slice.
77
+
78
+ ## Step 8 — Record the evidence on the board
79
+
80
+ After the commit, invoke `/record-evidence`: it writes this slice's element → code mapping, field mapping, specification → test mapping, files, seams, assumptions and commit as a markdown note into the slice's column (chapter lane `Implementation`). Only then is the slice set to `Done`.
81
+
82
+ ---
83
+
84
+ ## Checklist
85
+
86
+ - [ ] The command was built (or reused) via build-state-change, with all its specifications tested
87
+ - [ ] The trigger uses the codebase's existing reactive mechanism, registered like the precedent automation slice (or the reference)
88
+ - [ ] The automation is its own class; any hook into existing code is listed under `Shared seams` in its Slice map row
89
+ - [ ] Trigger → command field mapping matches slice.json exactly
90
+ - [ ] Reprocessing the same trigger does not issue the command twice
91
+ - [ ] Every specification has a passing test
92
+ - [ ] If `storylines[]` is present, its command segment is covered via build-state-change's storyline-derived tests
93
+ - [ ] No new framework, library or architectural style was introduced; any first-time mechanism choice is recorded in `ARCHITECTURE.md`
94
+ - [ ] The evidence note exists in the slice's column on the board (`/record-evidence`)
@@ -0,0 +1,114 @@
1
+ ---
2
+ name: build-state-change
3
+ description: Implements a state-change slice (command → validated state transition) inside an existing codebase, following the architecture recorded in .build-kit/ARCHITECTURE.md — typically a CRUD service method + endpoint + persistence, not necessarily event-sourced
4
+ ---
5
+
6
+ # Build State Change Slice
7
+
8
+ > Before doing anything else, read the slice definition from `.slices/{Context}/{slicename}/slice.json`. This file is the **source of truth** for all fields, events, and metadata — never invent fields not defined there.
9
+
10
+ > Then read `.build-kit/ARCHITECTURE.md` (run `/learn-architecture` if it is missing). It decides **how** the slice is built: which layers, which reference feature to copy, how events map onto persistence, how tests look, and which commands to run.
11
+
12
+ ---
13
+
14
+ ## What a State Change Slice is
15
+
16
+ A state-change slice accepts a command, checks it against the current state and the business rules in its specifications, and — if it is allowed — records the resulting state transition (the slice's event(s)).
17
+
18
+ In this codebase that usually means: an entry point (endpoint/handler/action) receives the command's fields, a service method validates them against the current persisted state, and the change is written through the existing persistence layer. Only if `ARCHITECTURE.md` says the codebase is event-based are the events materialized as event objects.
19
+
20
+ ---
21
+
22
+ ## Step 1 — Read the slice.json
23
+
24
+ From the slice definition, extract:
25
+ - **sliceName** — the slice title (names the command/use case)
26
+ - **context** — the bounded context (maps to the module/package/feature area named in `ARCHITECTURE.md`)
27
+ - **commands[]** — list of commands with their data fields
28
+ - **events[]** — list of events emitted by each command — their fields are the state the command must persist
29
+ - **specifications[]** — test scenarios (given/when/then)
30
+ - **storylines[]** (optional) — present only when the board author built an explicit walkthrough for this flow; most slices have none. See "Storyline-derived tests" under Step 5.
31
+
32
+ > **Comments & description**: each element carries a `comments: string[]` array (board comments) and a `description` field — use them as implementation hints, and resolve consumed comments via `POST <BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/<nodeId>/comments/<commentId>/resolve`.
33
+
34
+ ---
35
+
36
+ ## Step 2 — Find the precedent slices
37
+
38
+ This codebase is usually a legacy system whose event model was built from its code, so most neighbouring slices already exist in the code. Before writing anything, find out how they were built:
39
+
40
+ 1. **Is this slice itself already in the code?** Look it up in the **Slice map** of `ARCHITECTURE.md` and search for the command name, event names, the entity and the key fields (also under their code names from *Code vs. board terms*). If it exists — a slice that reappears as `Planned`, or one the analysis found but never marked `Done` — this skill is a **diff**: update the existing code to match slice.json field by field, add missing specifications, remove fields slice.json no longer has. Never dismiss a mismatch as harmless drift, and don't rebuild it next to the existing code.
41
+ 2. **Which existing slices are its neighbours?** From the Slice map and the context's `index.json`, pick the `Done` slices closest to this one — same context, same entity/aggregate, same events (e.g. the slice that produces this slice's `given` events, or another status transition on the same entity), same type. Read their `slice.json` and the code the map lists for them, **side by side**: that is exactly how a COMMAND, its EVENTs and its specifications from this board become code here. If no Done slice is close, fall back to the reference implementation in `ARCHITECTURE.md`.
42
+ 3. **What does the slice build on?** Events this slice reads in its `given`/specifications that other Done slices produce are already persisted somewhere — reuse that entity/table, never re-implement or duplicate it.
43
+
44
+ If a Done slice in this context has no row in the Slice map, run `/learn-architecture slices` first.
45
+
46
+ ## Step 3 — Map the slice the way its precedent is mapped
47
+
48
+ Take the precedent slice from Step 2 (or the **write** reference implementation from `ARCHITECTURE.md`) and build the same shape for this slice — same layers, naming, validation, error mapping and test style — placed as described in *New slices — isolation* of `ARCHITECTURE.md`:
49
+
50
+ | From slice.json | Becomes (per `ARCHITECTURE.md`) |
51
+ |---|---|
52
+ | command + its fields | input DTO/request type and the entry point that receives it, with the same validation style as the reference |
53
+ | business rules (from specifications whose `then` is an error, plus `description`/`comments`) | checks in the service/use case, failing the way the codebase fails (exception type, result type, error response) |
54
+ | event(s) + their fields | the persisted state change: entity fields/columns set, rows inserted/updated/deleted. In an event-based codebase: the event types, appended/published the existing way |
55
+ | event that represents a status transition (`...Approved`, `...Cancelled`) | the field/status column update the codebase uses for such transitions |
56
+
57
+ Rules:
58
+ - **Isolated and deletable**: the slice's code lives in its own files (its own package/folder where the layout allows — see *New slices — isolation*), not as new methods appended to an existing shared service, controller or repository. Shared files are touched only at the seams `ARCHITECTURE.md` names (registration, routing, a new migration), each as one small, recognisable change. No other code may start depending on this slice's internals; it reaches shared state through the existing entity/repository. Removing the slice should mean deleting its files and reverting its seams.
59
+ - Every command field, every event field ends up somewhere concrete — persisted, validated or returned. A field with no destination is a gap: record your assumption.
60
+ - No fields that are not in slice.json. Technical columns the codebase always adds (ids, timestamps, version, audit) are fine if every comparable entity has them.
61
+ - Schema changes go through the codebase's migration mechanism — add a new migration, never edit an existing one.
62
+ - Follow the codebase's transaction, auth and error-mapping conventions exactly as the reference does.
63
+
64
+ ## Step 4 — Expose it
65
+
66
+ Expose the command the way comparable writes are exposed (REST endpoint, RPC method, message handler, UI/server action, ...), including whatever registration, routing, API documentation or permission config the reference has. If comparable writes are not exposed externally (pure domain service), don't add an entry point.
67
+
68
+ ## Step 5 — Write the tests
69
+
70
+ Write one executable test per entry in `specifications[]`, in the test style and location of the reference test:
71
+
72
+ - **given** — each given event becomes pre-existing state: insert the equivalent rows via the codebase's fixtures/builders/repositories, or execute the earlier commands that produce that state if that is how the reference tests set up data. An empty `given` means a clean state.
73
+ - **when** — invoke the command through the same layer the reference tests use (service call, HTTP request, handler).
74
+ - **then** — for events: assert the persisted state (or the published events, in an event-based codebase) carries exactly the event fields and example values; for an error: assert the codebase's failure (exception, error result, status code).
75
+
76
+ Name each test after the specification title so it can be traced back to the board.
77
+
78
+ ### Storyline-derived tests (optional)
79
+
80
+ `storylines[]` in slice.json (optional — only present when the board author built an explicit walkthrough) can supply additional tests. A storyline embedded in this slice's slice.json already belongs entirely to this slice — no need to match beats against `commands[]` by id/title. Scan each storyline's ordered `elements` for a `type: COMMAND` beat followed by its EVENT beat(s):
81
+
82
+ - `given` — the cumulative ordered events from the start of the storyline up to (not including) the command beat, set up as state as above
83
+ - `when` — the command, built from that beat's `fields`
84
+ - `then` — the following EVENT beat(s)' data, asserted on the resulting state
85
+
86
+ Group these separately (own test class/`describe`/region, named after the storyline), on top of — never instead of — the specification tests.
87
+
88
+ ## Step 6 — Quality gate
89
+
90
+ Run the build command and the single-test command from `ARCHITECTURE.md` for the tests this slice added or changed. Do not run the full suite.
91
+
92
+ ## Step 7 — Record the slice in the Slice map
93
+
94
+ Add (or update) this slice's row in the **Slice map** of `ARCHITECTURE.md`: entry point, persistence, tests, and every shared file it touched under `Shared seams` — that column is the checklist for deleting the slice later. Commit it with the slice.
95
+
96
+ ## Step 8 — Record the evidence on the board
97
+
98
+ After the commit, invoke `/record-evidence`: it writes this slice's element → code mapping, field mapping, specification → test mapping, files, seams, assumptions and commit as a markdown note into the slice's column (chapter lane `Implementation`). Only then is the slice set to `Done`.
99
+
100
+ ---
101
+
102
+ ## Final Verification: Does the Implementation Match slice.json?
103
+
104
+ - [ ] Every command field is received and used (validated, persisted or passed on)
105
+ - [ ] Every event field is persisted (or published) with the expected value
106
+ - [ ] Every specification has exactly one corresponding, passing test
107
+ - [ ] If `storylines[]` is present, a storyline-derived test was added for every COMMAND beat
108
+ - [ ] No business rules, defaults, or constraints were added that do not appear in slice.json `description`, `comments` or specifications
109
+ - [ ] No field names were assumed or guessed — if a field is not in slice.json, it is not in the code
110
+ - [ ] The new code mirrors its precedent slice (or the reference implementation): layout, naming and conventions; no new framework, library or architectural style was introduced
111
+ - [ ] Data produced by existing slices was reused, not duplicated
112
+ - [ ] The slice lives in its own files; shared files were touched only at the seams in `ARCHITECTURE.md`, and its Slice map row lists them
113
+ - [ ] Assumptions are recorded in `progress.txt` and as code comments
114
+ - [ ] The evidence note exists in the slice's column on the board (`/record-evidence`)
@@ -0,0 +1,116 @@
1
+ ---
2
+ name: build-state-view
3
+ description: Implements a state-view slice (read model served from previously recorded state) inside an existing codebase, following the architecture recorded in .build-kit/ARCHITECTURE.md — typically a query + response DTO + read endpoint over existing tables, not necessarily a projection
4
+ ---
5
+
6
+ # Build State View Slice
7
+
8
+ > Before doing anything else, read the slice definition from `.slices/{Context}/{slicename}/slice.json`. This file is the **source of truth** for all fields, events, and metadata — never invent fields not defined there.
9
+
10
+ > Then read `.build-kit/ARCHITECTURE.md` (run `/learn-architecture` if it is missing). It decides **how** the slice is built: which layers, which reference feature to copy, where reads come from, how tests look, and which commands to run.
11
+
12
+ ---
13
+
14
+ ## What a State View Slice is
15
+
16
+ A state-view slice answers a question with data that earlier state changes recorded: the read model's fields are derived from the events that feed it.
17
+
18
+ In a CRUD codebase there is usually no projection to maintain — the events were persisted as rows by their state-change slices, so the read model is a **query** over those tables plus a response DTO and a read entry point. A dedicated table, view or projection is only justified when `ARCHITECTURE.md` says the codebase uses them, or the read genuinely can't be answered from existing data.
19
+
20
+ ---
21
+
22
+ ## Step 1 — Read the slice.json
23
+
24
+ From the slice definition, extract:
25
+ - **sliceName** — the slice title (names the query/read model)
26
+ - **context** — the bounded context (maps to the module/package/feature area named in `ARCHITECTURE.md`)
27
+ - **readmodels[]** / **projections[]** / **queries[]** — the read model(s) this slice serves, with their fields
28
+ - the **events** feeding each read model — they tell you which persisted state the fields come from
29
+ - **specifications[]** — test scenarios (given events → expected read model)
30
+ - **storylines[]** (optional) — present only when the board author built an explicit walkthrough for this flow; most slices have none. See "Storyline-derived tests" under Step 6.
31
+
32
+ > **Comments & description**: each element carries a `comments: string[]` array (board comments) and a `description` field — use them as implementation hints, and resolve consumed comments via `POST <BASE_URL>/api/org/<ORG_ID>/boards/<BOARD_ID>/nodes/<nodeId>/comments/<commentId>/resolve`.
33
+
34
+ ---
35
+
36
+ ## Step 2 — Find the precedent slices
37
+
38
+ This codebase is usually a legacy system whose event model was built from its code, so most neighbouring slices already exist in the code. Before writing anything:
39
+
40
+ 1. **Is this read already in the code?** Look the slice up in the **Slice map** of `ARCHITECTURE.md` and search for the read model name, its fields and the endpoint (also under the code names from *Code vs. board terms*). If it exists, this skill is a **diff** (see Step 3) — don't build a second query next to it.
41
+ 2. **Which existing slices are its neighbours?** Pick the closest `Done` slices — the state-change slices that produce this read model's events (they show where the data is persisted), and another state-view slice in the same context or over the same entity (it shows how reads are built here). Read their `slice.json` and the code the map lists for them side by side. If no Done state-view is close, fall back to the read reference implementation in `ARCHITECTURE.md`.
42
+
43
+ If a Done slice in this context has no row in the Slice map, run `/learn-architecture slices` first.
44
+
45
+ ## Step 3 — Locate the source data
46
+
47
+ For every read model field, find where the feeding event's data is persisted (the entity/table/column written by that event's state-change slice). Search by event name, entity and field names.
48
+
49
+ - **Found** → the field is read from there.
50
+ - **Not persisted anywhere** (the state-change slice isn't built yet, or never stored that field) → do not invent a parallel store. Add the missing column/entity the way the reference does (new migration) only if it clearly belongs to this read; otherwise record the gap as an assumption in `progress.txt` and as a code comment.
51
+
52
+ If the slice reappears as `Planned` and a query for it already exists, treat this skill as a **diff** against slice.json: add missing fields, remove fields slice.json no longer has, add missing specifications.
53
+
54
+ ## Step 4 — Map the slice the way its precedent is mapped
55
+
56
+ Take the precedent state-view slice from Step 2 (or the **read** reference implementation from `ARCHITECTURE.md`) and build the same shape, with the same naming, placed as described in *New slices — isolation* of `ARCHITECTURE.md` — its own query class/DTO/endpoint files (own package/folder where the layout allows), not new methods appended to a shared repository or controller when a separate class works just as well. Shared files are touched only at the seams `ARCHITECTURE.md` names, and the query reads existing tables without changing them for other readers:
57
+
58
+ | From slice.json | Becomes (per `ARCHITECTURE.md`) |
59
+ |---|---|
60
+ | read model + its fields | response DTO with exactly these fields |
61
+ | how events shape the read model (list vs. single item, filters, status mapping, latest-wins) | the query: joins, filters, ordering, status derivation — in the codebase's query style (repository method, ORM query, SQL, ...) |
62
+ | key/id fields and how the read is addressed | query parameters / path variables, matching how comparable reads are addressed |
63
+ | list-shaped read models | pagination/sorting the way comparable list endpoints do it — only if they do |
64
+
65
+ In an event-based codebase (per `ARCHITECTURE.md`), build the projection/read-model updater the existing way instead and register it like the reference does.
66
+
67
+ ## Step 5 — Expose it
68
+
69
+ Expose the read the way comparable reads are exposed (REST endpoint, RPC, UI loader, ...), including registration, routing, API documentation and permission config, mirroring the reference. If the read is only consumed internally (e.g. by an automation's todo list), a service/repository method is enough.
70
+
71
+ ## Step 6 — Write the tests
72
+
73
+ Write one executable test per entry in `specifications[]`, in the test style and location of the reference test:
74
+
75
+ - **given** — the given events become pre-existing state: insert the equivalent rows via the codebase's fixtures/builders/repositories, or run the commands that produce them if that's how the reference tests set up data.
76
+ - **when** — run the query through the same layer the reference tests use.
77
+ - **then** — assert the returned read model matches the expected fields and example values exactly (including an empty result where the spec expects one).
78
+
79
+ Name each test after the specification title.
80
+
81
+ ### Storyline-derived tests (optional)
82
+
83
+ `storylines[]` in slice.json is optional — present only when the board author explicitly built a walkthrough for this flow. When present, mine it for **additional** tests, on top of (never instead of) the specification tests.
84
+
85
+ A storyline is `{ id, title, elements: [...] }`, where `elements` is an ordered list of beats — the same element can repeat to show its state at different points in the flow. A storyline embedded in this slice's slice.json already belongs entirely to this slice — no need to match beats against `readmodels[]` by id/title. Scan for a pair of adjacent beats that are both `type: READMODEL`, with only EVENT beat(s) between them and no COMMAND beat in that run. That pair is one self-contained test:
86
+
87
+ - `given` — the cumulative ordered events from the start of the storyline through the intervening event beat(s), set up as state
88
+ - `then` — the second READMODEL beat's fields
89
+
90
+ Group these separately, named after the storyline. Skip a beat pair when a COMMAND beat sits in between (that half belongs to build-state-change) or when the run includes a SCREEN/AUTOMATION beat with no traceable event — a one-line comment noting the storyline segment exists is enough.
91
+
92
+ ## Step 7 — Quality gate
93
+
94
+ Run the build command and the single-test command from `ARCHITECTURE.md` for the tests this slice added or changed. Do not run the full suite.
95
+
96
+ ## Step 8 — Record the slice in the Slice map
97
+
98
+ Add (or update) this slice's row in the **Slice map** of `ARCHITECTURE.md`, including every shared file it touched under `Shared seams`. Commit it with the slice.
99
+
100
+ ## Step 9 — Record the evidence on the board
101
+
102
+ After the commit, invoke `/record-evidence`: it writes this slice's element → code mapping, field mapping, specification → test mapping, files, seams, assumptions and commit as a markdown note into the slice's column (chapter lane `Implementation`). Only then is the slice set to `Done`.
103
+
104
+ ---
105
+
106
+ ## Checklist
107
+
108
+ - [ ] Every read model field is returned, sourced from where its feeding event's data is persisted
109
+ - [ ] No extra fields in the response that are not in slice.json
110
+ - [ ] No parallel data store was invented for data that already exists
111
+ - [ ] Every specification has exactly one corresponding, passing test
112
+ - [ ] If `storylines[]` is present, a storyline-derived test was added for every isolable read-model transition (adjacent READMODEL beats with only EVENT beats between them)
113
+ - [ ] The new code mirrors its precedent slice (or the reference implementation): layout, naming and conventions; no new framework, library or architectural style was introduced
114
+ - [ ] The slice lives in its own files; shared files were touched only at the seams in `ARCHITECTURE.md`, and its Slice map row lists them
115
+ - [ ] Assumptions and data gaps are recorded in `progress.txt` and as code comments
116
+ - [ ] The evidence note exists in the slice's column on the board (`/record-evidence`)
@@ -0,0 +1,157 @@
1
+ ---
2
+ name: learn-architecture
3
+ description: Analyzes the existing codebase once and writes .build-kit/ARCHITECTURE.md — layers, feature layout, persistence, how writes/reads/background work are built, test style, build/test commands, and the slice map — which code every existing (Done) slice on the board corresponds to, and the mapping pattern they share. Run before the first slice is built, when ARCHITECTURE.md is missing or stale, or with `slices` when Done slices are missing from the slice map.
4
+ ---
5
+
6
+ # Learn Architecture
7
+
8
+ The classic build kit implements slices inside a codebase it did not create. Before any slice is built, the agent has to know how *this* codebase builds a feature — otherwise every slice drifts toward a generic pattern. This skill finds that out once and writes it down in `.build-kit/ARCHITECTURE.md`, which every `build-*` skill reads instead of re-exploring.
9
+
10
+ **The typical situation is a legacy system.** Someone ran a code base analysis (`analyze-code-base`), built the event model from the existing code, and now extends it. Most slices on the board therefore *already exist in the code* — usually in status `Done` — and their elements' `description`s carry code references (`path/to/File.java:57`, `Class#method`, tables, endpoints). Those slices are the most valuable input this skill has: each one is a worked example of how a COMMAND, EVENT, READMODEL or AUTOMATION from *this* board is realized in *this* code. The mapping is learned from them first, and from generic code reading only where no slice covers it.
11
+
12
+ **Arguments** (optional):
13
+ - `refresh` — re-analyze and rewrite `ARCHITECTURE.md` even if it exists.
14
+ - `slices` — keep everything else, only bring the **Slice map** up to date (Steps 2–3, then only the **Slice map** and *Code vs. board terms* sections in Step 6): add Done slices that are missing from it, re-check rows whose code moved.
15
+
16
+ If `.build-kit/ARCHITECTURE.md` exists and neither argument was passed: read it, then compare its **Slice map** against the `Done` slices in the local `index.json` files. If every Done slice has a row, return. Otherwise run as `slices`.
17
+
18
+ ---
19
+
20
+ ## Step 1 — Identify the stack
21
+
22
+ Read the build/dependency files at the project root and one level down (`package.json`, `pom.xml`, `build.gradle*`, `*.csproj`/`*.sln`, `pyproject.toml`, `go.mod`, `Gemfile`, `composer.json`, `Cargo.toml`, ...), plus `README*`, any existing `CLAUDE.md`/`AGENTS.md`/`CONTRIBUTING.md`, `docker-compose*`, and CI config (`.github/workflows/*`, `.gitlab-ci.yml`, ...).
23
+
24
+ Record: language + version, framework(s), persistence (database, ORM/query layer, migration tool), messaging if any, test framework(s), and the exact commands to build, run all tests, and run a **single** test file/class. Prefer what CI actually runs over what a README claims. Do not guess a command — if one can't be determined, say so in `ARCHITECTURE.md`.
25
+
26
+ ## Step 2 — Collect the existing slices
27
+
28
+ Make sure the slice files are present (`/load-slice` without arguments refreshes all of them), then read every `.build-kit/.slices/*/index.json`. This is read-only reference work, so it covers **all** contexts, not only the current one.
29
+
30
+ Every slice with status `Done` is already implemented — treat it as a precedent. Slices in other statuses (`Created`, `Planned`, ...) may also carry code references from the analysis (the code exists, the slice was just never marked Done); include them when their descriptions point at code. Sort each slice by type the way `CLAUDE.md` *Building a Slice* does (state-change / state-view / automation / translation).
31
+
32
+ For each such slice, read its `slice.json` and collect the code references from:
33
+ - the `description` (and `comments`) of the slice and of every command, event, read model, screen and processor — paths with line numbers, `Class#method`, tables, endpoints, tests, templates;
34
+ - if the board is reachable and the chapter has them: the `Legacy Sources — <Chapter>` note (*Where this flow lives*) and the `Decisions — <column>` notes in the chapter's **Decisions** lane (`get_nodes { boardId, type: "MARKDOWN", projection: "line" }` for the ids, then read only the notes of chapters you need). They map elements to code with exact references and record code-vs-board term differences (`PetType` → *Species*).
35
+
36
+ A slice without any reference is located by search instead (Step 3).
37
+
38
+ ## Step 3 — Map each existing slice onto its code
39
+
40
+ For every slice from Step 2, find the code that implements it. Open the referenced files (or search for the command/event/read model names, their fields, and the code terms from the Decisions notes) and record, per slice:
41
+
42
+ - **COMMAND** → entry point (endpoint/handler/action) + input DTO + service method
43
+ - **EVENT** → what it writes: entity/table + columns, status column + value, or the published event class
44
+ - **READMODEL** → query/repository method + response DTO + endpoint
45
+ - **AUTOMATION / processor** → listener/consumer/job + what it calls
46
+ - **Specifications** → the test class/method that covers them, if any
47
+
48
+ Verify, don't copy: a line number may be stale, a file may have moved. Fix the reference in the map; if a slice's code can't be found at all, record it as `not found` — never treat it as a feature to build, and never invent a location.
49
+
50
+ Then **look for the pattern across slices**. The same board construct is usually realized the same way every time — e.g. every `... Registered` event is an insert into the aggregate's table, every `... Approved/Cancelled` event is an update of a `status` column, every read model is a repository method + DTO in the same package, every automation is a `@Scheduled` job. That shared pattern *is* the Event Modeling mapping of this codebase (Step 5). Where slices are realized differently (two persistence styles, an old and a new module), record both and which slices use which, so a new slice can follow its nearest neighbour.
51
+
52
+ Do not read the whole codebase — follow the references.
53
+
54
+ ## Step 4 — Fill the gaps with reference features
55
+
56
+ Only for what the existing slices don't cover (no Done slices at all, or e.g. no automation among them) pick **one or two existing features** that look like the slices the board will produce — something that accepts input and changes state, something that only reads/lists data. A recently changed, ordinary feature is a better reference than the oldest or the most special one (`git log --stat -20` helps). When existing slices cover a path, the best-mapped Done slice of that type *is* the reference implementation.
57
+
58
+ Trace each one end to end, reading only the files on that path:
59
+
60
+ - **Write path**: entry point (controller/route/handler/form action) → input DTO + validation → service/use case → domain object/entity → repository/DAO → schema/migration → response and error mapping.
61
+ - **Read path**: entry point → query/repository → response DTO → pagination/filtering conventions.
62
+ - **Background/reactive work**: anything that reacts to a state change — event listeners, after-commit hooks, message consumers, schedulers/cron jobs, outbox. Note if there is none.
63
+ - **Tests**: where they live, unit vs. integration, how state is set up (fixtures, builders, factories, test containers, in-memory DB, mocks), how assertions are written.
64
+
65
+ Do not read the whole codebase. Search (`grep`/glob) for a concept, then follow references.
66
+
67
+ ## Step 5 — Decide the Event Modeling mapping
68
+
69
+ Decide how each board element is built **in this codebase**, based on what the existing slices show (Step 3) and, only where they say nothing, on the reference features (Step 4) — not on Event Modeling theory:
70
+
71
+ - **Is it event-based?** Only if the code already persists or publishes events (event store, domain events, outbox, audit log of facts). Otherwise it is state-based (typically CRUD) and board EVENTs become the state transitions a command persists, not new event classes.
72
+ - **COMMAND** → which entry point + DTO + service method shape, and where business-rule violations go (exception type, result type, validation error).
73
+ - **EVENT** → the persisted change (tables/columns written), or the existing event mechanism if there is one.
74
+ - **READMODEL** → query + response DTO; when a new table/view/materialized projection is justified versus querying existing tables.
75
+ - **AUTOMATION / processor** → the existing reactive mechanism (or, if none exists, the least invasive option consistent with the codebase — usually a direct call or a scheduled job; note that choice explicitly).
76
+ - **Specification (given/when/then)** → how `given` events become pre-existing state in a test, and what `then` asserts on.
77
+
78
+ Then decide **where a new slice goes so it stays isolated and deletable**. Existing slices show how the code *is* built; new slices follow those conventions but are placed as self-contained as the codebase allows — ideally, removing a slice means deleting its own files plus undoing a short, known list of registrations. Settle, from what the codebase tolerates:
79
+
80
+ - **Own unit per slice** — the most local place the layout allows: a package/folder per slice inside the context's module (best), otherwise own files (controller, DTOs, service/handler, query, test) next to the comparable ones — never new methods appended to an existing shared service, controller or repository when a separate class registered the same way works just as well.
81
+ - **Shared seams** — the unavoidable touch points (router/DI registration, a menu entry, a schema migration, an existing entity that must gain a column) and how to keep each one to a single, small, recognisable change.
82
+ - **Dependencies between slices** — a new slice reads shared state through the existing entity/repository (or the codebase's query style), never by calling another slice's internals; no other slice depends on the new one.
83
+ - **Data** — new state goes into a new table or new columns via a new migration; existing columns are not repurposed.
84
+
85
+ Where the codebase genuinely forces a shared file (one big controller per aggregate, a central route file), follow it — and record that the slice touches it.
86
+
87
+ ## Step 6 — Write `.build-kit/ARCHITECTURE.md`
88
+
89
+ Keep it short and concrete — paths, class names and commands, not prose. Use this structure:
90
+
91
+ ```markdown
92
+ # Architecture
93
+
94
+ ## Stack
95
+ - Language/framework/persistence/test framework (with versions)
96
+ - Architecture style: e.g. layered CRUD (controller → service → JPA repository), or event-sourced, or ...
97
+
98
+ ## Commands
99
+ - Build: `...`
100
+ - All tests: `...`
101
+ - Single test: `...` (with a concrete example)
102
+ - Other checks CI runs (lint, format, typecheck): `...`
103
+
104
+ ## Where a feature lives
105
+ - Layout of one feature, with the reference feature as a concrete tree
106
+ - Naming conventions (classes, files, endpoints, tables, columns)
107
+
108
+ ## Reference implementations
109
+ Prefer a mapped Done slice per type; a plain feature only where no slice covers the type.
110
+ - Write: slice *Register Owner* — `path/to/Controller` → `path/to/Service` → `path/to/Repository` (+ migration)
111
+ - Read: ...
112
+ - Background: ... (or "none")
113
+ - Tests: `path/to/ReferenceTest` — how state is set up and asserted
114
+
115
+ ## Event Modeling mapping
116
+ Derived from the slices in the Slice map — cite the slices that show each row.
117
+ | Board element | Built as | Seen in |
118
+ |---|---|---|
119
+ | COMMAND | ... | *Register Owner*, *Book Visit* |
120
+ | EVENT (creation) | ... | ... |
121
+ | EVENT (status transition) | ... | ... |
122
+ | READMODEL | ... | ... |
123
+ | AUTOMATION | ... | ... |
124
+ | Specification | ... | ... |
125
+
126
+ ## Code vs. board terms
127
+ | Code | Board |
128
+ |---|---|
129
+ | `PetType` | *Species* |
130
+
131
+ ## Slice map
132
+ Every slice that exists in the code. One row per slice; paths relative to the repo root. `Shared seams` lists the shared files a slice changed — for slices built by the kit it is the deletion checklist.
133
+ | Slice | Context | Type | Status | Command / entry point | Persistence (events) | Read / query | Trigger | Tests | Shared seams |
134
+ |---|---|---|---|---|---|---|---|---|---|
135
+ | Register Owner | owners | state-change | Done | `OwnerController#processCreationForm` → `OwnerService#register` | `owners` insert | — | — | `OwnerControllerTests` | legacy |
136
+ | Owner Details | owners | state-view | Done | `GET /owners/{id}` | — | `OwnerRepository#findById` → `OwnerDto` | — | ... | legacy |
137
+ | Remind Visit | visits | automation | Done | `VisitService#remind` | `visits.reminded_at` | — | `VisitReminderJob` (`@Scheduled`) | ... | legacy |
138
+
139
+ ## New slices — isolation
140
+ - Placement: e.g. `src/main/java/org/acme/<context>/<slicename>/` with `<Slice>Controller`, `<Slice>Request`, `<Slice>Handler`, `<Slice>Test`
141
+ - Shared seams (the only shared files a slice may touch): `RouteConfig.java` (one line), `db/migration/V<n>__<slice>.sql` (new file), ...
142
+ - Codebase constraints that force sharing, and what to do then
143
+
144
+ ## Rules
145
+ - Conventions a new slice must follow (transactions, validation, error mapping, auth, migrations append-only, ...)
146
+ - Files a slice must not touch (app bootstrap, global config, lockfiles, ...)
147
+ ```
148
+
149
+ ## Step 7 — Verify the commands
150
+
151
+ Run the build command and the single-test command against one existing test. If either fails for reasons unrelated to the code (missing service, missing env var), record the prerequisite under **Commands**. Do not "fix" the project to make it pass.
152
+
153
+ Commit `ARCHITECTURE.md` on its own: `chore: document architecture for build kit` (with `slices`: `chore: update slice map`).
154
+
155
+ ## Keeping the slice map current
156
+
157
+ The `build-*` skills add a row for every slice they build, so the map grows with the code. When a later code base analysis puts new Done slices on the board, the next build notices the missing rows and runs this skill with `slices` — only those slices are mapped, the rest of `ARCHITECTURE.md` stays as it is.