sad-mcp 1.1.7 → 1.1.8
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
|
@@ -87,11 +87,29 @@ The shared files prepended to this prompt define the layout recipes, model shape
|
|
|
87
87
|
|
|
88
88
|
---
|
|
89
89
|
|
|
90
|
-
## 4.
|
|
90
|
+
## 4. Relationship Gates — Mandatory Justifications
|
|
91
91
|
|
|
92
|
-
|
|
92
|
+
Every relationship in the diagram — actor-UC associations, «include», and «extend» — must pass a written justification block BEFORE it is drawn. **If you cannot fill the block in truthfully, the relationship does not exist.** Write these blocks in your planning text (not in the final HTML).
|
|
93
93
|
|
|
94
|
-
###
|
|
94
|
+
### 4.1 Association justification (actor ↔ UC)
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
Association: [actor id] ↔ [UC id]
|
|
98
|
+
Actor role: initiator / participant / recipient / supplier
|
|
99
|
+
Does this actor TRIGGER / INITIATE the UC? yes / no
|
|
100
|
+
One-line justification: "The actor ___ in order to ___"
|
|
101
|
+
Description shown on click (Hebrew): ___________________
|
|
102
|
+
|
|
103
|
+
→ If the actor has no meaningful interaction with the UC → DELETE the association.
|
|
104
|
+
→ Every UC MUST have ≥1 association where "initiates = yes". A UC with no initiator is orphaned — fix it or delete the UC.
|
|
105
|
+
→ A human actor connected only as "recipient" of a notification is usually wrong — the initiator of the notification-sending UC is who you want (see CRITICAL RULE #10, no send-only UCs).
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The "Description shown on click" is the text surfaced in the HTML by the `showAssocDesc` popup — see §8. Store it on the connection object under `description`.
|
|
109
|
+
|
|
110
|
+
### 4.2 «include» justification
|
|
111
|
+
|
|
112
|
+
**Default to zero.** Treat every «include» as guilty until proven innocent.
|
|
95
113
|
|
|
96
114
|
```
|
|
97
115
|
«include» candidate: __________________________________
|
|
@@ -109,7 +127,7 @@ Semantics: «include» means the target runs **every single time** the base runs
|
|
|
109
127
|
|
|
110
128
|
Arrow: dashed, open arrowhead, `Base UC → Included UC`.
|
|
111
129
|
|
|
112
|
-
### «extend» justification
|
|
130
|
+
### 4.3 «extend» justification
|
|
113
131
|
|
|
114
132
|
```
|
|
115
133
|
«extend» candidate: __________________________________
|
|
@@ -414,9 +432,9 @@ If neither trigger fires, produce a single tab. Do not split on gut feeling.
|
|
|
414
432
|
|
|
415
433
|
---
|
|
416
434
|
|
|
417
|
-
## 8. Clickable Descriptions for UCs and
|
|
435
|
+
## 8. Clickable Descriptions for UCs, Actors, and Associations (Always Include)
|
|
418
436
|
|
|
419
|
-
Every UC ellipse **and
|
|
437
|
+
Every UC ellipse, every actor, **and every association line** must be clickable — click shows a floating panel with name + description. One `showDesc(id, type, event)` function handles all three.
|
|
420
438
|
|
|
421
439
|
```javascript
|
|
422
440
|
const useCaseDescriptions = {
|
|
@@ -427,10 +445,18 @@ const actorDescriptions = {
|
|
|
427
445
|
'customer': { name: 'לקוח', desc: 'לקוח הרשום במערכת. יוזם הזמנות ועוקב אחר סטטוס.' },
|
|
428
446
|
// ... one entry per actor
|
|
429
447
|
};
|
|
448
|
+
const assocDescriptions = {
|
|
449
|
+
// keyed by connection id; name = actor-name ↔ UC-name (auto-composed is fine)
|
|
450
|
+
'a1': { name: 'לקוח ↔ פתיחת הזמנה', desc: 'הלקוח יוזם את פתיחת ההזמנה. תפקיד: Initiator.' },
|
|
451
|
+
// ... one entry per association line you want described
|
|
452
|
+
};
|
|
430
453
|
|
|
431
454
|
function showDesc(id, type, event) {
|
|
432
455
|
hideDesc();
|
|
433
|
-
const data = type === 'uc'
|
|
456
|
+
const data = type === 'uc' ? useCaseDescriptions[id]
|
|
457
|
+
: type === 'actor' ? actorDescriptions[id]
|
|
458
|
+
: type === 'assoc' ? assocDescriptions[id]
|
|
459
|
+
: null;
|
|
434
460
|
if (!data) return;
|
|
435
461
|
const panel = document.createElement('div');
|
|
436
462
|
panel.id = 'desc-panel';
|
|
@@ -454,13 +480,50 @@ function showDesc(id, type, event) {
|
|
|
454
480
|
}
|
|
455
481
|
function hideDesc() { document.getElementById('desc-panel')?.remove(); }
|
|
456
482
|
document.addEventListener('click', e => {
|
|
457
|
-
if (!e.target.closest('[data-uc-id]') &&
|
|
483
|
+
if (!e.target.closest('[data-uc-id]') &&
|
|
484
|
+
!e.target.closest('[data-actor-id]') &&
|
|
485
|
+
!e.target.closest('[data-conn-id]')) hideDesc();
|
|
458
486
|
});
|
|
459
487
|
```
|
|
460
488
|
|
|
461
489
|
UC group: `<g id="uc-uc1" data-uc-id="uc1" onclick="showDesc('uc1','uc',event)" style="cursor:pointer">`
|
|
462
490
|
Actor group: `<g id="actor-student" data-actor-id="student" onclick="showDesc('student','actor',event)" style="cursor:pointer">`
|
|
463
491
|
|
|
492
|
+
### Clickable association lines (required)
|
|
493
|
+
|
|
494
|
+
A 1.8px line is nearly impossible to click. Every associatable line gets **two** line elements: a visible thin line (the one `fixLayout` already updates via `data-conn-from` / `data-conn-to`) and a transparent thick hitbox on top.
|
|
495
|
+
|
|
496
|
+
```html
|
|
497
|
+
<!-- Visible line — fixLayout writes x1/y1/x2/y2 here -->
|
|
498
|
+
<line data-conn-from="worker" data-conn-to="uc-receive"
|
|
499
|
+
x1="0" y1="0" x2="0" y2="0"
|
|
500
|
+
stroke="#334155" stroke-width="1.8"/>
|
|
501
|
+
|
|
502
|
+
<!-- Click hitbox — invisible, tracks the same endpoints, carries the id -->
|
|
503
|
+
<line class="conn-hitbox" data-conn-id="a1"
|
|
504
|
+
data-hitbox-from="worker" data-hitbox-to="uc-receive"
|
|
505
|
+
x1="0" y1="0" x2="0" y2="0"
|
|
506
|
+
stroke="transparent" stroke-width="14"
|
|
507
|
+
style="cursor:pointer"
|
|
508
|
+
onclick="showDesc('a1','assoc',event)"/>
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
Inside `fixLayout` Step 8, copy the endpoints from each visible line to its matching hitbox so the clickable region tracks the visible line exactly:
|
|
512
|
+
|
|
513
|
+
```javascript
|
|
514
|
+
// After writing x1/y1/x2/y2 on the visible line:
|
|
515
|
+
const hitbox = svg.querySelector(
|
|
516
|
+
`.conn-hitbox[data-hitbox-from="${fId}"][data-hitbox-to="${tId}"]`);
|
|
517
|
+
if (hitbox) {
|
|
518
|
+
hitbox.setAttribute('x1', x1); hitbox.setAttribute('y1', y1);
|
|
519
|
+
hitbox.setAttribute('x2', x2); hitbox.setAttribute('y2', y2);
|
|
520
|
+
}
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
Draw order: visible line first, hitbox second, ellipses/actors last. The hitbox stays under the ellipses and actors but above the thin visible line so clicks on or near the line hit it.
|
|
524
|
+
|
|
525
|
+
Associations without a meaningful description (e.g., auto-generated from trivially obvious actor-UC pairs) may omit the hitbox and the `assocDescriptions` entry, but they still need to pass the §4.1 justification gate.
|
|
526
|
+
|
|
464
527
|
### Timing labels on cron/time-actor associations (required)
|
|
465
528
|
|
|
466
529
|
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.
|
|
@@ -514,6 +577,21 @@ The `connections` entry for a timed link looks like:
|
|
|
514
577
|
|
|
515
578
|
Define this variable in every generated HTML `<script>` block, following the `model-shape.md` spec for `kind: 'use-case'`. Every actor, use case, and relationship must be represented. The Visual Paradigm exporter walks this variable.
|
|
516
579
|
|
|
580
|
+
### Assumptions and open questions
|
|
581
|
+
|
|
582
|
+
Both `diagramModel` and `diagramLayout` carry two parallel string arrays, matching the BPMN skill's shape:
|
|
583
|
+
|
|
584
|
+
```javascript
|
|
585
|
+
{
|
|
586
|
+
// ...
|
|
587
|
+
assumptions: [ 'המערכת מזהה כל עובד מחסן דרך מזהה משתמש יחיד.',
|
|
588
|
+
'ייבוא נתונים מה-ERP של הספק מתבצע באמצעות API ולא קובץ.' ],
|
|
589
|
+
openQuestions: [ 'האם נדרש אישור נוסף על ידי מנהל לכל שינוי רמות מלאי?' ]
|
|
590
|
+
}
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
Render them in a panel at the bottom of the page — see §11. Only render if at least one of the arrays is non-empty. Do NOT hide the panel; a reader reviewing the diagram needs to see which statements in the diagram rest on assumptions.
|
|
594
|
+
|
|
517
595
|
---
|
|
518
596
|
|
|
519
597
|
## 10. Styling
|
|
@@ -529,9 +607,25 @@ Association line: #334155, 1.8px, no arrowhead
|
|
|
529
607
|
Include/extend: #7c3aed, 1.6px, dasharray 6 4, open arrowhead, label 9.5px
|
|
530
608
|
Generalization: #334155, 1.8px, hollow triangle (fill white)
|
|
531
609
|
System title: white on #1e40af, 14px bold
|
|
610
|
+
Assumptions panel: white bg, border #cbd5e1 (1px), rx=8, padding 16px, margin-top 20px
|
|
611
|
+
Assumptions h3: #1e40af, 13px, weight 600, margin-bottom 6px
|
|
612
|
+
Assumptions li: #334155, 12px, line-height 1.6, list-style disc inside
|
|
532
613
|
Fonts: 'Noto Sans Hebrew' + 'IBM Plex Mono' from Google Fonts
|
|
533
614
|
```
|
|
534
615
|
|
|
616
|
+
The assumptions panel CSS must be included in the HTML `<style>` block:
|
|
617
|
+
|
|
618
|
+
```css
|
|
619
|
+
.assumptions-panel { display: flex; gap: 24px; max-width: 1000px;
|
|
620
|
+
margin: 20px auto; padding: 16px 20px; background: #fff;
|
|
621
|
+
border: 1px solid #cbd5e1; border-radius: 8px; direction: rtl; }
|
|
622
|
+
.assumptions-col { flex: 1; min-width: 0; }
|
|
623
|
+
.assumptions-col h3 { margin: 0 0 6px; font-size: 13px; font-weight: 600;
|
|
624
|
+
color: #1e40af; }
|
|
625
|
+
.assumptions-col ul { margin: 0; padding-right: 18px; }
|
|
626
|
+
.assumptions-col li { color: #334155; font-size: 12px; line-height: 1.6; }
|
|
627
|
+
```
|
|
628
|
+
|
|
535
629
|
---
|
|
536
630
|
|
|
537
631
|
## 11. HTML File Structure
|
|
@@ -571,6 +665,18 @@ Fonts: 'Noto Sans Hebrew' + 'IBM Plex Mono' from Google Fonts
|
|
|
571
665
|
<svg id="diagram-svg-1" viewBox="0 0 1100 900" xmlns="http://www.w3.org/2000/svg">...</svg>
|
|
572
666
|
</div>
|
|
573
667
|
|
|
668
|
+
<!-- Assumptions / open-questions panel (render only if at least one non-empty) -->
|
|
669
|
+
<div id="assumptions-panel" class="assumptions-panel" hidden>
|
|
670
|
+
<div class="assumptions-col" id="assumptions-col">
|
|
671
|
+
<h3>הנחות</h3>
|
|
672
|
+
<ul id="assumptions-list"></ul>
|
|
673
|
+
</div>
|
|
674
|
+
<div class="assumptions-col" id="open-questions-col">
|
|
675
|
+
<h3>שאלות פתוחות</h3>
|
|
676
|
+
<ul id="open-questions-list"></ul>
|
|
677
|
+
</div>
|
|
678
|
+
</div>
|
|
679
|
+
|
|
574
680
|
<!-- VP export button -->
|
|
575
681
|
<button class="vp-export-btn" onclick="exportXMI()">Download .xmi (Visual Paradigm)</button>
|
|
576
682
|
|
|
@@ -595,8 +701,31 @@ Fonts: 'Noto Sans Hebrew' + 'IBM Plex Mono' from Google Fonts
|
|
|
595
701
|
function xmiBody(elements, rels) { /* export-buttons.md */ }
|
|
596
702
|
function exportXMI() { /* export-buttons.md */ }
|
|
597
703
|
|
|
704
|
+
function renderAssumptions() {
|
|
705
|
+
const a = (diagramModel && diagramModel.assumptions) || [];
|
|
706
|
+
const q = (diagramModel && diagramModel.openQuestions) || [];
|
|
707
|
+
const panel = document.getElementById('assumptions-panel');
|
|
708
|
+
if (!panel) return;
|
|
709
|
+
if (!a.length && !q.length) { panel.hidden = true; return; }
|
|
710
|
+
panel.hidden = false;
|
|
711
|
+
const ul = id => document.getElementById(id);
|
|
712
|
+
const fill = (listEl, items) => {
|
|
713
|
+
listEl.innerHTML = '';
|
|
714
|
+
items.forEach(s => {
|
|
715
|
+
const li = document.createElement('li');
|
|
716
|
+
li.textContent = s;
|
|
717
|
+
listEl.appendChild(li);
|
|
718
|
+
});
|
|
719
|
+
};
|
|
720
|
+
fill(ul('assumptions-list'), a);
|
|
721
|
+
fill(ul('open-questions-list'), q);
|
|
722
|
+
document.getElementById('assumptions-col').hidden = !a.length;
|
|
723
|
+
document.getElementById('open-questions-col').hidden = !q.length;
|
|
724
|
+
}
|
|
725
|
+
|
|
598
726
|
document.addEventListener('DOMContentLoaded', () => {
|
|
599
727
|
diagramLayouts.forEach(l => fixLayout(l));
|
|
728
|
+
renderAssumptions();
|
|
600
729
|
});
|
|
601
730
|
</script>
|
|
602
731
|
</body>
|
|
@@ -616,6 +745,9 @@ Fonts: 'Noto Sans Hebrew' + 'IBM Plex Mono' from Google Fonts
|
|
|
616
745
|
|
|
617
746
|
## 13. Pre-Delivery Checklist
|
|
618
747
|
|
|
748
|
+
- [ ] Every actor-UC association has a written §4.1 justification block (role, initiates? one-line "why"); every UC has ≥1 association with `initiates = yes`.
|
|
749
|
+
- [ ] Every association line carries a transparent click-hitbox (stroke-width ≥12, `data-conn-id`) wired to `showDesc(id,'assoc',event)`; `assocDescriptions` entry exists for each id.
|
|
750
|
+
- [ ] `assumptions` and `openQuestions` arrays are present in `diagramModel`; the HTML renders the bottom panel whenever either is non-empty.
|
|
619
751
|
- [ ] Every «include» and «extend» has a written §4 justification block; any that cannot be filled in has been deleted.
|
|
620
752
|
- [ ] No «include» with exactly one incoming arrow — every include target has ≥2 bases AND zero actor associations.
|
|
621
753
|
- [ ] No generic "ניהול הגדרות מערכת" or combined-entity UCs; each entity has its own UC.
|