@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 +12 -0
- package/cli.js +11 -0
- package/package.json +1 -1
- package/shared/skills/learn-eventmodelers-api/SKILL.md +2 -0
- package/stacks/classic/templates/.claude/skills/build-automation/SKILL.md +94 -0
- package/stacks/classic/templates/.claude/skills/build-state-change/SKILL.md +114 -0
- package/stacks/classic/templates/.claude/skills/build-state-view/SKILL.md +116 -0
- package/stacks/classic/templates/.claude/skills/learn-architecture/SKILL.md +157 -0
- package/stacks/classic/templates/.claude/skills/record-evidence/SKILL.md +177 -0
- package/stacks/classic/templates/build-kit/CLAUDE.md +69 -0
- package/stacks/classic/templates/build-kit/lib/AGENT.md +47 -0
- package/stacks/classic/templates/build-kit/lib/backend-prompt.md +138 -0
- package/stacks/classic/templates/build-kit/lib/prompt.md +132 -0
- package/stacks/modeling-kit/templates/.claude/skills/detect-model-drift/SKILL.md +123 -84
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md +10 -6
- package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-orchestrating-event-modeling/SKILL.md +2 -2
- package/stacks/modeling-kit/templates/.claude/skills/place-element/SKILL.md +1 -1
- package/stacks/modeling-kit/templates/kit/CLAUDE.md +1 -1
|
@@ -1,21 +1,29 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: detect-model-drift
|
|
3
|
-
description:
|
|
3
|
+
description: Find where the code has moved away from the event model since the last analysis — reads the baseline commit from each chapter's first-column note (Analysis history), takes every commit since then as the changes made, and reports how the changed code now differs from the board (fields, specifications/tests, screens, edges, elements, slices). Changes on the board are out of scope — the model leads, the code follows. `--branch <name>` checks that branch's history instead of the current one. Read-only; every finding comes with a suggested fix the user confirms first.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Detect Model Drift
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
**Drift is a change in the code that the model doesn't reflect.** People model first and then have an agent build it, so a difference that comes from the board (a field renamed on the board, a new slice not built yet) is planned work, not drift, and this skill ignores it. What it looks for is the other direction: someone changed the code by hand (a manual commit, a hotfix, a refactoring) and the board no longer describes what the code does.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
So the skill looks at **the code changes since the last analysis**, nothing else:
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
1. the baseline is the commit of the last row in the chapter note's *Analysis history* (first column of the chapter),
|
|
13
|
+
2. every commit since then (plus uncommitted changes) is a change that was made,
|
|
14
|
+
3. for each change, compare what the code now does with what the board says. The differences are the drift.
|
|
13
15
|
|
|
14
|
-
|
|
16
|
+
## Step 0 — Is there a code base with history?
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
**Before anything else — before `connect`, before reading the board — resolve the directory to check**: `$EVENTMODELERS_CODE_DIR` if it is set (the agent runs from the global install and was started inside that directory — treat it as the repository root for every step below, not the current directory), otherwise the current directory.
|
|
17
19
|
|
|
18
|
-
|
|
20
|
+
- It must contain source code (e.g. `src/`, `.build-kit/`, a `package.json`, `pom.xml`, `build.gradle`, `*.csproj`, or similar). If it does not (an empty folder, a modeling-only workspace holding just `.claude/`, `.eventmodelers/` or `.agent-modeling-kit/`), **stop and answer in one or two plain sentences**, e.g.:
|
|
21
|
+
> Model drift can't be detected here — there is no code to compare the board against. Run this in the code base that implements the model.
|
|
22
|
+
- It must be a git repository (`git rev-parse --is-inside-work-tree`). If it is not, stop the same way: *"Drift is detected from the git history since the last analysis, and this code base has no git history. Run `/analyze-code-base` for a full analysis instead."*
|
|
23
|
+
|
|
24
|
+
Do not connect, do not read the board, and do not produce a report when either check fails.
|
|
25
|
+
|
|
26
|
+
**With `--branch <name>`** (see Step 1), check that the branch exists: `git rev-parse --verify <name>`, else `git fetch origin <name>` and `git rev-parse --verify origin/<name>`. Use whichever resolves as `REF`. If neither does, stop in one sentence (*"There is no branch `<name>` in this repository."*). Without `--branch`, `REF` is `HEAD`, and uncommitted changes in the working tree count too.
|
|
19
27
|
|
|
20
28
|
---
|
|
21
29
|
|
|
@@ -23,7 +31,7 @@ Do not connect, do not read the board, do not produce a report or snippet, and d
|
|
|
23
31
|
|
|
24
32
|
Prefer `mcp__eventmodelers__*` tools when available. See `learn-eventmodelers-api` for the curl fallback.
|
|
25
33
|
|
|
26
|
-
|
|
34
|
+
**This skill never changes the code**, and it changes the board only for what the person confirms — plus the one *Analysis history* row of Step 7.
|
|
27
35
|
|
|
28
36
|
---
|
|
29
37
|
|
|
@@ -33,158 +41,189 @@ From `$ARGUMENTS` and the prompt context, extract:
|
|
|
33
41
|
|
|
34
42
|
| Field | How to find it | Default |
|
|
35
43
|
|-------|---------------|---------|
|
|
36
|
-
| `timelineId` | `context.timelineId` of the prompt (overrules the prompt's own `timeline_id`), or a timeline/chapter named in the text |
|
|
44
|
+
| `timelineId` | `context.timelineId` of the prompt (overrules the prompt's own `timeline_id`), or a timeline/chapter named in the text | every chapter on the board |
|
|
37
45
|
| `contextName` | a bounded context named in the text | all contexts |
|
|
38
46
|
| `sliceTitle` | a single slice named in the text | all slices in scope |
|
|
39
|
-
| `branch` | `--branch <name>` in `$ARGUMENTS`, or a branch named in the text | the current branch
|
|
40
|
-
|
|
41
|
-
**If a timeline is in focus, only that timeline's slices are compared** — both on the board side and, through the slice → code mapping of Step 3, on the code side. Code that belongs to no slice in scope is not reported as "additional" in a scoped run; say so in the report footer instead of guessing.
|
|
47
|
+
| `branch` | `--branch <name>` in `$ARGUMENTS`, or a branch named in the text | the current branch (`HEAD` + working tree) |
|
|
48
|
+
| `since` | `--since <commit>` in `$ARGUMENTS`, or a commit named in the text | the baseline of Step 2 |
|
|
42
49
|
|
|
43
50
|
---
|
|
44
51
|
|
|
45
|
-
## Step 2 —
|
|
52
|
+
## Step 2 — Find the baseline (per chapter)
|
|
46
53
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
mcp__eventmodelers__list_slices { "boardId": "$BOARD_ID" }
|
|
51
|
-
mcp__eventmodelers__get_slice_data { "boardId": "$BOARD_ID", "contextName": "<CONTEXT>", "format": "toon" }
|
|
52
|
-
```
|
|
54
|
+
Every chapter carries its own baseline. For each chapter in scope:
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
1. `get_board_outline { boardId, chapterId }` and take the **first column's** MARKDOWN node in a `feedback` lane — the chapter note (`Legacy Sources — <Chapter>` when `/analyze-code-base` wrote it). Read it with `get_node` → `meta.description`.
|
|
57
|
+
2. Find its `## Analysis history` table and take the **Commit** of the **last row**. Strip a `+dirty` suffix (and note it: the files dirty back then are changed files too). Save it as `BASE`.
|
|
58
|
+
3. Check it: `git cat-file -e <BASE>^{commit}` and `git merge-base --is-ancestor <BASE> <REF>` — the baseline must be in this branch's history.
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
The rest of the note is context for judging findings too — *Where this flow lives* says which folders belong to the chapter, *Chapter decisions* and *Not modeled* say what is deliberately different or left out.
|
|
57
61
|
|
|
58
|
-
**
|
|
62
|
+
**No usable baseline** — no first-column note, no *Analysis history*, `no git`, or a hash that is unreachable or not an ancestor (rebased/squashed history): **don't fall back to comparing the whole board against the whole code.** Ask once which commit to start from, offering the most likely one: the newest commit recorded anywhere on the board for that chapter (an `Implementation — <Slice>` evidence note's **Commit**, a column note), or else the last few entries of `git log --oneline -10 <REF>`. A `since` argument answers this up front and overrules the note's baseline.
|
|
59
63
|
|
|
60
|
-
|
|
64
|
+
Chapters with the same baseline share one git read in Step 3.
|
|
61
65
|
|
|
62
66
|
---
|
|
63
67
|
|
|
64
|
-
## Step 3 —
|
|
68
|
+
## Step 3 — Collect the changes since the baseline
|
|
65
69
|
|
|
66
|
-
|
|
70
|
+
For each distinct `BASE`, list the commits and walk them **one by one, oldest first** — never judge from the net diff `<BASE>..<REF>` alone:
|
|
67
71
|
|
|
68
|
-
|
|
72
|
+
```bash
|
|
73
|
+
git log --reverse --format='%h %ad %an %s' --date=short <BASE>..<REF> # the commits, oldest first
|
|
74
|
+
git show --name-status --format= <sha> # the files one commit touched
|
|
75
|
+
git show <sha> -- <file> # that commit's change to one file
|
|
76
|
+
git diff <sha>..<REF> -- <file> # what happened to a file after a given commit
|
|
77
|
+
git status --porcelain && git diff HEAD # without --branch: uncommitted changes too
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Why per commit: when the range contains the commit that **created** a file (e.g. the build kit's `feat: <Slice>`), the net diff shows the file only as "added" with its final content, and a later hand edit (a renamed field, a removed check) disappears inside it — attributed to the build. Walking the commits keeps them apart. Never `git checkout`/`git switch` — read other refs with `git show <REF>:<path>`; the working tree may hold someone's uncommitted work.
|
|
69
81
|
|
|
70
|
-
|
|
71
|
-
2. Without a build kit, infer the layout from the repo (folders named after slices, `*Command*`, `*Event*`, projection/read-model files, route/UI files). If you cannot find any slice structure, stop and tell the user what you looked at — do not fabricate a mapping.
|
|
82
|
+
**Sort per file, not per commit.** One commit can mix kit or tooling files with real code (e.g. a commit that installs a kit *and* renames a field) — the noise files of that commit are dropped, its code files still count:
|
|
72
83
|
|
|
73
|
-
|
|
84
|
+
| Group | Examples | Handling |
|
|
85
|
+
|-------|----------|----------|
|
|
86
|
+
| **Behavior** | controllers/handlers, services, entities/aggregates, events, projections/queries, schema & migrations, validation, templates/UI, tests | compared in Step 5 |
|
|
87
|
+
| **Noise** | dependency bumps, build/CI config, gradle/maven wrappers, formatting-only diffs, docs, `.build-kit/`, `.claude/`, `.agent-modeling-kit/`, `.eventmodelers/`, `.mcp.json`, `CLAUDE.md`, `progress.txt`, generated files | listed in the report footer as *not relevant*, never a finding |
|
|
74
88
|
|
|
75
|
-
|
|
89
|
+
**Build-kit commits vs. hand-made commits.** A commit is the build kit's only if an `Implementation — <Slice>` evidence note names its sha, or — without evidence notes — its subject is `feat: <Slice Name>` for a slice on the board. It implemented the board, so its files are the **as-built state**: compare it cheaply against that slice only. Every other commit — including any commit after it that touches the same files — is a **hand-made change** and the main subject of this skill: for each file the build kit wrote, the drift candidates are `git diff <buildSha>..<REF> -- <file>`, attributed to the later commits.
|
|
76
90
|
|
|
77
|
-
|
|
91
|
+
No behavior changes since the baseline → report *"No code changes since `<BASE>` (<date of that row>) that affect the model"*, do Step 7, and stop.
|
|
78
92
|
|
|
79
93
|
---
|
|
80
94
|
|
|
81
|
-
## Step 4 —
|
|
95
|
+
## Step 4 — Map the changes to the board
|
|
82
96
|
|
|
83
|
-
|
|
97
|
+
For every behavior change, find the slice(s) and element(s) it belongs to. Use, in this order:
|
|
84
98
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
| **Fields** | On every COMMAND, EVENT and READMODEL: a field only in the model, a field only in the code, a name that differs (case or typo), a type that differs, `idAttribute` vs `@TargetEntityId`/event tag/aggregate key mismatch, optional vs required. |
|
|
90
|
-
| **Specifications** | A model GWT scenario with no test asserting it; a test (given/when/then) with no matching model scenario; a scenario whose events/fields no longer match the code. Storylines are narrative — compare their beats only for elements that no longer exist. |
|
|
91
|
-
| **Screens** | A SCREEN on the board with no UI counterpart; a UI screen/route/component wired to a slice's command or read model that the board doesn't show; a screen whose displayed read-model fields or submitted command fields differ from the model's dependencies. |
|
|
92
|
-
| **Edges** | Model dependency with no code counterpart, and the reverse: command → event (handler emits it), event → read model (projection consumes it), read model → screen, event → automation → command. A handler that emits an event the model doesn't connect, or a projection subscribing to events the model doesn't wire in. |
|
|
93
|
-
| **Elements** | An event/command/read model present on one side only, or a rename (same fields, different name). |
|
|
99
|
+
1. **`Implementation — <Slice>` evidence notes** (build kit, `Implementation` feedback lane, one per slice column): their *Element → Code*, *Fields*, *Specifications → Tests* and *Files* tables map code back to `ref:<nodeId>`s directly.
|
|
100
|
+
2. **`.build-kit/ARCHITECTURE.md` → Slice map**, if present: entry point, persistence, read/query, tests and shared seams per slice.
|
|
101
|
+
3. **The chapter note's** *Where this flow lives*, and the column decision notes' `Source` references (paths with line numbers).
|
|
102
|
+
4. Names: a command/event/read model/field whose name (or code name) appears in the changed code.
|
|
94
103
|
|
|
95
|
-
|
|
104
|
+
Then read the model **only for the slices you hit**: `get_slice_data { boardId, contextName, sliceId?, format: "toon" }` (full data: fields with types and `idAttribute`, dependencies, specifications, screens), and `get_node` for single elements. Don't read the rest of the board.
|
|
96
105
|
|
|
97
|
-
|
|
98
|
-
- **Direction matters.** A mismatch with no earlier evidence has no "right" side: propose the fix in the direction the evidence points (see Step 5), and say when it is a judgement call.
|
|
99
|
-
- Don't report framework noise: generated ids, metadata/envelope fields, serialization annotations, test fixtures' helper types, routes when the slice has no screen.
|
|
100
|
-
- Don't flag what is clearly intentional (an internal slice with no screen; a read model used only by another read model), or what the chapter's first-column feedback note (Step 2) describes as intended.
|
|
101
|
-
- Report each underlying difference **once**, at the element where it starts; a renamed field shows up on the command and the event and the read model — that is one finding listing the chain, not three.
|
|
106
|
+
A change that maps to no slice is itself a candidate finding — new behavior the board doesn't have (Step 5, *Element* / *Slice*). If it falls inside a chapter's *Where this flow lives* folders, it belongs to that chapter; otherwise say which chapter it most likely belongs to, or that it needs a new one.
|
|
102
107
|
|
|
103
108
|
---
|
|
104
109
|
|
|
105
|
-
## Step 5 —
|
|
110
|
+
## Step 5 — Compare the changed code with the board
|
|
106
111
|
|
|
107
|
-
|
|
112
|
+
Go change by change. Read the code **as it is at `REF`** (not just the diff hunk) for the elements the change touches, and compare it with what the board says about those elements. Every finding records: **kind**, **commit(s)** (short sha + subject + author), **file(s)** with line, **slice**, **element** (board `nodeId` where there is one), **what the board says**, **what the code now does**.
|
|
108
113
|
|
|
109
|
-
|
|
|
110
|
-
|
|
111
|
-
|
|
|
112
|
-
|
|
|
113
|
-
|
|
|
114
|
+
| Kind | A change in the code that … |
|
|
115
|
+
|------|------------------------------|
|
|
116
|
+
| **Fields** | adds, removes or renames a field/parameter/column on a command input, a persisted state (event) or a query result; changes its type, nullability/required-ness, or which field identifies the entity (`idAttribute`) |
|
|
117
|
+
| **Rules / specifications** | adds, removes or changes a validation, a guard, an error case or a default — or a test (given/when/then) — so that the board's GWT scenarios no longer describe the behavior |
|
|
118
|
+
| **Screens** | changes a UI template/component/route so that what it shows (read-model fields) or submits (command fields) differs from the board's SCREEN and its edges |
|
|
119
|
+
| **Edges** | makes a handler persist/emit something the board doesn't connect, a query read data the board doesn't wire in, a reaction (listener, job) trigger or issue something new — or removes such a connection |
|
|
120
|
+
| **Element** | adds a new command/state change/query/reaction, or removes or renames one the board has |
|
|
121
|
+
| **Slice** | adds a whole new feature (entry point + persistence/query) that is no slice on the board, or removes one the board says is `Done` |
|
|
114
122
|
|
|
115
|
-
|
|
123
|
+
Rules for judging:
|
|
116
124
|
|
|
117
|
-
|
|
125
|
+
- **Only changes since the baseline.** Code that was already different from the board at `BASE` is not this run's drift (the last analysis accepted it) — unless a change since then touched it.
|
|
126
|
+
- **Ignore everything that changed only on the board.** A board field the code never had, a `Planned` slice with no code, scenarios added after the build — that is planned work for the build kit, not drift. Don't report it, not even as a footnote.
|
|
127
|
+
- **A change that brings the code closer to the board is no finding** (e.g. a fix that finally implements a modeled scenario). Mention it in the summary at most.
|
|
128
|
+
- Don't flag what the chapter note's *Chapter decisions* / *Not modeled* or a column decision note explain as intended.
|
|
129
|
+
- No framework noise: generated ids, metadata/envelope fields, serialization annotations, test helpers, logging, refactorings that keep behavior (rename of a private method, extract class).
|
|
130
|
+
- **One difference, one finding**: a field renamed in the controller, the entity, the column and the template is one finding listing the chain, at the element where it starts.
|
|
118
131
|
|
|
119
132
|
---
|
|
120
133
|
|
|
121
|
-
## Step 6 —
|
|
134
|
+
## Step 6 — Propose the fix, show the findings, act only on confirmation
|
|
135
|
+
|
|
136
|
+
**The code change is the newer decision**, so the default fix is to **update the model** to describe what the code does now. The alternative is to treat the code change as a mistake and have the build kit bring the code back to the board.
|
|
137
|
+
|
|
138
|
+
| Finding | Recommended fix | Alternative |
|
|
139
|
+
|---------|-----------------|-------------|
|
|
140
|
+
| Field, rule/spec, screen, edge or element changed in code | **Model**: `/attributes` (fields), `/timeline` / `/place-element` (elements, edges), `/eventmodeling-elaborating-scenarios` (rules → GWT), `/html-screen` / `/storyboard-screen` (screens) | **Code**: `/update-slice-status` → `Planned` so the build kit rebuilds it from the board |
|
|
141
|
+
| New feature in code, no slice on the board | **Model**: place its elements, then `/eventmodeling-slicing-event-models` | **Code**: remove it — a decision for a human, never done from here |
|
|
142
|
+
| Board says `Done`, the code was removed | **Model**: remove the slice, or set it back to `Planned` if it is still wanted | — |
|
|
143
|
+
|
|
144
|
+
Recommend the alternative instead only when the change is evidently accidental (a typo like `custoemrId`, a debugging leftover, a disabled test) — say why.
|
|
145
|
+
|
|
146
|
+
**A model fix always includes the existing screens.** When a field, command or read model changes on the board, every SCREEN/HTML_SCREEN that shows or submits it changes with it — list each affected screen in the same finding (follow read model → screen and screen → command edges) and update it in place (`/html-screen` with its `nodeId`, `/storyboard-screen`). Never create a new screen next to an outdated one. **Never edit the code from this skill.**
|
|
122
147
|
|
|
123
148
|
Nothing is modified until the person answers.
|
|
124
149
|
|
|
125
150
|
### In a chat (`CHAT_SESSION_ID` is set)
|
|
126
151
|
|
|
127
|
-
Post **one** `post_chat_message` with a `tasks` snippet — the
|
|
152
|
+
Post **one** `post_chat_message` with a `tasks` snippet — the findings *and* the question which fixes to apply. Name the range in `text`, so the answering turn can read it back for Step 7:
|
|
128
153
|
|
|
129
154
|
```
|
|
130
155
|
mcp__eventmodelers__post_chat_message {
|
|
131
156
|
"boardId": "$BOARD_ID",
|
|
132
157
|
"sessionId": "<CHAT_SESSION_ID>",
|
|
133
|
-
"text": "
|
|
158
|
+
"text": "Since the last analysis (a1b2c3d → 9c1e2f0, 3 commits) the code changed in 2 places the board doesn't show. Tick the fixes to apply.",
|
|
134
159
|
"snippet": {
|
|
135
160
|
"kind": "tasks",
|
|
136
161
|
"headline": "Apply these fixes?",
|
|
137
162
|
"submitLabel": "Apply",
|
|
138
163
|
"tasks": [
|
|
139
|
-
{ "id": "d:model:<nodeId>:field:
|
|
140
|
-
{ "id": "d:code:<sliceId
|
|
164
|
+
{ "id": "d:model:<nodeId>:field:chipId", "title": "Rename field “microchipNumber” to “chipId” on Register Microchip and Microchip Registered", "description": "9c1e2f0 “chip id” renamed it in Microchip.kt and the controller after the slice was built — the board still says microchipNumber", "nodeId": "<nodeId>" },
|
|
165
|
+
{ "id": "d:code:<sliceId>:rule:uniqueness", "title": "Rebuild “Register Microchip” — the uniqueness check was removed in code", "description": "Sets the slice back to Planned", "nodeId": "<sliceId>" }
|
|
141
166
|
]
|
|
142
167
|
}
|
|
143
168
|
}
|
|
144
169
|
```
|
|
145
170
|
|
|
146
|
-
- `title` is the proposed fix, one sentence, plain language. `description`
|
|
171
|
+
- `title` is the proposed fix, one sentence, plain language. `description` names the commit (sha, subject, author) and what differs.
|
|
147
172
|
- `id` is `d:<model|code|decide>:<nodeId>:<kind>[:<detail>]` — the answering turn remembers nothing, so it must carry the direction and target. Put **every** finding in the one snippet (it pages itself at 5).
|
|
148
|
-
- Zero findings: a one-line text reply, no snippet
|
|
173
|
+
- Zero findings: a one-line text reply, no snippet, then Step 7.
|
|
149
174
|
|
|
150
|
-
The answer comes as a `CHAT` turn (`Please do these:` + ticked titles or ids). Read the snippet back with `get_chat_session`, match what was ticked, and create **one prompt** per direction: model fixes → *"Apply these model-drift fixes: `<id> | <title>` per line"*; code fixes → set each slice to `Planned`. Unticked fixes are declined — drop them.
|
|
175
|
+
The answer comes as a `CHAT` turn (`Please do these:` + ticked titles or ids). Read the snippet back with `get_chat_session`, match what was ticked, and create **one prompt** per direction: model fixes → *"Apply these model-drift fixes: `<id> | <title>` per line"*; code fixes → set each slice to `Planned`. Unticked fixes are declined — drop them. Then Step 7.
|
|
151
176
|
|
|
152
177
|
### Without a chat
|
|
153
178
|
|
|
154
|
-
Print the report below,
|
|
179
|
+
Print the report below, every finding as a `- [ ]` checkbox carrying its proposed fix, and ask which to apply. Wait for the answer, act on it, then Step 7.
|
|
155
180
|
|
|
156
|
-
Name each slice and element as `[<title>](ref:<nodeId>)` (the slice border's id for a slice)
|
|
181
|
+
Name each slice and element as `[<title>](ref:<nodeId>)` (the slice border's id for a slice); code that has no node stays plain text.
|
|
157
182
|
|
|
158
183
|
```
|
|
159
184
|
## Model Drift — <scope: board | timeline "<title>" | context "<name>">
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
### Slices
|
|
163
|
-
- [ ] <slice> — in code, not on the board → add it to the model
|
|
164
|
-
- [ ] [<slice>](ref:<sliceBorderId>) — board says Done, no code → set back to Planned
|
|
185
|
+
Changes: <BASE> (<date>, last analysis) → <REF short sha><, branch <name>><, + uncommitted> · <n> commits, <n> files with behavior changes Analysed: <ISO timestamp>
|
|
165
186
|
|
|
166
187
|
### Fields
|
|
167
|
-
- [ ] [<slice>](ref:<sliceBorderId>) / [<element>](ref:<nodeId>):
|
|
188
|
+
- [ ] [<slice>](ref:<sliceBorderId>) / [<element>](ref:<nodeId>): board says `microchipNumber`; `9c1e2f0 chip id` renamed it to `chipId` after the build (`7e3d1a2 feat: Register Microchip`) — `Microchip.kt:27`, `RegisterMicrochipController.kt:41`, both tests → rename the field on the command, the event and the screen
|
|
168
189
|
|
|
169
|
-
###
|
|
190
|
+
### Rules / Specifications · Screens · Edges · Elements · Slices
|
|
170
191
|
- [ ] ...
|
|
171
192
|
|
|
172
193
|
### Summary
|
|
173
|
-
<2–3 sentences:
|
|
174
|
-
|
|
194
|
+
<2–3 sentences: what changed in the code since the last analysis, how much of it the board is missing, the one fix worth doing first.>
|
|
195
|
+
Commits: <sha subject — one line each; mark build-kit commits>
|
|
196
|
+
Not relevant: <noise commits/files>
|
|
175
197
|
```
|
|
176
198
|
|
|
177
199
|
Omit empty sections. Say plainly when nothing drifted.
|
|
178
200
|
|
|
179
201
|
---
|
|
180
202
|
|
|
203
|
+
## Step 7 — Move the baseline forward
|
|
204
|
+
|
|
205
|
+
Once the person has answered (or there was nothing to report), append **one row** to the *Analysis history* of every chapter in scope (`node:changed` on that note's `meta.description` — never edit or drop earlier rows), so the next run starts from here:
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
| 4 | 2026-10-07 | `9c1e2f0` | drift check since `a1b2c3d` | `Microchip.kt`, `RegisterMicrochipController.kt` | 2 findings: 1 model fix applied, 1 declined |
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
- **Commit** is `REF`'s short sha; add `+dirty` when uncommitted changes were part of the check. With `--branch`, write `<sha> (<branch>)`.
|
|
212
|
+
- A declined finding is accepted as intended — that is why the baseline still moves past it; record it in the row so it can be found later.
|
|
213
|
+
- A chapter without a chapter note: don't create one here — say in the report that `/analyze-code-base` would add it, and that the next drift check needs `--since` until then.
|
|
214
|
+
- If the person never answers, the baseline stays where it was — the same changes come up again next time.
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
181
218
|
## Learnings
|
|
182
219
|
|
|
183
|
-
-
|
|
184
|
-
-
|
|
185
|
-
-
|
|
186
|
-
-
|
|
187
|
-
-
|
|
188
|
-
-
|
|
189
|
-
-
|
|
190
|
-
-
|
|
220
|
+
- Drift = code changed, board didn't. Board-only changes are planned work for the build kit — never report them.
|
|
221
|
+
- The baseline is the last *Analysis history* row of the chapter's first-column note; the changes are `git log/diff <BASE>..<REF>` plus uncommitted work. No baseline → ask for a starting commit, never fall back to a whole-board comparison.
|
|
222
|
+
- The code change is the newer decision: recommend updating the model, unless the change is evidently accidental.
|
|
223
|
+
- Map changes to the board through the `Implementation — <Slice>` evidence notes and the Slice map first, names last; read only the slices hit.
|
|
224
|
+
- Walk the commits one by one and sort per file: a net diff hides a hand edit inside a file the build kit created in the same range, and one commit can mix kit-install noise with real code changes.
|
|
225
|
+
- A build-kit commit (evidence note names its sha, else `feat: <Slice>`) is the as-built state; every later change to its files is a hand-made change.
|
|
226
|
+
- No git history → no drift detection; say so in one line and stop (Step 0).
|
|
227
|
+
- `--branch` reads the code via `git show <REF>:<path>` — never check it out, the working tree may hold someone's uncommitted work.
|
|
228
|
+
- Every completed run appends one *Analysis history* row; an unanswered run doesn't move the baseline.
|
|
229
|
+
- `.build-kit/.slices` is no evidence of what the code was built from: every fetch, `/load-slice` and runner loop rewrites it.
|
package/stacks/modeling-kit/templates/.claude/skills/eventmodeling-elaborating-scenarios/SKILL.md
CHANGED
|
@@ -198,15 +198,19 @@ As scenarios are written, ensure each role checks:
|
|
|
198
198
|
| # | Type | Question to ask |
|
|
199
199
|
|---|------|----------------|
|
|
200
200
|
| 1 | **Happy Path** | "What is the normal success case?" |
|
|
201
|
-
| 2 | **Validation Failure** | "What
|
|
201
|
+
| 2 | **Validation Failure** | "What input does the *business* refuse?" (e.g. a booking date in the past) — not schema checks like missing/malformed fields |
|
|
202
202
|
| 3 | **State Violation** | "What if the system is in a state that makes this command invalid?" |
|
|
203
|
-
| 4 | **Duplicate Action** | "What if this
|
|
203
|
+
| 4 | **Duplicate Action** | "What if someone performs this business action again on something already in that state?" (e.g. resolving an already-resolved todo) — never a re-sent or colliding id |
|
|
204
204
|
| 5 | **Alternative Path** | "Are there different valid outcomes depending on context?" |
|
|
205
205
|
| 6 | **External Failure** | "What if an external system or scheduler fails during this command?" |
|
|
206
206
|
| 7 | **Compensation** | "Can this be undone or reversed? What triggers the cleanup?" |
|
|
207
207
|
|
|
208
208
|
Work through each question with the domain in mind. If the answer is "that situation cannot occur in this business" then no scenario is needed for that type — but that judgment must come from the domain, not from a desire to write fewer scenarios.
|
|
209
209
|
|
|
210
|
+
**Scenarios specify business rules, not technical guarantees.** Every rejection must name a rule a domain expert would recognise and could argue about ("a closed day takes no new todos"). Do not write scenarios for: duplicate or colliding ids, retries/idempotency of the same request, missing or malformed required fields, type/format checks, or infrastructure failures — the implementation handles those and they say nothing about the domain. Rules the brief states explicitly (e.g. "adding on a closed day is rejected") are the core of the coverage and must all be present.
|
|
211
|
+
|
|
212
|
+
**`errorDescription` is domain language** — the reason as a user would read it ("Day is already closed"), never an id, a placeholder or a truncated example value.
|
|
213
|
+
|
|
210
214
|
## Workflow
|
|
211
215
|
|
|
212
216
|
### 0. Decide GWT vs. storyline for every READMODEL — before drafting any scenario payload
|
|
@@ -426,21 +430,21 @@ When a scenario represents a command being **rejected** (validation failure, sta
|
|
|
426
430
|
|
|
427
431
|
**`expectError: true`** — signals that the command should be rejected and no event is produced. Set this for every failure/rejection scenario.
|
|
428
432
|
|
|
429
|
-
**`errorDescription`** — a short human-readable description of the expected error or rejection reason (e.g. `"
|
|
433
|
+
**`errorDescription`** — a short human-readable description of the expected error or rejection reason (e.g. `"reservation date is in the past"`, `"reservation already confirmed"`). Do not leave this empty when `expectError` is `true`.
|
|
430
434
|
|
|
431
435
|
When `expectError` is `true`:
|
|
432
436
|
- `then` must be `[]` — no events are produced on rejection
|
|
433
437
|
- `when` contains the COMMAND being rejected
|
|
434
|
-
- `given` contains any prerequisite EVENTs needed to establish the wrong state (may be empty for
|
|
438
|
+
- `given` contains any prerequisite EVENTs needed to establish the wrong state (may be empty for an input the business refuses)
|
|
435
439
|
- Do **not** set `expectEmptyList` — that flag is only for list READMODELs
|
|
436
440
|
|
|
437
441
|
Rejection scenario example:
|
|
438
442
|
```json
|
|
439
443
|
{
|
|
440
444
|
"id": "<uuid>",
|
|
441
|
-
"title": "Reject reservation
|
|
445
|
+
"title": "Reject a reservation for a date in the past",
|
|
442
446
|
"expectError": true,
|
|
443
|
-
"errorDescription": "
|
|
447
|
+
"errorDescription": "reservation date is in the past",
|
|
444
448
|
"given": [],
|
|
445
449
|
"when": [{"id": "<commandNodeId>", "title": "Reservation Command", "type": "COMMAND"}],
|
|
446
450
|
"then": [],
|
|
@@ -344,7 +344,7 @@ if Conway's Law boundaries are not relevant to the project.
|
|
|
344
344
|
|
|
345
345
|
### Step 7: Elaborate Scenarios
|
|
346
346
|
|
|
347
|
-
Invoke `eventmodeling-elaborating-scenarios
|
|
347
|
+
Invoke `eventmodeling-elaborating-scenarios` with the Skill tool **before the first `add_scenario` call** — its scenario types, business-rules-only rule and payload shape are not repeated here, so scenarios written from this file alone come out wrong.
|
|
348
348
|
|
|
349
349
|
**Input**: Commands and read models.
|
|
350
350
|
**Output to carry forward**: Given-When-Then specifications (or storylines, for
|
|
@@ -356,7 +356,7 @@ the elaborating-scenarios workflow — not just happy path + one error case —
|
|
|
356
356
|
gate checklist below. A command-only pass is an incomplete Step 7, even if
|
|
357
357
|
every command's coverage looks exhaustive.
|
|
358
358
|
|
|
359
|
-
> **Do not reduce scenarios to a simple good-case / bad-case pair
|
|
359
|
+
> **Do not reduce scenarios to a simple good-case / bad-case pair** — but do not pad them with technical cases either. Which scenario types apply is defined in `eventmodeling-elaborating-scenarios` and decided by the domain.
|
|
360
360
|
|
|
361
361
|
> **Read models need scenarios too — easy to forget since the seven types above are command-shaped.** Every READMODEL needs at least one view scenario (GWT or storyline); a read model with zero scenarios is as incomplete as a command with zero. `eventmodeling-elaborating-scenarios`'s own checklist covers the details — connectivity rules, GWT-vs-storyline judgment per read model, and avoiding redundancy between a storyline and its GWTs — don't re-derive those here, just enforce the gate.
|
|
362
362
|
|
|
@@ -138,7 +138,7 @@ mcp__eventmodelers__add_scenario {
|
|
|
138
138
|
}
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
Same rules apply (given=EVENTs only, when=at most one COMMAND or QUERY, then=EVENTs only or exactly one READMODEL, `expectError`+`errorDescription` for error cases). This auto-creates the spec node if the cell is empty, same as the curl endpoint.
|
|
141
|
+
Same rules apply (context=READMODELs only, given=EVENTs only, when=at most one COMMAND or QUERY, then=EVENTs only or exactly one READMODEL, `expectError`+`errorDescription` for error cases). This auto-creates the spec node if the cell is empty, same as the curl endpoint.
|
|
142
142
|
|
|
143
143
|
**Fallback (no MCP):** see `references/api-fallback.md` — "Step 4a — SCENARIO: append scenarios via the spec endpoint" for the full curl call and example payload.
|
|
144
144
|
|
|
@@ -413,7 +413,7 @@ nothing to sanitize either, since a board change is not user text.
|
|
|
413
413
|
| Business analysis, gap spotting, posting questions | `/wdyt` |
|
|
414
414
|
| Analyse the existing model structure, slice coverage, element counts | `/analyze-existing-model` |
|
|
415
415
|
| Reverse-engineer the business process of an existing code base into the model, as an interview — all chapters first, then one chapter at a time | `/analyze-code-base` |
|
|
416
|
-
| Find
|
|
416
|
+
| Find what changed in the code since the last analysis (git history from the chapter note's baseline) that the model doesn't reflect, and propose fixes | `/detect-model-drift` |
|
|
417
417
|
| Walk someone through a feature slice by slice, or through every slice a planned feature needs (existing, to adjust, new), as a player in the chat | `/walkthrough` |
|
|
418
418
|
| Turn a document or image (e.g. a tender/RFP) into a chaptered event model with screens, scenarios and a written blueprint summary | `/summarize` |
|
|
419
419
|
| Look up any API endpoint or element type not already covered by the skill you're executing | `/learn-eventmodelers-api` |
|