sad-mcp 1.1.3 → 1.1.5
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
|
@@ -28,6 +28,9 @@ The shared files prepended to this prompt define the layout recipes, model shape
|
|
|
28
28
|
5. **Every use case must have at least one association line to an actor.**
|
|
29
29
|
6. **Actors must not overlap** — minimum 200px vertical distance between actor centers.
|
|
30
30
|
7. **Default: zero «include» and zero «extend».** Most diagrams need none. More than two is almost always wrong. §4 defines a gate every such relationship must pass before you draw it.
|
|
31
|
+
8. **UC labels describe actions WITH the information system, not physical real-world actions.** See §3 for the full rule and verb list.
|
|
32
|
+
9. **Every system-time / cron actor association MUST carry a timing label** (e.g., "פעם ביום", "כל 5 דקות"). See §2 and §8 for rendering.
|
|
33
|
+
10. **No "send-only" use cases.** If a UC's entire behavior is sending a message / notification / alert / email / SMS, it is not a UC — it is a step inside the UC that *decided* to send it. See §3.
|
|
31
34
|
|
|
32
35
|
---
|
|
33
36
|
|
|
@@ -56,18 +59,32 @@ The shared files prepended to this prompt define the layout recipes, model shape
|
|
|
56
59
|
|
|
57
60
|
**System administrator** — include unless explicitly excluded. Responsible for managing the master data of every entity the system references. Each entity gets its own specific UC (not one catch-all).
|
|
58
61
|
|
|
62
|
+
**System-time / cron actor (שחקן זמן).** Draw as a system-style actor (amber head, `«system»` label). Every association line from a time actor MUST carry a visible timing label at the midpoint (e.g., `פעם ביום`, `כל 5 דקות`, `בסוף חודש`). Without a timing label the trigger frequency is ambiguous and the diagram is incomplete. Rendering: the label is injected as a `<g class="timing-label">` (white `<rect>` + `<text>`) after `fixLayout` sets the endpoint coordinates — see §8.
|
|
63
|
+
|
|
59
64
|
---
|
|
60
65
|
|
|
61
66
|
## 3. Use Case Rules
|
|
62
67
|
|
|
63
68
|
**Granularity**: one complete user goal per UC, achievable in one session.
|
|
64
69
|
|
|
70
|
+
**Labels describe actions WITH the information system, not physical real-world actions.** A use case is the actor's interaction with the IS; the verb must imply data entry, query, or a system-state change.
|
|
71
|
+
|
|
72
|
+
- ✅ Preferred verbs: `רישום`, `הזנה`, `עדכון`, `הצגה`, `הפקה` (of a report), `שליחה` (of a notification), `אישור`, `הקצאה`, `חיפוש`, `סגירה`, `פתיחה` (of a record).
|
|
73
|
+
- ❌ Bare physical verbs that describe things happening outside the system: `הכנת משלוח` (physical preparing), `ספירת מלאי` (physical counting), `מסירת חבילה` (physical delivering), `תשלום במזומן` (physical paying).
|
|
74
|
+
- **Fix rule:** when the requirement uses a physical verb, wrap it with a system verb. `הכנת משלוח` → `רישום הכנת משלוח` or `סימון משלוח כמוכן`. `ביצוע ספירת מלאי` → `הזנת תוצאות ספירת מלאי`. `קבלת משלוח` → `קליטת משלוח נכנס` (data entry on arrival).
|
|
75
|
+
|
|
65
76
|
**When to combine vs. separate "ניהול X":**
|
|
66
77
|
- Simple entity (just a form): single "ניהול X" is fine.
|
|
67
78
|
- Complex entity with sub-items, lifecycle states, or different actors per operation: separate UCs (open / update / cancel / approve).
|
|
68
79
|
|
|
69
80
|
**Implied UCs**: every entity the system references needs at least one UC that manages it.
|
|
70
81
|
|
|
82
|
+
**No "send-only" use cases.** A UC must carry non-trivial behavior (decision, data entry, state change, non-trivial computation). If the only behavior is firing a message, it is not a UC — it is a system step inside the UC that *decided* to send.
|
|
83
|
+
|
|
84
|
+
- ❌ Wrong UCs: `שליחת התראה`, `שליחת מייל אישור`, `שליחת SMS`, `הודעה למנהל`, `שליחת דוח`.
|
|
85
|
+
- ✅ Fold into the decider: the alert send-step belongs inside `עדכון רמות מלאי` (which *detected* the low stock); the confirmation email belongs inside `אישור הזמנה` (which *decided* to confirm).
|
|
86
|
+
- This rule applies even when the send is triggered by a condition — conditional sending is part of the conditional UC, not its own UC.
|
|
87
|
+
|
|
71
88
|
---
|
|
72
89
|
|
|
73
90
|
## 4. Include and Extend — Mandatory Justification Gate
|
|
@@ -225,17 +242,30 @@ function fixLayout(layout) {
|
|
|
225
242
|
sortCol(cols.left, 'left');
|
|
226
243
|
sortCol(cols.right, 'right');
|
|
227
244
|
|
|
228
|
-
// Step 3 —
|
|
229
|
-
|
|
245
|
+
// Step 3 — Adaptive column X positions. Account for actual ellipse widths so
|
|
246
|
+
// adjacent columns never overlap horizontally. Widen the boundary if needed.
|
|
247
|
+
const COL_GAP = 30;
|
|
248
|
+
const maxRx = ucs => (ucs.length ? Math.max(...ucs.map(u => u.rx)) : 0);
|
|
249
|
+
const lRx = maxRx(cols.left), cRx = maxRx(cols.center), rRx = maxRx(cols.right);
|
|
250
|
+
const gaps =
|
|
251
|
+
(cRx ? 2 * COL_GAP : (lRx && rRx ? 2 * COL_GAP : 0));
|
|
252
|
+
const needed = 2 * UC_PAD + 2 * lRx + 2 * cRx + 2 * rRx + gaps;
|
|
253
|
+
if (b.width < needed) b.width = needed;
|
|
254
|
+
|
|
255
|
+
const leftX = b.x + UC_PAD + lRx;
|
|
256
|
+
const rightX = b.x + b.width - UC_PAD - rRx;
|
|
257
|
+
const centerX = (leftX + rightX) / 2;
|
|
258
|
+
|
|
259
|
+
function place(ucs, colX, colName) {
|
|
230
260
|
ucs.forEach((uc, i) => {
|
|
231
261
|
uc.cy = b.y + HEADER_H + UC_PAD + uc.ry + i * (uc.ry * 2 + UC_GAP);
|
|
232
262
|
uc.cx = colX;
|
|
233
|
-
uc.col =
|
|
263
|
+
uc.col = colName;
|
|
234
264
|
});
|
|
235
265
|
}
|
|
236
|
-
place(cols.left,
|
|
237
|
-
place(cols.right,
|
|
238
|
-
place(cols.center,
|
|
266
|
+
place(cols.left, leftX, 'left');
|
|
267
|
+
place(cols.right, rightX, 'right');
|
|
268
|
+
place(cols.center, centerX, 'center');
|
|
239
269
|
|
|
240
270
|
// Step 4 — Clamp and resolve overlaps. Idempotent fixed-point loop.
|
|
241
271
|
const minX = b.x + UC_PAD;
|
|
@@ -314,15 +344,26 @@ function fixLayout(layout) {
|
|
|
314
344
|
line.setAttribute('x2', x2); line.setAttribute('y2', y2);
|
|
315
345
|
});
|
|
316
346
|
|
|
317
|
-
// Step 9 — Resize boundary rect and SVG viewBox.
|
|
347
|
+
// Step 9 — Resize boundary rect and SVG viewBox (BOTH width and height).
|
|
318
348
|
const br = svg.querySelector('.system-boundary, rect[rx="12"]');
|
|
319
|
-
if (br) br.setAttribute('height', b.height);
|
|
349
|
+
if (br) { br.setAttribute('width', b.width); br.setAttribute('height', b.height); }
|
|
320
350
|
const vb = svg.getAttribute('viewBox');
|
|
321
351
|
if (vb) {
|
|
322
352
|
const p = vb.split(/\s+/);
|
|
353
|
+
// Height: fit boundary + any bottom actors.
|
|
323
354
|
p[3] = Math.max(parseFloat(p[3]), b.y + b.height + 120);
|
|
355
|
+
// Width: fit the rightmost actor + label margin. Without this, widening the
|
|
356
|
+
// boundary pushes right-column actors off the visible SVG.
|
|
357
|
+
const ACTOR_LABEL_MARGIN = 120; // covers stick figure width + longest label
|
|
358
|
+
const maxActorX = layout.actors.length
|
|
359
|
+
? Math.max(...layout.actors.map(a => a.x))
|
|
360
|
+
: b.x + b.width;
|
|
361
|
+
p[2] = Math.max(parseFloat(p[2]), maxActorX + ACTOR_LABEL_MARGIN);
|
|
324
362
|
svg.setAttribute('viewBox', p.join(' '));
|
|
325
363
|
}
|
|
364
|
+
|
|
365
|
+
// Step 10 — Decorate timed associations (see §8 renderTimingLabels).
|
|
366
|
+
if (typeof renderTimingLabels === 'function') renderTimingLabels(layout);
|
|
326
367
|
}
|
|
327
368
|
```
|
|
328
369
|
|
|
@@ -420,7 +461,52 @@ document.addEventListener('click', e => {
|
|
|
420
461
|
UC group: `<g id="uc-uc1" data-uc-id="uc1" onclick="showDesc('uc1','uc',event)" style="cursor:pointer">`
|
|
421
462
|
Actor group: `<g id="actor-student" data-actor-id="student" onclick="showDesc('student','actor',event)" style="cursor:pointer">`
|
|
422
463
|
|
|
423
|
-
|
|
464
|
+
### Timing labels on cron/time-actor associations (required)
|
|
465
|
+
|
|
466
|
+
When a connection carries a `timing` field (every connection from a system-time actor MUST have one — see §2), inject the label at the line midpoint **after** `fixLayout` has written the endpoints. The helper below is idempotent: it removes any existing `.timing-label` group before re-rendering, so calling it again after another `fixLayout` pass is safe.
|
|
467
|
+
|
|
468
|
+
```javascript
|
|
469
|
+
function renderTimingLabels(layout) {
|
|
470
|
+
const svg = document.getElementById(layout.svgId);
|
|
471
|
+
if (!svg) return;
|
|
472
|
+
svg.querySelectorAll('.timing-label').forEach(el => el.remove());
|
|
473
|
+
const NS = 'http://www.w3.org/2000/svg';
|
|
474
|
+
(layout.connections || []).forEach(conn => {
|
|
475
|
+
if (!conn.timing) return;
|
|
476
|
+
const line = svg.querySelector(
|
|
477
|
+
`[data-conn-from="${conn.from}"][data-conn-to="${conn.to}"]`);
|
|
478
|
+
if (!line) return;
|
|
479
|
+
const x1 = +line.getAttribute('x1'), y1 = +line.getAttribute('y1');
|
|
480
|
+
const x2 = +line.getAttribute('x2'), y2 = +line.getAttribute('y2');
|
|
481
|
+
const mx = (x1 + x2) / 2, my = (y1 + y2) / 2;
|
|
482
|
+
const w = 10 + conn.timing.length * 7, h = 16;
|
|
483
|
+
const g = document.createElementNS(NS, 'g');
|
|
484
|
+
g.setAttribute('class', 'timing-label');
|
|
485
|
+
const rect = document.createElementNS(NS, 'rect');
|
|
486
|
+
rect.setAttribute('x', mx - w / 2); rect.setAttribute('y', my - h / 2);
|
|
487
|
+
rect.setAttribute('width', w); rect.setAttribute('height', h);
|
|
488
|
+
rect.setAttribute('fill', 'white'); rect.setAttribute('stroke', '#334155');
|
|
489
|
+
rect.setAttribute('stroke-width', '0.8'); rect.setAttribute('rx', '3');
|
|
490
|
+
const text = document.createElementNS(NS, 'text');
|
|
491
|
+
text.setAttribute('x', mx); text.setAttribute('y', my + 4);
|
|
492
|
+
text.setAttribute('font-size', '10'); text.setAttribute('text-anchor', 'middle');
|
|
493
|
+
text.setAttribute('fill', '#334155');
|
|
494
|
+
text.setAttribute('font-family', "'Noto Sans Hebrew', sans-serif");
|
|
495
|
+
text.setAttribute('direction', 'rtl');
|
|
496
|
+
text.textContent = conn.timing;
|
|
497
|
+
g.appendChild(rect); g.appendChild(text);
|
|
498
|
+
svg.appendChild(g);
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
Call `renderTimingLabels(layout)` at the end of every `fixLayout` invocation, **and** on `DOMContentLoaded` (after the initial `fixLayout`). Add it inside `showDiagram(index)` too so the label tracks the active tab.
|
|
504
|
+
|
|
505
|
+
The `connections` entry for a timed link looks like:
|
|
506
|
+
|
|
507
|
+
```javascript
|
|
508
|
+
{ from: 'cron', to: 'uc-daily-reconcile', type: 'association', timing: 'פעם ביום' }
|
|
509
|
+
```
|
|
424
510
|
|
|
425
511
|
---
|
|
426
512
|
|
|
@@ -540,5 +626,9 @@ For a single-tab diagram, drop the tab bar, the split-rationale line, and the se
|
|
|
540
626
|
- [ ] `fixLayout` runs on `DOMContentLoaded` and on the Fix Layout button; uses named constants (HEADER_H, UC_PAD, UC_GAP, ACTOR_PAD), not magic numbers.
|
|
541
627
|
- [ ] UC and actor description popups wired to every `data-uc-id` / `data-actor-id` element.
|
|
542
628
|
- [ ] System actors drawn as stick figures with `«system»` label (not rectangles).
|
|
629
|
+
- [ ] Every UC label describes an action WITH the information system (data entry, query, state change) — no bare physical verbs (`הכנת X`, `ספירת X`, `מסירת X`, `תשלום במזומן`). Physical verbs are wrapped with a system verb (`רישום`, `הזנה`, `עדכון`, `סימון`, etc.).
|
|
630
|
+
- [ ] Every system-time / cron actor association has a `timing` field (and the rendered diagram shows a visible label at the line midpoint) — no unlabeled cron connections.
|
|
631
|
+
- [ ] No "send-only" UCs (no bare `שליחת X` / `הודעה ל-X`); message-sends are folded into the UC that decided to send.
|
|
632
|
+
- [ ] `fixLayout` uses adaptive column X (widens boundary if needed so left/center/right ellipses never overlap horizontally) AND expands viewBox width so right-side actors are not clipped.
|
|
543
633
|
- [ ] If split: each tab has a split-rationale line citing the actual trigger that fired (count > 8, or 2+ disjoint actor groups); `showDiagram` and per-tab `fixLayout` wired per §11.
|
|
544
634
|
- [ ] `diagramModel` defined per `model-shape.md`; VP export button present and wired to `exportXMI()`.
|