sad-mcp 1.1.3 → 1.1.4

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": "sad-mcp",
3
- "version": "1.1.3",
3
+ "version": "1.1.4",
4
4
  "description": "MCP server for Software Analysis and Design course materials at BGU",
5
5
  "type": "module",
6
6
  "bin": {
@@ -28,6 +28,8 @@ 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.
31
33
 
32
34
  ---
33
35
 
@@ -56,12 +58,20 @@ The shared files prepended to this prompt define the layout recipes, model shape
56
58
 
57
59
  **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
60
 
61
+ **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.
62
+
59
63
  ---
60
64
 
61
65
  ## 3. Use Case Rules
62
66
 
63
67
  **Granularity**: one complete user goal per UC, achievable in one session.
64
68
 
69
+ **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.
70
+
71
+ - ✅ Preferred verbs: `רישום`, `הזנה`, `עדכון`, `הצגה`, `הפקה` (of a report), `שליחה` (of a notification), `אישור`, `הקצאה`, `חיפוש`, `סגירה`, `פתיחה` (of a record).
72
+ - ❌ Bare physical verbs that describe things happening outside the system: `הכנת משלוח` (physical preparing), `ספירת מלאי` (physical counting), `מסירת חבילה` (physical delivering), `תשלום במזומן` (physical paying).
73
+ - **Fix rule:** when the requirement uses a physical verb, wrap it with a system verb. `הכנת משלוח` → `רישום הכנת משלוח` or `סימון משלוח כמוכן`. `ביצוע ספירת מלאי` → `הזנת תוצאות ספירת מלאי`. `קבלת משלוח` → `קליטת משלוח נכנס` (data entry on arrival).
74
+
65
75
  **When to combine vs. separate "ניהול X":**
66
76
  - Simple entity (just a form): single "ניהול X" is fine.
67
77
  - Complex entity with sub-items, lifecycle states, or different actors per operation: separate UCs (open / update / cancel / approve).
@@ -225,17 +235,30 @@ function fixLayout(layout) {
225
235
  sortCol(cols.left, 'left');
226
236
  sortCol(cols.right, 'right');
227
237
 
228
- // Step 3 — Initial Y assignment per column.
229
- function place(ucs, colX) {
238
+ // Step 3 — Adaptive column X positions. Account for actual ellipse widths so
239
+ // adjacent columns never overlap horizontally. Widen the boundary if needed.
240
+ const COL_GAP = 30;
241
+ const maxRx = ucs => (ucs.length ? Math.max(...ucs.map(u => u.rx)) : 0);
242
+ const lRx = maxRx(cols.left), cRx = maxRx(cols.center), rRx = maxRx(cols.right);
243
+ const gaps =
244
+ (cRx ? 2 * COL_GAP : (lRx && rRx ? 2 * COL_GAP : 0));
245
+ const needed = 2 * UC_PAD + 2 * lRx + 2 * cRx + 2 * rRx + gaps;
246
+ if (b.width < needed) b.width = needed;
247
+
248
+ const leftX = b.x + UC_PAD + lRx;
249
+ const rightX = b.x + b.width - UC_PAD - rRx;
250
+ const centerX = (leftX + rightX) / 2;
251
+
252
+ function place(ucs, colX, colName) {
230
253
  ucs.forEach((uc, i) => {
231
254
  uc.cy = b.y + HEADER_H + UC_PAD + uc.ry + i * (uc.ry * 2 + UC_GAP);
232
255
  uc.cx = colX;
233
- uc.col = ucSide[uc.id] || 'center';
256
+ uc.col = colName;
234
257
  });
235
258
  }
236
- place(cols.left, b.x + b.width * 0.28);
237
- place(cols.right, b.x + b.width * 0.72);
238
- place(cols.center, b.x + b.width * 0.50);
259
+ place(cols.left, leftX, 'left');
260
+ place(cols.right, rightX, 'right');
261
+ place(cols.center, centerX, 'center');
239
262
 
240
263
  // Step 4 — Clamp and resolve overlaps. Idempotent fixed-point loop.
241
264
  const minX = b.x + UC_PAD;
@@ -323,6 +346,9 @@ function fixLayout(layout) {
323
346
  p[3] = Math.max(parseFloat(p[3]), b.y + b.height + 120);
324
347
  svg.setAttribute('viewBox', p.join(' '));
325
348
  }
349
+
350
+ // Step 10 — Decorate timed associations (see §8 renderTimingLabels).
351
+ if (typeof renderTimingLabels === 'function') renderTimingLabels(layout);
326
352
  }
327
353
  ```
328
354
 
@@ -420,7 +446,52 @@ document.addEventListener('click', e => {
420
446
  UC group: `<g id="uc-uc1" data-uc-id="uc1" onclick="showDesc('uc1','uc',event)" style="cursor:pointer">`
421
447
  Actor group: `<g id="actor-student" data-actor-id="student" onclick="showDesc('student','actor',event)" style="cursor:pointer">`
422
448
 
423
- **Association labels (timing/frequency):** when an actor connection has a timing annotation (e.g., "פעם ביום" for a cron actor), add a `<text>` with a white `<rect>` behind it at the midpoint of the line after `fixLayout` has set the endpoints.
449
+ ### Timing labels on cron/time-actor associations (required)
450
+
451
+ 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.
452
+
453
+ ```javascript
454
+ function renderTimingLabels(layout) {
455
+ const svg = document.getElementById(layout.svgId);
456
+ if (!svg) return;
457
+ svg.querySelectorAll('.timing-label').forEach(el => el.remove());
458
+ const NS = 'http://www.w3.org/2000/svg';
459
+ (layout.connections || []).forEach(conn => {
460
+ if (!conn.timing) return;
461
+ const line = svg.querySelector(
462
+ `[data-conn-from="${conn.from}"][data-conn-to="${conn.to}"]`);
463
+ if (!line) return;
464
+ const x1 = +line.getAttribute('x1'), y1 = +line.getAttribute('y1');
465
+ const x2 = +line.getAttribute('x2'), y2 = +line.getAttribute('y2');
466
+ const mx = (x1 + x2) / 2, my = (y1 + y2) / 2;
467
+ const w = 10 + conn.timing.length * 7, h = 16;
468
+ const g = document.createElementNS(NS, 'g');
469
+ g.setAttribute('class', 'timing-label');
470
+ const rect = document.createElementNS(NS, 'rect');
471
+ rect.setAttribute('x', mx - w / 2); rect.setAttribute('y', my - h / 2);
472
+ rect.setAttribute('width', w); rect.setAttribute('height', h);
473
+ rect.setAttribute('fill', 'white'); rect.setAttribute('stroke', '#334155');
474
+ rect.setAttribute('stroke-width', '0.8'); rect.setAttribute('rx', '3');
475
+ const text = document.createElementNS(NS, 'text');
476
+ text.setAttribute('x', mx); text.setAttribute('y', my + 4);
477
+ text.setAttribute('font-size', '10'); text.setAttribute('text-anchor', 'middle');
478
+ text.setAttribute('fill', '#334155');
479
+ text.setAttribute('font-family', "'Noto Sans Hebrew', sans-serif");
480
+ text.setAttribute('direction', 'rtl');
481
+ text.textContent = conn.timing;
482
+ g.appendChild(rect); g.appendChild(text);
483
+ svg.appendChild(g);
484
+ });
485
+ }
486
+ ```
487
+
488
+ 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.
489
+
490
+ The `connections` entry for a timed link looks like:
491
+
492
+ ```javascript
493
+ { from: 'cron', to: 'uc-daily-reconcile', type: 'association', timing: 'פעם ביום' }
494
+ ```
424
495
 
425
496
  ---
426
497
 
@@ -540,5 +611,8 @@ For a single-tab diagram, drop the tab bar, the split-rationale line, and the se
540
611
  - [ ] `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
612
  - [ ] UC and actor description popups wired to every `data-uc-id` / `data-actor-id` element.
542
613
  - [ ] System actors drawn as stick figures with `«system»` label (not rectangles).
614
+ - [ ] 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.).
615
+ - [ ] 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.
616
+ - [ ] `fixLayout` uses adaptive column X (widens boundary if needed so left/center/right ellipses never overlap horizontally).
543
617
  - [ ] 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
618
  - [ ] `diagramModel` defined per `model-shape.md`; VP export button present and wired to `exportXMI()`.