@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.
@@ -1,21 +1,29 @@
1
1
  ---
2
2
  name: detect-model-drift
3
- description: Compare the event model on the board with the code in this repository and report where they have drifted apart — fields on commands/events/read models, specifications vs tests, screens, edges, slices missing in code, and slices in code that the model doesn't have. `--branch <name>` compares against that branch's code (default: the current branch's working tree). Read-only; every finding comes with a suggested fix the user confirms first.
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
- ## Step 0 — Is there a code base?
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
- Drift is the gap between the board and the code, so it can only be detected where code exists. **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. Check that it is a code base: it contains source code (e.g. `src/`, `.build-kit/`, a `package.json`, `pom.xml`, `*.csproj`, `build.gradle`, or similar project files with slice/command/event/projection code).
10
+ So the skill looks at **the code changes since the last analysis**, nothing else:
11
11
 
12
- If it is 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.:
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
- > 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.
16
+ ## Step 0 — Is there a code base with history?
15
17
 
16
- Do not connect, do not read the board, do not produce a report or snippet, and do not offer other fixes.
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
- **With `--branch <name>`** (see Step 1), also check that the branch exists in that code base: `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 the same way in one sentence (*"There is no branch `<name>` in this repository."*).
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
- The board is the intended design, the code is what was built. Over time the two part ways: someone edits a handler without touching the model, someone renames a field on the board after the slice was built. This skill finds those places and proposes how to close each gap. **It never changes the board or the code** — it reports, and only acts on what the person confirms.
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 | whole board |
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's working tree |
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 — Read the model (once)
52
+ ## Step 2 — Find the baseline (per chapter)
46
53
 
47
- Cheapest read first, once per run:
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
- Use the **full** slice data (no `projection`): you need fields with types and `idAttribute`, dependencies (the edges), specifications, storylines and screens together. If the scope is a timeline, pass its title as `contextName` and keep only slices whose `chapter` is that timeline.
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
- Call `validate_model { boardId, chapterId }` for a scoped timeline — it already lists structural gaps on the board side (zero/multi-issuer commands, sourceless read models, missing scenarios). Those are model problems, not drift: mention them in one line, don't duplicate them as findings.
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
- **Always read each chapter's context note before comparing.** The MARKDOWN note in the `feedback` lane of a chapter's **first column** holds the context for that chapter — architecture decisions, intended deviations, what the code is supposed to look like. For every chapter in scope, call `get_board_outline { boardId, chapterId }`, take the first column's node with `type: "MARKDOWN"` in a `feedback` lane, and read its `meta.description` with `get_node`. Judge every finding against it: a difference the note explains is intentional, not drift. A chapter without such a note is fine — carry on without it.
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
- Index the result in memory: per slice → commands, events, read models, screens, automations, specifications, dependencies.
64
+ Chapters with the same baseline share one git read in Step 3.
61
65
 
62
66
  ---
63
67
 
64
- ## Step 3 — Understand the code
68
+ ## Step 3 — Collect the changes since the baseline
65
69
 
66
- The code base is the directory resolved in Step 0 (`$EVENTMODELERS_CODE_DIR`, else the current directory) — every path below is relative to it, and it may hold any stack. Find out which, don't assume.
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
- **With a `branch` other than the one checked out, read the code from git, never from the working tree:** `git ls-tree -r --name-only $REF [<dir>]` lists the files, `git show $REF:<path>` reads one. That applies to everything below — `.build-kit/CLAUDE.md`, the slice folders. Never `git checkout`/`git switch`: the working tree may hold someone's uncommitted work. Without `branch`, or when it is the current branch, read the working tree as usual.
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
- 1. Read `.build-kit/CLAUDE.md` (and `.build-kit/AGENTS.md` if present) — its *Structure* section says where slices live and how they're named (e.g. `src/slices/{slice}/` for Node, `src/main/java/.../slices/{context}/{slice}/` for Axon).
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
- Build a **code inventory** per slice folder: the command type and its fields, the events it emits and their fields, read-model/projection types and fields, the screens/components/routes wired to it, tests and what they assert, and what each handler/projection/automation reads and writes.
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
- Map slice → code folder by name (ignore case, spaces and the `slice:` prefix, same slug rule as `load-slice`). A slice with no matching folder, or a folder with no matching slice, is itself a finding (5a/5b).
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
- Read only the slice folders in scope. Shared event/type folders are read only for the events the in-scope slices reference.
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 — Compare
95
+ ## Step 4 — Map the changes to the board
82
96
 
83
- Work slice by slice. Every finding records: **kind**, **slice**, **element** (board `nodeId` where there is one), **what the model says**, **what the code says**.
97
+ For every behavior change, find the slice(s) and element(s) it belongs to. Use, in this order:
84
98
 
85
- | Kind | Look for |
86
- |------|----------|
87
- | **Slice missing in code** | Slice on the board in status `Planned`/`InProgress`/`Done`… with no code folder. Status `Created`/`Planned` is expected not to have code yet — report only slices the board calls `Done`/`Review`. |
88
- | **Slice only in code** | Code folder shaped like a slice (command handler, projection, automation) with no slice on the board. |
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
- Rules for judging, in order:
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
- - **Obvious typos and clearly wrong flags are one finding with a direct fix** (`custoemrId` in the code vs `customerId` on the board; `idAttribute: false` on the entity's own id) — name the correct side, don't ask which it is.
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 — Decide the direction, propose the fix
110
+ ## Step 5 — Compare the changed code with the board
106
111
 
107
- For every finding, pick who should move:
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
- | Evidence | Fix goes to | How it's done |
110
- |----------|-------------|---------------|
111
- | A field, element, spec, screen or edge differs | **Judgement call** — present both options, recommend one | Recommend by which side is more specific/complete. Model fix: `/attributes` (fields), `/timeline` / `/place-element` (elements), `/eventmodeling-elaborating-scenarios` (specs), `/storyboard-screen` or `/html-screen` (screens — including updating every existing screen connected to a changed element, see below). Code fix: set the slice back to `Planned` with `/update-slice-status` so the build kit picks it up again |
112
- | Slice only in code | **Model** | Add the slice, via `/eventmodeling-slicing-event-models` after placing its elements |
113
- | Slice missing in code, board says `Done` | **Model status** (it isn't done) | `/update-slice-status` → `Planned` |
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
- **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 must change with it — otherwise the drift just moves from the code into the storyboard. List each affected screen in the same finding (follow the read model → screen and screen → command edges), and update it in place: `/html-screen` with its `nodeId` (loads the current pages and edits them, plus its `meta.fields`), `/storyboard-screen` for a wireframe sketch. Never create a new screen next to an outdated one.
123
+ Rules for judging:
116
124
 
117
- Never propose "fix the code" by editing it from this skill — code changes belong to the build kit (`/update-slice-status` → `Planned`).
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 — Show the findings; act only on confirmation
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 list of findings *and* the question which fixes to apply (same mechanics as `/wdyt` Step 4.0):
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": "Found 6 places where the model and the code differ in Registration. Tick the fixes to apply.",
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:customerId", "title": "Rename field “custoemrId” to “customerId” on Register Customer", "description": "Code has customerId — model has a typo", "nodeId": "<nodeId>" },
140
- { "id": "d:code:<sliceId>", "title": "Rebuild “Cancel Order” — the board added the field “reason” after it was built", "description": "Sets the slice back to Planned", "nodeId": "<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` says what differs (model vs code). `nodeId` is the element or slice border to jump to.
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. Then **end the turn.**
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, with every finding as a `- [ ]` checkbox carrying its proposed fix, and ask which to apply. Wait for the answer.
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). The terminal shows the title, and once the report is in a board note or an element description, a click zooms the canvas to it (see `/learn-eventmodelers-api` *Linking to elements in markdown*). A slice that exists only in code has no node, so it stays plain text.
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
- Compared: <n> slices on the board, <n> slice folders in code<, branch <name> @ <short sha>> Analysed: <ISO timestamp>
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>): model `customerId: String`, code `custoemrId` → fix model typo
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
- ### Specifications / Screens / Edges
190
+ ### Rules / Specifications · Screens · Edges · Elements · Slices
170
191
  - [ ] ...
171
192
 
172
193
  ### Summary
173
- <2–3 sentences: how far apart they are, which direction the drift mostly runs, the one fix worth doing first.>
174
- Not checked: <what was out of scope or unreadable>
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
- - No code base in the working directory → drift can't be detected; say so in one line and stop (Step 0).
184
- - Read-only until confirmed — the report is the product; the fixes run as separate, ticked work.
185
- - A scoped run (timeline in focus) never reports "slice only in code" for code outside the scope.
186
- - One difference, one finding: follow a rename along the chain instead of listing it per element.
187
- - Fixing drift on the model side means updating the existing screens connected to the changed elements too (pages and `meta.fields`), in place — not just the command/event/read model.
188
- - `--branch` reads the code via `git ls-tree`/`git show` from that ref — never check it out, the working tree may hold someone's uncommitted work. No `--branch` = the working tree, as before.
189
- - Always read the MARKDOWN note in the feedback lane of each chapter's first column first — it is the chapter's context, and differences it explains are not drift.
190
- - `.build-kit/.slices` is no evidence for the direction of a fix: every fetch, `/load-slice` and runner loop rewrites it, so it holds the last fetch, not what the code was built from — reading it as "the build" recommends undoing board changes. The direction is a judgement call.
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.
@@ -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 invalid or missing inputs should be rejected?" |
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 command is issued again after it already succeeded?" |
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. `"missing required field: date"`, `"reservation already confirmed"`). Do not leave this empty when `expectError` is `true`.
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 pure input-validation rejections)
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 with missing required date field",
445
+ "title": "Reject a reservation for a date in the past",
442
446
  "expectError": true,
443
- "errorDescription": "missing required field: date",
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.** The `eventmodeling-elaborating-scenarios` skill defines a structured scenario workshop covering seven scenario types per command (Happy Path, Validation Failure, State Violation, Duplicate Action, Alternative Path, External Failure, Compensation — see that skill's own table for the question-form definition of each) — which apply is determined by the domain, not by a fixed rule. All applicable types must be written before this step is complete; do not decide based on brevity.
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 where the model and the code have drifted apart (fields, specs, screens, edges, missing/extra slices) and propose fixes | `/detect-model-drift` |
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` |