@eventmodelers/cli 1.0.23 → 1.0.25

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eventmodelers/cli",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "description": "Eventmodelers CLI — real-time Claude agent + skills for Claude Code, for any stack (Node, Supabase, Axon, Cratis, or modeling-only)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -346,7 +346,9 @@ Does not produce commands (info-only)
346
346
 
347
347
  ## Output Format
348
348
 
349
- Instead of writing a markdown document, **place each READMODEL (and any missing AUTOMATION) on the board** using the `node:created` API. Screens are typically already placed from Step 3 (storyboarding) — do not re-place them unless one is clearly missing. Automations are placed here when analysis reveals a processor that reads state and issues commands but is not yet on the board.
349
+ Instead of writing a markdown document, **place each READMODEL (and any missing AUTOMATION) on the board** using the `node:created` API. Screens are typically already placed from Step 3 (storyboarding) as one plain screen per screen state Step 3 does not pre-split anything, so working out how many components a screen actually has is this step's job (Step 5a below), not something to re-derive from storyboarding's output. Do not re-place an existing screen unless one is clearly missing. Automations are placed here when analysis reveals a processor that reads state and issues commands but is not yet on the board.
350
+
351
+ This step proceeds as a sequence of lettered sub-steps: identify each screen's components (5a), break multi-component screens apart into copies (5b), then design and place one read model per component (5c onward).
350
352
 
351
353
  > **CRITICAL: Every READMODEL node MUST include `meta.fields` with a `mapping` on every field.** A read model without fields — or with fields that lack `mapping` — has no data lineage and cannot be traced back to its source events.
352
354
 
@@ -372,31 +374,39 @@ READ MODEL → AUTOMATION → COMMAND → EVENT
372
374
 
373
375
  Treat any screen or automation without an incoming read model as a gap unless it provably needs no prior state at all (e.g., a blank registration form).
374
376
 
375
- ### One read model per screen region, not one read model per screen
377
+ ### Step 5a Enumerate consumers and identify components
376
378
 
377
- **Do not default to a single monolithic read model that supplies an entire screen.** If a screen has more than one visually distinct data region (e.g. a stats row plus a list below it, or a summary card plus a detail table), each region gets its **own** read model, sourced only from the events that region actually needs not the union of every event any part of the screen touches.
379
+ **Before designing any read model, enumerate every SCREEN and AUTOMATION already placed on the board** (from Step 3 Storyboarding). Storyboarding hands off one plain screen per screen state and does not pre-split anything deciding how many components a screen actually has is this step's decision, because a component is defined by its read model: **one component in a screen resembles one read model.**
378
380
 
379
- Why this matters, beyond tidiness:
380
- - **It prevents backward arrows.** A single screen-wide read model is forced to aggregate from whatever events each of its regions needs, which often means reaching back across many columns to events scattered throughout the timeline — and the read model can only sit in one column, so some of those connections end up spanning a wide gap or, worse, pushing the read model's column later than some of its screen's other consumers require. Splitting by region lets each narrower read model sit close to its own natural source event(s), keeping every `EVENT → READMODEL` arrow short and forward.
381
- - **Regions evolve independently.** A stats tile and a "recently added" list are driven by different events, change at different rates, and are typically owned by different slices in implementation. Bundling them into one read model couples their release/change cadence for no reason.
381
+ Read models exist to serve the elements already on the board:
382
+ - Every **view screen** (output/read model screen) needs at least one read model to supply its data.
383
+ - Every **automation** that makes a decision based on system state needs at least one read model to read from.
384
+ - Every **command/input screen** needs a read model unless it is a blank creation form with no prior state to display (this is the rare exception, not the rule).
382
385
 
383
- **How to realize this on the board**: each region's read model gets its own column (inserted immediately after that region's primary source event, per the placement rule above) and its own **screen copy** a duplicate of the same screen layout where the region that read model drives is shown normally and every other region is visually de-emphasized (e.g. `filter: blur(3px); opacity: 0.45; pointer-events: none;` on the non-relevant regions, keeping the target region crisp, optionally with a highlight border). Title each copy after the region it foregrounds, e.g. `"Librarian Dashboard Statistics"` and `"Librarian DashboardRecently Added"` as two separate HTML_SCREEN nodes, each in its own column, each wired `READMODEL → SCREEN` to only its own read model. This is the `html-screen` skill's job when asked to produce the copy pass it the full original screen markup plus which region to foreground.
386
+ For each SCREEN node, look at its rendered layout and its `meta.fields` and identify its componentsgroups of fields/UI elements a user would perceive as one area: a stats tile, a list below it, a summary card, a detail panel, a status column in a table, etc. **Most screens genuinely have exactly one componentdo not force a split.** A screen has more than one component only when a user would point at two separate areas and describe them as different things.
384
387
 
385
- Before finalizing any read model, ask: "does this screen have more than one visually distinct data region?" If yes, split — don't ask whether splitting is worth the extra columns, it always is at this scale, since the alternative is a hidden coupling and a higher chance of a backward-arrow layout error.
388
+ A single homogeneous list/table is still one component, even when its rows draw on many different event types (e.g. a catalog whose per-row status is set by many different lifecycle events scattered across the timeline). Splitting is about visually distinct *areas* of a screen, not about how many event types feed one area — don't split a table by row or by source event. Flag this kind of component as a **roll-up component**: its read model will legitimately need wide fan-in (many `EVENT → READMODEL` connections spanning much of the timeline) because it projects current state across an entity's full lifecycle rather than from one recent slice. This is expected, not a modeling error — but treat calling it a roll-up component as an **assumption**, not a fact, and document it as one: use the orchestrating skill's "Documenting decisions inline, at any step" mechanic (a MARKDOWN note in the feedback lane, placed in the component's column) to record explicitly *why* you concluded this is a legitimate single roll-up rather than an unsplit multi-component screen — e.g. "CopyAvailabilityView treated as one roll-up component: availability is a single per-copy status derived across that copy's full lifecycle (repair, loss, reservation, return), not several screen areas bundled together." That note is what lets a later reader (or reviewer) tell a deliberate roll-up apart from a missed split, instead of re-litigating or silently re-flagging it as a god read model.
386
389
 
387
- ### Read models serve existing screens and automations
390
+ After this step is done, **every SCREEN and every AUTOMATION on the board must be connected to at least one read model** via a `READMODEL → SCREEN` or `READMODEL → AUTOMATION` connection, and every screen identified above as having 2+ components must have been broken apart per Step 5b before any read model is placed. If a screen or automation has no incoming read model connection, it is a gap — either a read model is missing or the connection arrow is missing.
388
391
 
389
- **Before designing any read model, enumerate every SCREEN and AUTOMATION already placed on the board** (from Step 3Storyboarding). Read models exist to serve those elements:
392
+ > **Placement rule**: A read model must be placed in a column that already contains a SCREEN or AUTOMATION it serves. Do not place read models in columns with no screen or automation doing so creates orphaned read models that will never have a consumer.
390
393
 
391
- - Every **view screen** (output/read model screen) needs at least one read model to supply its data.
392
- - Every **automation** that makes a decision based on system state needs at least one read model to read from.
393
- - Every **command/input screen** needs a read model unless it is a blank creation form with no prior state to display (this is the rare exception, not the rule).
394
+ ### Step 5b Break apart multi-component screens into copies
394
395
 
395
- After the step is done, **every SCREEN and every AUTOMATION on the board must be connected to at least one read model** via a `READMODEL SCREEN` or `READMODEL → AUTOMATION` connection. If a screen or automation has no incoming read model connection, it is a gap either a read model is missing or the connection arrow is missing.
396
+ **Do not default to a single monolithic read model that supplies an entire screen.** For every screen identified in Step 5a as having 2+ components, break it apart into one screen copy per component before designing its read models:
396
397
 
397
- > **Placement rule**: A read model must be placed in a column that already contains a SCREEN or AUTOMATION it serves. Do not place read models in columns with no screen or automation doing so creates orphaned read models that will never have a consumer.
398
+ - Each copy is the **same page**, so it **keeps the same screen name/title** do not rename copies after their component (e.g. don't title one copy `"Librarian DashboardStatistics"` and another `"Librarian Dashboard Recently Added"`; both stay `"Librarian Dashboard"`).
399
+ - Distinguish copies visually, not by name: **mark/highlight the one component each copy is about**, using the `html-screen` skill's native Marks feature — see that skill's "Marks" section for the full mechanism. In short: the component of interest gets `data-em-mark-id`/`em-mark em-mark-<colorhex>` baked onto it in the page HTML, paired with a matching `meta.marks` entry (`{id, color, pageIndex, blurOutside: true}`) that blurs every other top-level section. Never hand-roll this with inline `filter`/`outline`/`opacity` CSS — that does not match how the app itself renders a mark.
400
+ - Use the `html-screen` skill to produce each copy: pass it the original screen's markup, explicitly asking it to mark/highlight the one component to keep crisp and blur the rest (this satisfies `html-screen`'s "only when the user explicitly asks for one" condition for its Marks feature — the ask comes from this step). This is the skill's job — don't hand-roll the markup here.
401
+ - Place each copy in its own column, one column to the right of the read model that will feed it — this is normally a different column per copy, since each component typically has a different natural source event.
402
+
403
+ Why this matters, beyond tidiness:
404
+ - **It prevents backward arrows.** A single screen-wide read model is forced to aggregate from whatever events each of its components needs, which often means reaching back across many columns to events scattered throughout the timeline — and the read model can only sit in one column, so some of those connections end up spanning a wide gap or, worse, pushing the read model's column later than some of its screen's other consumers require. Splitting by component lets each narrower read model sit close to its own natural source event(s), keeping every `EVENT → READMODEL` arrow short and forward.
405
+ - **Components evolve independently.** A stats tile and a "recently added" list are driven by different events, change at different rates, and are typically owned by different slices in implementation. Bundling them into one read model couples their release/change cadence for no reason.
406
+
407
+ Before finalizing any read model, ask: "does this screen contain more than one component?" If yes and it wasn't already broken apart, do it now — don't ask whether it's worth the extra columns, it always is at this scale, since the alternative is a hidden coupling and a higher chance of a backward-arrow layout error.
398
408
 
399
- ### Pull field mappings from Step 3 — they are the spec, not a guess
409
+ ### Step 5c — Pull field mappings from Step 3 — they are the spec, not a guess
400
410
 
401
411
  **Do not re-derive read model needs from a screen's title or description alone, and do not rely on the orchestrator's phase-summary handoff for this** — if you arrived here via `eventmodeling-orchestrating-event-modeling`, the handoff after Step 3 is a short hand-written prose summary (`.trogonai/interviews/.../EVENTMODELING.md`), not the actual field data. It will not reliably carry the per-field mappings forward. Go back to the board itself:
402
412
 
@@ -406,7 +416,7 @@ For every SCREEN node, fetch it directly (`get_node`/`get_nodes`, never from mem
406
416
  - If a field's `mapping` names a read model that isn't `"<CommandTitle>.<fieldName>"` or `"session:..."` or `"derived:..."`, it is a read-model reference — treat it as a requirement, not a suggestion.
407
417
  - A screen with no fields, or with fields that carry no read-model-shaped mapping, is **not** evidence that it needs no read model. Re-check it against the three rules above (view screen / automation / command screen showing prior state) before concluding it's the rare blank-form exception — and say explicitly why it qualifies.
408
418
 
409
- ### Field data lineage — the `mapping` attribute on READMODEL fields
419
+ ### Step 5d — Field data lineage — the `mapping` attribute on READMODEL fields
410
420
 
411
421
  Every field on a READMODEL must carry a `mapping` that says exactly which event (or command) field it is projected from. Use one of these forms:
412
422
 
@@ -446,7 +456,7 @@ Read models go in the `interaction` lane — **in the same column as the SCREEN
446
456
 
447
457
  > **Timeline alignment rule**: Place the read model in the same column as its consumer screen/automation. If that column already holds a COMMAND (state-change slice occupies the interaction row), insert a new column immediately after (`{"index": N+1}`) and place both the READMODEL and any new SCREEN there. Do not append read model columns to the end of the timeline — doing so severs the visual left→right flow from data projection to UI consumption.
448
458
 
449
- ### Placing READMODEL and AUTOMATION nodes with `cellId` (Mandatory)
459
+ ### Step 5e — Placing READMODEL and AUTOMATION nodes with `cellId` (Mandatory)
450
460
 
451
461
  **Every `node:created` call MUST include `cellId`.** Without it the node has no cell reference and will appear stranded at position 0,0 — not in any timeline column.
452
462
 
@@ -522,7 +532,7 @@ mcp__eventmodelers__get_node { "boardId": "<BOARD_ID>", "nodeId": "<CHAPTER_ID>"
522
532
 
523
533
  > **Never call `drop` after using `cellId` in `node:created`.** The drop endpoint adds a second cell reference without removing the first. `node:created + cellId` is the only placement step needed.
524
534
 
525
- ### Preventing backward arrows (mandatory pre-placement check)
535
+ ### Step 5f — Preventing backward arrows (mandatory pre-placement check)
526
536
 
527
537
  The timeline must always progress left-to-right. A `READMODEL → SCREEN` connection going right-to-left is a layout error.
528
538
 
@@ -544,7 +554,7 @@ For each view screen S that queries this read model:
544
554
 
545
555
  **View screens go in the column immediately to the right of the read model they display** — either because they were placed there in Step 3, or because you move them here now.
546
556
 
547
- ### Wire connections after placing each READMODEL (and its SCREEN)
557
+ ### Step 5g — Wire connections after placing each READMODEL (and its SCREEN)
548
558
 
549
559
  After `place-element` returns the READMODEL node ID, create the arrows that complete the slice:
550
560
 
@@ -604,13 +614,14 @@ After `place-element` returns the READMODEL node ID, create the arrows that comp
604
614
 
605
615
  Skip a connection silently if the target cell is empty. Log each created arrow: `→ connected EVENT→READMODEL "OrderPlaced"→"OrderStatusView"`, `→ connected READMODEL→SCREEN "OrderStatusView"→"Order Status Screen"`, or `→ connected READMODEL→AUTOMATION "OrderStatusView"→"Fulfillment Processor"`.
606
616
 
607
- ### Mandatory per-node verification (run before declaring this step done)
617
+ ### Step 5h — Mandatory per-node verification (run before declaring this step done)
608
618
 
609
619
  Do not declare Step 5 complete on the strength of the read models you happened to design. Instead, **re-fetch every SCREEN and AUTOMATION node on the board** (`get_nodes` per type — don't rely on the list built earlier in this step, the board may have moved on) and check each one individually:
610
620
 
611
621
  1. Does it now have an incoming `READMODEL → SCREEN` or `READMODEL → AUTOMATION` connection?
612
622
  2. If not — is it a provably blank creation form with no prior state? State the reason in one line (e.g. `"Register Account" screen: blank form, no prior state — exempt`).
613
623
  3. If it's neither connected nor exempt, it is an **unresolved gap**. Fix it now: design the missing read model (pulling from its `meta.fields`/`mapping` as above) and wire the connection. Do not move to Step 6 with an unresolved gap silently carried forward — either fix it or explicitly flag it to the user as accepted debt.
624
+ 4. Does any screen still carry more than one component undivided (a Step 5a/5b miss)? If so, break it apart now per Step 5b before counting it as resolved.
614
625
 
615
626
  List the result of this pass (connected / exempt / fixed) for every screen and automation checked — this list is the evidence the orchestrator's Step 5 gate ("every screen data need is satisfied by a read model") actually holds, not just an assumption.
616
627
 
@@ -729,7 +740,9 @@ Identify UI needs without event sources:
729
740
  - [ ] **Every SCREEN from storyboarding is connected to at least one read model** (via `READMODEL → SCREEN`); only blank creation forms may be exempt — verified via the mandatory per-node pass above, not assumed
730
741
  - [ ] **Every AUTOMATION from storyboarding is connected to at least one read model** (via `READMODEL → AUTOMATION`) — same per-node verification
731
742
  - [ ] **No read model is placed without a connected SCREEN or AUTOMATION consumer**
732
- - [ ] **No read model spans more than one visually distinct screen region** — a screen with N distinct data regions gets N read models and N highlighted screen copies, not one screen-wide read model
743
+ - [ ] **No read model spans more than one component** — a screen with N distinct components gets N read models and N highlighted screen copies (same screen name, one component crisp per copy), not one screen-wide read model
744
+ - [ ] **Every multi-component screen was broken apart in Step 5b** — each copy keeps the original screen's name, differs only in which component is marked/highlighted
745
+ - [ ] **Roll-up components are flagged, not force-split** — a single homogeneous list/table with wide event fan-in is documented as an explicit assumption (inline MARKDOWN note, per "Documenting decisions inline"), not silently accepted or mistaken for an unsplit multi-component screen
733
746
  - [ ] Every read model has clear purpose
734
747
  - [ ] Every data field has event source
735
748
  - [ ] Update logic for each event is explicit
@@ -35,8 +35,12 @@ These rules govern how every element is placed on the board. Enforce them throug
35
35
  ### Never stack read models at the end
36
36
  Placing all read models in new columns at the very end of the timeline severs the visual connection to the events they're derived from. The board must show a coherent left-to-right narrative where each slice is self-contained.
37
37
 
38
- ### One read model per screen region the main defense against backward arrows
39
- A screen-wide read model that has to aggregate from events scattered across many columns is the most common source of backward-arrow layout errors: it can only occupy one column, but its regions each pull from different, differently-positioned source events. The fix is architectural, not just positional — **split the read model by UI region before it ever gets placed.** A screen with a stats row and a list below it is two read models and two screen copies (each copy showing the full screen with the non-relevant region blurred/dimmed via CSS, the relevant region left normal), not one. Each narrower read model then sits naturally close to its own source event, and the `EVENT READMODEL SCREEN` chain for each region stays short and forward. See `eventmodeling-identifying-outputs` ("One read model per screen region") and `eventmodeling-storyboarding-events` ("View screens with multiple data regions get one copy per region") for the mechanics — apply this during Steps 3 and 5, before the placement problem exists, rather than reordering columns to patch it afterward.
38
+ ### One read model per component, not one read model per screen
39
+ A read model does not necessarily serve a whole screen **one component in a screen resembles one read model.** Storyboarding (Step 3) only produces a plain screen per screen state; it does not decide components. That decision, and the work of breaking a multi-component screen apart into copies, happens in Step 5 (Identifying Outputs), because a component is defined by its read model.
40
+
41
+ A screen-wide read model that has to aggregate from events scattered across many columns is the most common source of unnecessary coupling — a "god read model." A screen with a stats row and a list below it is two components: two read models and two screen copies of the *same page* (same screen name on both copies; the copies differ only in which component is marked/highlighted crisp while the other is blurred/dimmed), not one screen-wide read model. Each narrower read model then sits naturally close to its own source event, and the `EVENT → READMODEL → SCREEN` chain for each component stays short and forward. See `eventmodeling-identifying-outputs`'s "Step 5a — Enumerate consumers and identify components" and "Step 5b — Break apart multi-component screens into copies" for the mechanics — apply this during Step 5, before the placement problem exists, rather than reordering columns to patch it afterward.
42
+
43
+ Not every wide fan-in is this problem, though: a single homogeneous list/table (e.g. a catalog whose per-row status comes from many different lifecycle events) is still **one** component, and its read model legitimately needs to subscribe to many events spread across the timeline — that's a "roll-up" component, not a god read model. Treat that classification as an assumption, not a given: `eventmodeling-identifying-outputs` Step 5a records it as an inline MARKDOWN note (per "Documenting decisions inline" above) explaining why the component is one roll-up rather than several bundled ones, instead of force-splitting it or letting it pass unremarked.
40
44
 
41
45
  ### No unplaced elements (0,0 nodes)
42
46
 
@@ -248,11 +252,13 @@ role or system processor.
248
252
 
249
253
  Invoke `eventmodeling-identifying-outputs`.
250
254
 
251
- **Input**: Event list + Commands from Step 4.
255
+ **Input**: Event list + Commands from Step 4 + the plain screens placed in Step 3.
252
256
  **Output to carry forward**: Read model definitions — projections of events
253
- optimized for UI and processor queries.
257
+ optimized for UI and processor queries — one per screen component, with any
258
+ multi-component screen already broken apart into same-named, highlighted
259
+ screen copies (this step's Step 5a/5b, not Step 3's job).
254
260
  **Gate**: Every screen data need from the storyboards is satisfied by a read
255
- model.
261
+ model, and no read model spans more than one component.
256
262
 
257
263
  ---
258
264
 
@@ -559,16 +559,9 @@ When placing screens on the board, follow these alignment rules:
559
559
 
560
560
  > **Do not create standalone screen columns that are disconnected from commands or read models.** Every screen must either share its column with the command it submits, or be placed one column to the right of the read model it displays.
561
561
 
562
- ### View screens with multiple data regions get one copy per region
562
+ ### Multi-component screens are broken apart in Step 5, not here
563
563
 
564
- If a view screen has more than one visually distinct data region (a stats row, a list below it, a summary card next to a detail panel, etc.), **do not design it as a single screen fed by one screen-wide read model.** Each region gets its own screen copy and, in Step 5, its own read model in its own column:
565
-
566
- - Render the **same full screen layout** once per region.
567
- - In each copy, the region that copy is meant to foreground stays normal; every other region is visually de-emphasized — `filter: blur(3px); opacity: 0.45; pointer-events: none;` on the wrapper of the non-relevant region(s) reads clearly in an HTML_SCREEN render (Bulma classes still apply normally underneath the blur).
568
- - Title each copy after the region, e.g. `"Librarian Dashboard — Statistics"` and `"Librarian Dashboard — Recently Added"`, not both just `"Librarian Dashboard"`.
569
- - Place each copy in its own column, one column to the right of the read model that will feed it (per the table above) — this is normally a different column per copy, since each region typically has a different natural source event.
570
-
571
- This is why the copies matter even though the underlying screen looks the same: it keeps each `READMODEL → SCREEN` connection narrow and forward (see `eventmodeling-identifying-outputs`'s "One read model per screen region" section), instead of one wide read model forced to straddle far-apart source events and pushed into a single column that can't sit correctly relative to every region's own event.
564
+ Storyboarding renders **one plain screen per screen state** do not pre-split a screen into per-component copies here. Deciding how many components a view screen actually has, and breaking it apart into one highlighted copy per component, is `eventmodeling-identifying-outputs`'s job (its "Step 5a Enumerate consumers and identify components" and "Step 5b — Break apart multi-component screens into copies"), because a component is defined by its read model and read models aren't designed until Step 5. During storyboarding, just place the single screen one column to the right of where its read model will end up (per the table above); document which read model it will query even before that read model exists.
572
565
 
573
566
  ### Placing Automations
574
567
 
@@ -68,18 +68,56 @@ Guidelines:
68
68
 
69
69
  ### Marks — only when the user explicitly asks for one
70
70
 
71
- The canvas has a native "Marks" feature (outline highlight, plus an optional "blur outside" or "white outside" spotlight) for calling out part of a screen — see `HtmlEditorModal.tsx`'s Highlight tool and `markBlur.ts`/`markStyles.ts` in the main app. **Do not add marks by default.** Only apply the effects below when the request explicitly asks to highlight/mark/call out/circle/spotlight, or blur/obscure/white-out part of the screen (e.g. "highlight the submit button", "blur everything except the email field", "white out everything but the header"). An ordinary "design a screen" request gets no marks.
71
+ The canvas has a native "Marks" feature (outline highlight, plus an optional "blur outside" or "white outside" spotlight) for calling out part of a screen — confirmed against the app's own source (`HtmlEditorModal.tsx`, `canvas/nodes/markHtml.ts`, `markBlur.ts`, `markStyles.ts`, `canvas/nodes/HtmlScreenNode.tsx`). **Do not add marks by default.** Only apply marks when the request explicitly asks to highlight/mark/call out/circle/spotlight, or blur/obscure/white-out part of the screen (e.g. "highlight the submit button", "blur everything except the email field", "white out everything but the header"), or when another skill's own instructions ask for a component to be marked/highlighted (e.g. `eventmodeling-identifying-outputs` Step 5b, which explicitly requests a mark per screen copy — that counts as an explicit ask). An ordinary "design a screen" request gets no marks.
72
72
 
73
- This skill only has a `pages`/`backgroundColor` field to send (no separate marks API the native feature persists marks as board metadata, not through this render call), so reproduce the same visual language directly as inline CSS on the target element(s), self-contained in the page HTML same as any other styling in Step 3. No `<script>`/`<style>` tags are needed (and `<script>` is stripped anyway) inline `style="..."` reproduces the same CSS the native feature injects:
73
+ **Never fake this with hand-written inline CSS** (`filter:blur(...)`, `outline:...`, `opacity:...`, `pointer-events:...` on page elements) that is not how the native feature works and won't match how the app itself renders a mark. A real mark has **two halves that must both be set together**, in the same page/meta update:
74
74
 
75
- - **Mark / highlight an area** add to the target element's `style`: `outline:4px solid <color> !important;outline-offset:1px;`. Default color `#e74c3c` (red) unless the user names one; other options mirror the app's mark picker (`ColorPicker.tsx`): `#1e293b` (dark slate), `#2ecc71` (green), `#3b82f6` (blue), `#f1c40f` (yellow), `#ffffff` (white).
76
- - **Blur outside / spotlight an area** — add `style="filter:blur(6px) !important;"` to every other top-level sibling/section on the page so only the called-out element stays sharp.
77
- - **White outside / spotlight an area** — same idea, but instead add `style="filter:brightness(0) invert(1) !important;"` to every other top-level sibling/section (collapses them to solid white works uniformly across text, shapes, and images, unlike a plain background-color override). Use this only when the user says "white out" / "whiteout" rather than "blur" — the two are mutually exclusive per mark in the native tool, so never apply both blur and white filters to the same sibling.
78
- - Combine the outline rule with either spotlight rule if the user asked to both mark *and* blur/white-out.
75
+ **Half 1 bake the mark onto the target element, inside the page HTML itself.** Pick the one element that should stay sharp (a row, a card, a tile whatever the "component" is) and add two attributes to it, merging into any `class`/other attributes it already has:
76
+ - `data-em-mark-id="em-<short-unique-id>"` a unique id for this mark, referenced by half 2 below.
77
+ - `class="em-mark em-mark-<colorhex-without-#>"` e.g. `em-mark em-mark-e74c3c` for the default red `#e74c3c`. This class is what actually draws the `outline:4px solid <color> !important;outline-offset:1px;` its stylesheet is injected by the app itself at render time, not something to write into the page.
79
78
 
80
- Apply these only to the specific element(s) the request describesdon't guess at additional areas to call out.
79
+ Do not add any blur/white styling directly to other elementsthat part is entirely driven by half 2.
81
80
 
82
- **Marked screens and field scoping**: when the same underlying screen is rendered multiple times as separate nodes once per slice, each with a different mark/highlight calling out a different part of the UI scope each node's `meta.fields` (Step 5 below) to only the data inside that node's highlighted area, not the full screen. Three slice-specific screen nodes sharing one visual base should end up with three different, narrower field lists, each matching what that node's mark calls out.
81
+ **Half 2add the matching entry to `meta.marks`**, an array of objects (one per mark on this node, across all its pages):
82
+
83
+ ```json
84
+ {
85
+ "id": "em-<same-id-as-the-data-em-mark-id-attribute>",
86
+ "color": "#e74c3c",
87
+ "pageIndex": 0,
88
+ "blurOutside": true,
89
+ "whiteOutside": false
90
+ }
91
+ ```
92
+
93
+ - `id` — must exactly match the `data-em-mark-id` value baked into the page in half 1. This is how the app's render script finds the marked element and applies blur/white to every *other* top-level branch of `<body>` (walking down, only recursing into branches that contain the marked element — see `markBlur.ts`'s `markOutsideScript`). No coordinates are needed or supported; targeting is purely by this shared id.
94
+ - `color` — must match the color used in the `em-mark-<colorhex>` class in half 1. Default `#e74c3c` (red) unless the user names one; other options mirror the app's mark picker: `#1e293b` (dark slate), `#2ecc71` (green), `#3b82f6` (blue), `#f1c40f` (yellow), `#ffffff` (white).
95
+ - `pageIndex` — which page in the `pages` array this mark's target element is on (0-based).
96
+ - `blurOutside` — `true` to blur every other top-level section (a spotlight effect). Mutually exclusive with `whiteOutside`.
97
+ - `whiteOutside` — `true` to collapse every other top-level section to solid white instead of blurring it. Use only when the user says "white out"/"whiteout" rather than "blur" — never set both `blurOutside` and `whiteOutside` on the same mark.
98
+
99
+ Set both halves in one `node:changed` call (or immediately paired calls) so the node is never left with one half but not the other:
100
+
101
+ ```
102
+ mcp__eventmodelers__submit_node_events {
103
+ "boardId": "<BOARD_ID>",
104
+ "events": [{
105
+ "id": "<event-uuid>", "eventType": "node:changed", "nodeId": "<NODE_ID>",
106
+ "boardId": "<BOARD_ID>", "timestamp": <NOW_MS>,
107
+ "meta": {
108
+ "type": "HTML_SCREEN",
109
+ "title": "<Screen Title>",
110
+ "pages": ["<div>...<tr data-em-mark-id=\"em-abc123\" class=\"em-mark em-mark-e74c3c\">...crisp row...</tr>...</div>"],
111
+ "marks": [{"id": "em-abc123", "color": "#e74c3c", "pageIndex": 0, "blurOutside": true, "whiteOutside": false}],
112
+ "fields": [...]
113
+ }
114
+ }]
115
+ }
116
+ ```
117
+
118
+ Pick a fresh, short random suffix for each mark id (`em-` + a few random alphanumeric characters) — don't reuse one across nodes/marks.
119
+
120
+ **Marked screens and field scoping**: when the same underlying screen is rendered multiple times as separate nodes — once per slice, each with a different mark calling out a different part of the UI — scope each node's `meta.fields` (Step 5 below) to only the data inside that node's marked area, not the full screen. Three slice-specific screen nodes sharing one visual base should end up with three different, narrower field lists, each matching what that node's mark calls out.
83
121
 
84
122
  ## Step 4 — Render the pages
85
123