skilldrop-cli 0.16.22 → 0.16.23
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 +1 -1
- package/packs/solution-architect/skills/interactive-diagram/SKILL.md +37 -9
- package/packs/solution-architect/skills/interactive-diagram/evals/evals.json +90 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/10/build-4127-timing.json +179 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/11/src/org/example/greenhouse/AbstractSensor.java +36 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/11/src/org/example/greenhouse/AlertService.java +15 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/11/src/org/example/greenhouse/Greenhouse.java +36 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/11/src/org/example/greenhouse/Sensor.java +10 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/11/src/org/example/greenhouse/SoilMoistureSensor.java +25 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/11/src/org/example/greenhouse/TemperatureSensor.java +19 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/11/src/org/example/greenhouse/Zone.java +26 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/6/notes.txt +7 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/6/trace-quote-slow.jsonl +13 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/7/oncall-notes.md +16 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/7/system-log.txt +14 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/8/run-4471.json +151 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/9/chat-export.txt +16 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/9/lesson-sync.log +24 -0
- package/packs/solution-architect/skills/interactive-diagram/evals/files/9/ticket-8812.csv +6 -0
- package/packs/solution-architect/skills/interactive-diagram/examples/checkout-map.html +25 -8
- package/packs/solution-architect/skills/interactive-diagram/examples/checkout-trace.html +203 -0
- package/packs/solution-architect/skills/interactive-diagram/examples/checkout-trace.json +110 -0
- package/packs/solution-architect/skills/interactive-diagram/examples/incident-timeline.html +203 -0
- package/packs/solution-architect/skills/interactive-diagram/examples/incident-timeline.json +21 -0
- package/packs/solution-architect/skills/interactive-diagram/examples/inputs/incident/oncall-notes.md +14 -0
- package/packs/solution-architect/skills/interactive-diagram/examples/inputs/trace/checkout-trace.jsonl +9 -0
- package/packs/solution-architect/skills/interactive-diagram/examples/leave-approval.html +25 -8
- package/packs/solution-architect/skills/interactive-diagram/manifest.json +2 -2
- package/packs/solution-architect/skills/interactive-diagram/reference.md +86 -6
- package/packs/solution-architect/skills/interactive-diagram/scripts/build_diagram.py +1025 -102
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" data-theme-default="auto"><head><meta charset="utf-8">
|
|
3
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'sha256-KdTstAM29gVIP104X7Tju5/PTeQKbRDMMG1TjJdsOyI='; style-src 'unsafe-inline'; img-src data: blob:; base-uri 'none'; form-action 'none'">
|
|
4
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
5
|
+
<!-- One self-contained file: no external scripts, styles, fonts or requests. The policy above makes the browser refuse any. -->
|
|
6
|
+
<title>Card payments failing, 3 October</title><meta name="generator" content="interactive-diagram (skilldrop)"></head>
|
|
7
|
+
<body>
|
|
8
|
+
<header class="top"><h1>Card payments failing, 3 October</h1><p class="q">What happened, and how long did it take anyone to notice?</p><p class="sum">Errors climbed seven minutes after the 2.14 rollout, but the page fired 22 minutes later because the alert waits for a fixed 5% to hold. From the page, rollback took 28 minutes.</p><div class="findings"><b>What it shows</b><ul><li>From 03 Oct 09:12 to 06 Oct 14:00: 3d 4h.</li><li>Longest quiet stretch: 3d 3h, between Status page: resolved (03 Oct 10:30) and Postmortem (06 Oct 14:00).</li></ul></div><p class="muted" style="font-size:12px;margin:6px 0 0">11 of 11 file citations checked against the repository</p></header>
|
|
9
|
+
<section class="text" id="outline" aria-label="The diagram as text">
|
|
10
|
+
<details open><summary>The diagram as text</summary><h2>Events, in time order</h2><p>03 Oct 09:12 to 06 Oct 14:00 (3d 4h).</p><ol><li><b>03 Oct 09:12</b> v2.14 rolled out <span class="muted">(Deploys)</span> <span class="ev">(<code>oncall-notes.md:5</code> ✓)</span></li><li><b>03 Oct 09:19</b> Card errors pass 5% <span class="muted">(Monitoring)</span> <span class="ev">(<code>oncall-notes.md:6</code> ✓)</span></li><li><b>03 Oct 09:41</b> Pager fires <span class="muted">(Monitoring)</span> <span class="ev">(<code>oncall-notes.md:7</code> ✓)</span></li><li><b>03 Oct 09:44</b> Ana acknowledges <span class="muted">(On-call)</span> <span class="ev">(<code>oncall-notes.md:8</code> ✓)</span></li><li><b>03 Oct 09:51</b> Rollout named as the cause <span class="muted">(On-call)</span> <span class="ev">(<code>oncall-notes.md:9</code> ✓)</span></li><li><b>03 Oct 09:58</b> Rollback to v2.13 <span class="muted">(Deploys)</span> <span class="ev">(<code>oncall-notes.md:10-11</code> ✓)</span></li><li><b>03 Oct 10:11</b> Errors back under 0.5% <span class="muted">(Monitoring)</span> <span class="ev">(<code>oncall-notes.md:12</code> ✓)</span></li><li><b>03 Oct 10:30</b> Status page: resolved <span class="muted">(On-call)</span> <span class="ev">(<code>oncall-notes.md:13</code> ✓)</span></li><li><b>06 Oct 14:00</b> Postmortem <span class="muted">(On-call)</span> <span class="ev">(<code>oncall-notes.md:14</code> ✓)</span></li></ol><h2>Notes</h2><ul><li>Customer impact (how many payments failed) is not in the notes, so it is not on the timeline.</li></ul></details></section>
|
|
11
|
+
<nav class="bar" aria-label="Diagram controls">
|
|
12
|
+
<input id="find" type="search" placeholder="Find a part…" aria-label="Find a part">
|
|
13
|
+
<button id="zin" title="Zoom in">+</button><button id="zout" title="Zoom out">−</button><button id="zfit" title="Fit to screen">Fit</button>
|
|
14
|
+
<span class="sep"></span><button id="tour">Walk through</button><button id="prev" title="Previous step">←</button><button id="next" title="Next step">→</button>
|
|
15
|
+
<span class="sep"></span><button id="xsvg">SVG</button><button id="xpng">PNG</button><button id="xmmd" title="Copy and download Mermaid">Mermaid</button>
|
|
16
|
+
<button id="theme" title="Light or dark">◐</button>
|
|
17
|
+
<span class="hint">Click a part for detail and evidence · shift-click a second part for the path · drag to pan · scroll to zoom</span></nav>
|
|
18
|
+
<div class="legend"><span>● event</span><span>◆ milestone</span><span>▬ lasted from … to</span><span>// a quiet stretch, shortened</span></div>
|
|
19
|
+
<div class="caption" id="caption" aria-live="polite"></div>
|
|
20
|
+
<div class="wrap"><div id="stage"><svg id="map" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1401 368" width="1401" height="368" role="group" aria-label="Card payments failing, 3 October"><defs><marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="userSpaceOnUse" markerWidth="10" markerHeight="10" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" class="arrowhead"/></marker><marker id="arrow-start" viewBox="0 0 10 10" refX="1" refY="5" markerUnits="userSpaceOnUse" markerWidth="10" markerHeight="10" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" class="arrowhead"/></marker><marker id="tri" viewBox="0 0 12 12" refX="11" refY="6" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto-start-reverse"><path d="M1,1 L11,6 L1,11 z" class="mk-open"/></marker><marker id="open" viewBox="0 0 12 12" refX="11" refY="6" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto-start-reverse"><path d="M1,1 L11,6 L1,11" class="mk-line"/></marker><marker id="dia-solid" viewBox="0 0 16 10" refX="15" refY="5" markerUnits="userSpaceOnUse" markerWidth="18" markerHeight="11" orient="auto-start-reverse"><path d="M1,5 L8,1 L15,5 L8,9 z" class="arrowhead"/></marker><marker id="dia-open" viewBox="0 0 16 10" refX="15" refY="5" markerUnits="userSpaceOnUse" markerWidth="18" markerHeight="11" orient="auto-start-reverse"><path d="M1,5 L8,1 L15,5 L8,9 z" class="mk-open"/></marker></defs><rect class="canvas" x="0" y="0" width="1401" height="368"/><g class="lane"><rect x="20" y="70.0" width="1341.5" height="68.0" rx="8"/><text class="glabel" x="32" y="124.0">Deploys</text><line class="lane-line" x1="180.0" y1="120.0" x2="1300.0" y2="120.0"/></g><g class="lane"><rect x="20" y="148.0" width="1341.5" height="68.0" rx="8"/><text class="glabel" x="32" y="202.0">Monitoring</text><line class="lane-line" x1="180.0" y1="198.0" x2="1300.0" y2="198.0"/></g><g class="lane"><rect x="20" y="226.0" width="1341.5" height="102.0" rx="8"/><text class="glabel" x="32" y="314.0">On-call</text><line class="lane-line" x1="180.0" y1="310.0" x2="1300.0" y2="310.0"/></g><g class="axis"><line x1="294.2" y1="44" x2="294.2" y2="52"/><text x="294.2" y="38" text-anchor="middle">03 Oct 09:20</text><line x1="424.5" y1="44" x2="424.5" y2="52"/><text x="424.5" y="38" text-anchor="middle">09:30</text><line x1="554.7" y1="44" x2="554.7" y2="52"/><text x="554.7" y="38" text-anchor="middle">09:40</text><line x1="685.0" y1="44" x2="685.0" y2="52"/><text x="685.0" y="38" text-anchor="middle">09:50</text><line x1="815.2" y1="44" x2="815.2" y2="52"/><text x="815.2" y="38" text-anchor="middle">10:00</text><line x1="945.5" y1="44" x2="945.5" y2="52"/><text x="945.5" y="38" text-anchor="middle">10:10</text><line x1="1075.7" y1="44" x2="1075.7" y2="52"/><text x="1075.7" y="38" text-anchor="middle">10:20</text><line x1="1206.0" y1="44" x2="1206.0" y2="52"/><text x="1206.0" y="38" text-anchor="middle">10:30</text><line x1="1270.0" y1="44" x2="1270.0" y2="52"/><text x="1270.0" y="38" text-anchor="middle">06 Oct 14:00</text><path class="brk" d="M1231.0,40 l6,16 M1239.0,40 l6,16"/><text class="brk-label" x="1238.0" y="66" text-anchor="middle">3d 3h later</text><line x1="180.0" y1="48" x2="1300.0" y2="48"/></g><g class="edge s-solid" id="e-e1" data-id="e1" data-from="deploy" data-to="climb"><title>caused</title><path class="hit" d="M190.0,120.0 L281.2,198.0"/><path class="line" d="M190.0,120.0 L281.2,198.0" marker-end="url(#arrow)"/></g><g class="edge s-solid" id="e-e2" data-id="e2" data-from="climb" data-to="page"><title>detected 22 min later</title><path class="hit" d="M281.2,198.0 L567.7,198.0"/><path class="line" d="M281.2,198.0 L567.7,198.0" marker-end="url(#arrow)"/></g><g class="node event c-amber k-change" id="n-deploy" data-id="deploy" tabindex="0" role="button" aria-label="03 Oct 09:12 v2.14 rolled out"><title>03 Oct 09:12 — v2.14 rolled out</title><line class="leader" x1="190.0" y1="113.0" x2="190.0" y2="98.0"/><circle class="shape" cx="190.0" cy="120.0" r="6.5"/><text class="label tl" x="187.0" y="94.0">v2.14 rolled out</text><text class="note tl" x="187.0" y="81.0">03 Oct 09:12</text></g><g class="node event c-rose k-alert" id="n-climb" data-id="climb" tabindex="0" role="button" aria-label="03 Oct 09:19 Card errors pass 5%"><title>03 Oct 09:19 — Card errors pass 5%</title><line class="leader" x1="281.2" y1="191.0" x2="281.2" y2="176.0"/><circle class="shape" cx="281.2" cy="198.0" r="6.5"/><text class="label tl" x="278.2" y="172.0">Card errors pass 5%</text><text class="note tl" x="278.2" y="159.0">03 Oct 09:19</text></g><g class="node event c-rose k-alert" id="n-page" data-id="page" tabindex="0" role="button" aria-label="03 Oct 09:41 Pager fires"><title>03 Oct 09:41 — Pager fires</title><line class="leader" x1="567.7" y1="191.0" x2="567.7" y2="176.0"/><circle class="shape" cx="567.7" cy="198.0" r="6.5"/><text class="label tl" x="564.7" y="172.0">Pager fires</text><text class="note tl" x="564.7" y="159.0">03 Oct 09:41</text></g><g class="node event c-green k-action" id="n-ack" data-id="ack" tabindex="0" role="button" aria-label="03 Oct 09:44 Ana acknowledges"><title>03 Oct 09:44 — Ana acknowledges</title><line class="leader" x1="606.8" y1="303.0" x2="606.8" y2="288.0"/><circle class="shape" cx="606.8" cy="310.0" r="6.5"/><text class="label tl" x="603.8" y="284.0">Ana acknowledges</text><text class="note tl" x="603.8" y="271.0">03 Oct 09:44</text></g><g class="node event c-green k-action" id="n-cause" data-id="cause" tabindex="0" role="button" aria-label="03 Oct 09:51 Rollout named as the cause"><title>03 Oct 09:51 — Rollout named as the cause</title><line class="leader" x1="698.0" y1="303.0" x2="698.0" y2="254.0"/><circle class="shape" cx="698.0" cy="310.0" r="6.5"/><text class="label tl" x="695.0" y="250.0">Rollout named as the cause</text><text class="note tl" x="695.0" y="237.0">03 Oct 09:51</text></g><g class="node event c-amber k-change" id="n-rollback" data-id="rollback" tabindex="0" role="button" aria-label="03 Oct 09:58 Rollback to v2.13"><title>03 Oct 09:58 — Rollback to v2.13</title><rect class="shape bar" x="789.2" y="115.0" width="143.3" height="10" rx="5"/><line class="leader" x1="789.2" y1="113.0" x2="789.2" y2="98.0"/><circle class="shape" cx="789.2" cy="120.0" r="6.5"/><text class="label tl" x="786.2" y="94.0">Rollback to v2.13</text><text class="note tl" x="786.2" y="81.0">03 Oct 09:58</text></g><g class="node event c-violet k-milestone" id="n-normal" data-id="normal" tabindex="0" role="button" aria-label="03 Oct 10:11 Errors back under 0.5%"><title>03 Oct 10:11 — Errors back under 0.5%</title><line class="leader" x1="958.5" y1="191.0" x2="958.5" y2="176.0"/><path class="shape" d="M958.5,190.0 l8,8 l-8,8 l-8,-8 z"/><text class="label tl" x="955.5" y="172.0">Errors back under 0.5%</text><text class="note tl" x="955.5" y="159.0">03 Oct 10:11</text></g><g class="node event c-green k-action" id="n-status" data-id="status" tabindex="0" role="button" aria-label="03 Oct 10:30 Status page: resolved"><title>03 Oct 10:30 — Status page: resolved</title><line class="leader" x1="1206.0" y1="303.0" x2="1206.0" y2="288.0"/><circle class="shape" cx="1206.0" cy="310.0" r="6.5"/><text class="label tl" x="1203.0" y="284.0">Status page: resolved</text><text class="note tl" x="1203.0" y="271.0">03 Oct 10:30</text></g><g class="node event c-violet k-milestone" id="n-pm" data-id="pm" tabindex="0" role="button" aria-label="06 Oct 14:00 Postmortem"><title>06 Oct 14:00 — Postmortem</title><line class="leader" x1="1270.0" y1="303.0" x2="1270.0" y2="254.0"/><path class="shape" d="M1270.0,302.0 l8,8 l-8,8 l-8,-8 z"/><text class="label tl" x="1267.0" y="250.0">Postmortem</text><text class="note tl" x="1267.0" y="237.0">06 Oct 14:00</text></g></svg></div><aside id="panel" aria-live="polite"></aside></div>
|
|
21
|
+
<style id="diagram-css">
|
|
22
|
+
:root{--bg:#f7f8fa;--panel:#ffffff;--ink:#16181d;--muted:#5b6170;--line:#d5d9e0;--edge:#6b7385;--accent:#2563eb;
|
|
23
|
+
--warn:#b45309;--ok:#15803d;--bad:#b91c1c;--hl:#f59e0b;--canvas:#fbfcfd;--group:#eef1f6;--group2:#e6eaf2;--shadow:0 1px 2px rgba(0,0,0,.06)}
|
|
24
|
+
:root[data-theme=dark]{--bg:#0f1115;--panel:#171a21;--ink:#e7e9ee;--muted:#9aa1b2;--line:#2b303b;--edge:#8b93a7;--accent:#60a5fa;
|
|
25
|
+
--warn:#f59e0b;--ok:#4ade80;--bad:#f87171;--hl:#fbbf24;--canvas:#12151b;--group:#1a1e27;--group2:#20252f;--shadow:none}
|
|
26
|
+
*{box-sizing:border-box}html,body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.45 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif}
|
|
27
|
+
body{display:flex;flex-direction:column}body>*{flex:none}section.text{order:10}
|
|
28
|
+
header.top{padding:18px 24px 10px;max-width:1400px}header.top h1{margin:0 0 4px;font-size:21px}
|
|
29
|
+
.q{color:var(--muted);margin:0 0 6px}.sum{margin:0;max-width:900px}
|
|
30
|
+
.bar{display:flex;flex-wrap:wrap;gap:6px;align-items:center;padding:8px 24px;position:sticky;top:0;background:var(--bg);z-index:5;border-bottom:1px solid var(--line)}
|
|
31
|
+
.bar button,.bar input{font:inherit;font-size:13px;border:1px solid var(--line);background:var(--panel);color:var(--ink);border-radius:6px;padding:4px 9px;cursor:pointer}
|
|
32
|
+
.bar input{cursor:text;width:180px}.bar button:hover{border-color:var(--accent)}.bar .sep{width:1px;height:20px;background:var(--line);margin:0 4px}
|
|
33
|
+
.bar .hint{color:var(--muted);font-size:12px;margin-left:auto}
|
|
34
|
+
.wrap{display:flex;gap:0;height:calc(100vh - 150px);min-height:420px;padding:10px 24px 0}
|
|
35
|
+
#stage{flex:1;position:relative;overflow:hidden;border:1px solid var(--line);border-radius:10px;background:var(--canvas);touch-action:none;cursor:grab}
|
|
36
|
+
#stage.drag{cursor:grabbing}#stage svg{position:absolute;left:0;top:0;width:100%;height:100%}
|
|
37
|
+
aside#panel{width:340px;margin-left:12px;border:1px solid var(--line);border-radius:10px;background:var(--panel);overflow:auto;padding:14px 16px;display:none}
|
|
38
|
+
aside#panel.open{display:block}aside h3{margin:0 0 2px;font-size:16px}aside .kind{color:var(--muted);font-size:12px;margin-bottom:8px}
|
|
39
|
+
aside .close{float:right;border:0;background:none;color:var(--muted);font-size:18px;cursor:pointer}
|
|
40
|
+
aside pre{background:var(--bg);border:1px solid var(--line);border-radius:6px;padding:8px;overflow:auto;font-size:12px;max-height:260px}
|
|
41
|
+
aside ul{padding-left:18px;margin:4px 0}aside a.jump{color:var(--accent);cursor:pointer;text-decoration:none}
|
|
42
|
+
aside .acts button{font:inherit;font-size:12px;margin:2px 4px 2px 0;border:1px solid var(--line);background:var(--bg);color:var(--ink);border-radius:5px;padding:3px 7px;cursor:pointer}
|
|
43
|
+
.caption{display:none;margin:8px 24px 0;padding:8px 12px;border-left:3px solid var(--hl);background:var(--panel);border-radius:4px}
|
|
44
|
+
.caption.on{display:block}
|
|
45
|
+
section.text{max-width:1100px;padding:10px 24px 40px}section.text h2{font-size:15px;margin:18px 0 4px}
|
|
46
|
+
.tag{font-size:11px;padding:0 5px;border-radius:4px;border:1px solid var(--line);margin-left:4px}.tag.warn{color:var(--warn);border-color:var(--warn)}
|
|
47
|
+
.tag.st-added{color:var(--ok);border-color:var(--ok)}.tag.st-removed{color:var(--bad);border-color:var(--bad)}.tag.st-changed{color:var(--accent);border-color:var(--accent)}
|
|
48
|
+
.muted{color:var(--muted)}.ev{color:var(--muted);font-size:12px}code{font-size:12px}
|
|
49
|
+
.legend{display:flex;flex-wrap:wrap;gap:14px;font-size:12px;color:var(--muted);padding:6px 24px 0}
|
|
50
|
+
.legend i{display:inline-block;width:22px;height:0;border-top:2px solid var(--edge);vertical-align:middle;margin-right:5px}
|
|
51
|
+
.legend i.d{border-top-style:dashed}.legend i.a{border-top:2px dashed var(--warn)}
|
|
52
|
+
.findings{margin:10px 0 0;padding:8px 14px;border-left:3px solid var(--accent);background:var(--panel);border-radius:4px;max-width:1000px}
|
|
53
|
+
.findings ul{margin:4px 0 0;padding-left:18px}.findings li{margin:2px 0}
|
|
54
|
+
table.answers{border-collapse:collapse;margin:10px 0 0;max-width:1100px;font-size:13px}
|
|
55
|
+
table.answers th,table.answers td{border:1px solid var(--line);padding:5px 9px;text-align:left;vertical-align:top}
|
|
56
|
+
table.answers th{background:var(--group2);font-weight:600}table.answers td.ev{font-size:12px;color:var(--muted)}
|
|
57
|
+
.issues{margin:8px 24px 0;padding:8px 12px;border:1px solid var(--warn);border-radius:6px;color:var(--warn);font-size:13px}
|
|
58
|
+
/* diagram */
|
|
59
|
+
.canvas{fill:var(--canvas)}
|
|
60
|
+
.group rect{fill:var(--group);stroke:var(--line);stroke-width:1}.group.d2 rect{fill:var(--group2)}.group.d3 rect{fill:var(--group)}
|
|
61
|
+
.glabel{font-size:12px;font-weight:600;fill:var(--muted);letter-spacing:.02em}
|
|
62
|
+
.group.st-removed rect{stroke:var(--bad);stroke-dasharray:6 4;fill:none}
|
|
63
|
+
.node{cursor:pointer;outline:none}.node .shape{stroke-width:1.5;fill:var(--panel)}.node .shape.solid{fill:var(--ink);stroke:var(--ink)}
|
|
64
|
+
.node .shape-line{fill:none;stroke-width:1.2}.node .glyph{fill:var(--muted)}
|
|
65
|
+
.node .label{font-size:13px;font-weight:600;fill:var(--ink)}.node .note{font-size:11px;fill:var(--muted)}
|
|
66
|
+
.node .badge{font-size:10px;fill:var(--ok)}.node .badge.warn{fill:var(--warn)}
|
|
67
|
+
.table-head{fill:var(--group2)}.fname{font-size:12px;fill:var(--ink)}.ftype{font-size:11px;fill:var(--muted)}.fkey{font-size:9px;font-weight:700;fill:var(--warn)}
|
|
68
|
+
.c-blue .shape,.c-blue .shape-line{stroke:#3b82f6}.c-violet .shape{stroke:#8b5cf6}.c-green .shape,.c-green .shape-line{stroke:#16a34a}
|
|
69
|
+
.c-teal .shape,.c-teal .shape-line{stroke:#0d9488}.c-amber .shape,.c-amber .shape-line{stroke:#d97706}.c-slate .shape{stroke:#64748b}
|
|
70
|
+
.c-rose .shape{stroke:#e11d48}.c-indigo .shape{stroke:#4f46e5}.k-external .shape{stroke-dasharray:5 3}
|
|
71
|
+
.node.assumed .shape{stroke:var(--warn)!important;stroke-dasharray:6 4}
|
|
72
|
+
.node.st-added .shape{stroke:var(--ok)!important;stroke-width:2.5}.node.st-changed .shape{stroke:var(--accent)!important;stroke-width:2.5}
|
|
73
|
+
.node.st-removed{opacity:.55}.node.st-removed .shape{stroke:var(--bad)!important;stroke-dasharray:6 4}.node.st-removed .label{text-decoration:line-through}
|
|
74
|
+
.edge .line{fill:none;stroke:var(--edge);stroke-width:1.5}.edge .hit{fill:none;stroke:transparent;stroke-width:12;cursor:pointer}
|
|
75
|
+
.edge.s-dashed .line{stroke-dasharray:7 5}.edge.s-dotted .line{stroke-dasharray:2 4}.edge.emph .line{stroke-width:2.6}
|
|
76
|
+
.edge.assumed .line{stroke:var(--warn);stroke-dasharray:6 4}.edge.st-added .line{stroke:var(--ok);stroke-width:2.2}
|
|
77
|
+
.edge.st-removed .line{stroke:var(--bad);stroke-dasharray:6 4;opacity:.7}
|
|
78
|
+
.arrowhead{fill:var(--edge)}.lbg{fill:var(--canvas);opacity:.92}.elabel{font-size:11px;fill:var(--muted)}.card{font-size:11px;font-weight:700;fill:var(--muted)}
|
|
79
|
+
.lifeline{stroke:var(--line);stroke-width:1.2;stroke-dasharray:4 4}.seqno{font-size:10px;fill:var(--muted)}
|
|
80
|
+
.frame rect{fill:none;stroke:var(--muted);stroke-width:1}.frame .ftab{fill:var(--group2);stroke:var(--muted)}.frame text{font-size:11px;fill:var(--ink);font-weight:600}
|
|
81
|
+
svg.focus .node,svg.focus .edge,svg.focus .group{opacity:.18;transition:opacity .15s}
|
|
82
|
+
svg.focus .on,svg.focus .group.on{opacity:1}svg .node.on .shape{stroke-width:2.6}svg .edge.on .line{stroke:var(--hl);stroke-width:2.6}
|
|
83
|
+
svg .node.sel .shape{stroke:var(--hl)!important;stroke-width:3}svg .node:focus-visible .shape{stroke:var(--accent)!important;stroke-width:3}
|
|
84
|
+
@media (prefers-reduced-motion:no-preference){svg .edge.on.flow .line{stroke-dasharray:8 6;animation:flow 1s linear infinite}}
|
|
85
|
+
@keyframes flow{to{stroke-dashoffset:-28}}
|
|
86
|
+
.lane rect{fill:var(--group);stroke:var(--line)}.lane-line{stroke:var(--line);stroke-width:2}
|
|
87
|
+
.axis line{stroke:var(--muted);stroke-width:1}.axis text{font-size:11px;fill:var(--muted)}.axis .grid{stroke:var(--line);stroke-dasharray:2 4}
|
|
88
|
+
.brk{stroke:var(--muted);stroke-width:1.5;fill:none}.brk-label{font-size:10px;fill:var(--muted)}
|
|
89
|
+
.node.event .shape{stroke-width:2;fill:var(--panel)}.node.event .bar{fill:var(--group2);stroke-width:1.5}
|
|
90
|
+
.node.event .leader{stroke:var(--line);stroke-width:1}.node.event.approx .shape{stroke-dasharray:3 2}.node .label.tl{font-size:12px}.node .note.tl{font-size:10px}
|
|
91
|
+
.node.span .rowbg{fill:transparent}.node.span:hover .rowbg,.node.span.on .rowbg{fill:var(--group)}
|
|
92
|
+
.node.span .bar{stroke:none;opacity:.88}.node.span.crit .bar{stroke:var(--hl);stroke-width:2.5;opacity:1}
|
|
93
|
+
.node.span.err .bar{stroke:var(--bad);stroke-width:2.5}.node .label.wf{font-size:12px;font-weight:500}.node .note.wf{font-size:11px}
|
|
94
|
+
.node .label.italic{font-style:italic}.mk-open{fill:var(--canvas);stroke:var(--edge);stroke-width:1.2}.mk-line{fill:none;stroke:var(--edge);stroke-width:1.4}
|
|
95
|
+
.edge.tree .line{stroke:var(--line);stroke-width:1.5}
|
|
96
|
+
aside table.facts{border-collapse:collapse;font-size:13px;margin:6px 0}aside table.facts th{text-align:left;color:var(--muted);font-weight:400;padding:2px 10px 2px 0}
|
|
97
|
+
@media print{.bar,aside,.caption{display:none!important}.wrap{height:auto}#stage{border:0}#stage svg{position:static;height:auto}}
|
|
98
|
+
</style>
|
|
99
|
+
<script type="application/json" id="diagram-data">{"type": "timeline", "slug": "card-payments-failing-3-october", "source_url": null, "story": [], "nodes": [{"id": "deploy", "label": "v2.14 rolled out", "kind": "change", "note": null, "detail": null, "fields": [], "evidence": [{"file": "oncall-notes.md", "start": 5, "end": 5, "quote": "v2.14 rolled out", "verified": true, "snippet": "09:12 deploy: payments v2.14 rolled out to all regions (release bot)"}], "assumed": false, "status": null, "facts": [["when", "03 Oct 09:12"], ["source", "Deploys"]], "group_label": null}, {"id": "climb", "label": "Card errors pass 5%", "kind": "alert", "note": null, "detail": null, "fields": [], "evidence": [{"file": "oncall-notes.md", "start": 6, "end": 6, "quote": "pass 5%", "verified": true, "snippet": "09:19 dashboard: card authorisation errors pass 5% and keep climbing"}], "assumed": false, "status": null, "facts": [["when", "03 Oct 09:19"], ["source", "Monitoring"]], "group_label": null}, {"id": "page", "label": "Pager fires", "kind": "alert", "note": null, "detail": "22 minutes after the climb began: the alert waits for 5% to hold for 20 minutes.", "fields": [], "evidence": [{"file": "oncall-notes.md", "start": 7, "end": 7, "quote": "22 minutes after", "verified": true, "snippet": "09:41 pager: \"card-auth-error-rate\" fires, 22 minutes after the climb began"}], "assumed": false, "status": null, "facts": [["when", "03 Oct 09:41"], ["source", "Monitoring"]], "group_label": null}, {"id": "ack", "label": "Ana acknowledges", "kind": "action", "note": null, "detail": null, "fields": [], "evidence": [{"file": "oncall-notes.md", "start": 8, "end": 8, "quote": "acknowledges", "verified": true, "snippet": "09:44 Ana acknowledges the page and opens the incident"}], "assumed": false, "status": null, "facts": [["when", "03 Oct 09:44"], ["source", "On-call"]], "group_label": null}, {"id": "cause", "label": "Rollout named as the cause", "kind": "action", "note": null, "detail": null, "fields": [], "evidence": [{"file": "oncall-notes.md", "start": 9, "end": 9, "quote": "started with the 2.14 rollout", "verified": true, "snippet": "09:51 Ana: errors started with the 2.14 rollout, nothing else changed"}], "assumed": false, "status": null, "facts": [["when", "03 Oct 09:51"], ["source", "On-call"]], "group_label": null}, {"id": "rollback", "label": "Rollback to v2.13", "kind": "change", "note": null, "detail": null, "fields": [], "evidence": [{"file": "oncall-notes.md", "start": 10, "end": 11, "quote": "rollback finished", "verified": true, "snippet": "09:58 rollback to v2.13 started\n10:09 rollback finished in all regions"}], "assumed": false, "status": null, "facts": [["when", "03 Oct 09:58"], ["source", "Deploys"]], "group_label": null}, {"id": "normal", "label": "Errors back under 0.5%", "kind": "milestone", "note": null, "detail": null, "fields": [], "evidence": [{"file": "oncall-notes.md", "start": 12, "end": 12, "quote": "back under 0.5%", "verified": true, "snippet": "10:11 dashboard: card errors back under 0.5%"}], "assumed": false, "status": null, "facts": [["when", "03 Oct 10:11"], ["source", "Monitoring"]], "group_label": null}, {"id": "status", "label": "Status page: resolved", "kind": "action", "note": null, "detail": null, "fields": [], "evidence": [{"file": "oncall-notes.md", "start": 13, "end": 13, "quote": "resolved", "verified": true, "snippet": "10:30 status page updated: resolved"}], "assumed": false, "status": null, "facts": [["when", "03 Oct 10:30"], ["source", "On-call"]], "group_label": null}, {"id": "pm", "label": "Postmortem", "kind": "milestone", "note": null, "detail": "Action: alert on the rise in the error rate, not on a fixed 5% for 20 minutes.", "fields": [], "evidence": [{"file": "oncall-notes.md", "start": 14, "end": 14, "quote": "postmortem held", "verified": true, "snippet": "6 Oct 14:00 postmortem held; action: alert on the error rate's rise, not a fixed 5% for 20 minutes"}], "assumed": false, "status": null, "facts": [["when", "06 Oct 14:00"], ["source", "On-call"]], "group_label": null}], "edges": [{"id": "e1", "from": "deploy", "to": "climb", "label": "caused", "detail": null, "evidence": [{"file": "oncall-notes.md", "start": 9, "end": 9, "quote": "errors started with the 2.14 rollout", "verified": true, "snippet": "09:51 Ana: errors started with the 2.14 rollout, nothing else changed"}], "assumed": false, "status": null}, {"id": "e2", "from": "climb", "to": "page", "label": "detected 22 min later", "detail": null, "evidence": [{"file": "oncall-notes.md", "start": 7, "end": 7, "verified": true, "snippet": "09:41 pager: \"card-auth-error-rate\" fires, 22 minutes after the climb began"}], "assumed": false, "status": null}], "mermaid": "timeline\n title Card payments failing, 3 October\n section Deploys\n 03 Oct 09.12 : v2.14 rolled out\n 03 Oct 09.58 : Rollback to v2.13\n section Monitoring\n 03 Oct 09.19 : Card errors pass 5%\n 03 Oct 09.41 : Pager fires\n 03 Oct 10.11 : Errors back under 0.5%\n section On-call\n 03 Oct 09.44 : Ana acknowledges\n 03 Oct 09.51 : Rollout named as the cause\n 03 Oct 10.30 : Status page - resolved\n 06 Oct 14.00 : Postmortem\n", "words": {"out": "Led to", "in": "Followed from", "reach_out": "What followed", "reach_in": "What led to it"}, "auto_steps": [{"text": "03 Oct 09:12 — v2.14 rolled out", "nodes": ["deploy"]}, {"text": "03 Oct 09:19 — Card errors pass 5%", "nodes": ["climb"]}, {"text": "03 Oct 09:41 — Pager fires", "nodes": ["page"]}, {"text": "03 Oct 09:44 — Ana acknowledges", "nodes": ["ack"]}, {"text": "03 Oct 09:51 — Rollout named as the cause", "nodes": ["cause"]}, {"text": "03 Oct 09:58 — Rollback to v2.13", "nodes": ["rollback"]}, {"text": "03 Oct 10:11 — Errors back under 0.5%", "nodes": ["normal"]}, {"text": "03 Oct 10:30 — Status page: resolved", "nodes": ["status"]}, {"text": "06 Oct 14:00 — Postmortem", "nodes": ["pm"]}]}</script>
|
|
100
|
+
<script>
|
|
101
|
+
(function(){
|
|
102
|
+
var DATA=JSON.parse(document.getElementById('diagram-data').textContent);var WD=DATA.words;
|
|
103
|
+
var svg=document.getElementById('map'),stage=document.getElementById('stage'),panel=document.getElementById('panel');
|
|
104
|
+
var root=document.documentElement;
|
|
105
|
+
var pref=localStorage.getItem('idg-theme')||root.getAttribute('data-theme-default');
|
|
106
|
+
if(pref==='auto'||!pref){pref=window.matchMedia&&matchMedia('(prefers-color-scheme: dark)').matches?'dark':'light'}
|
|
107
|
+
root.setAttribute('data-theme',pref);
|
|
108
|
+
var W=+svg.getAttribute('width'),H=+svg.getAttribute('height');svg.removeAttribute('width');svg.removeAttribute('height');
|
|
109
|
+
var vb={x:0,y:0,w:W,h:H};
|
|
110
|
+
function apply(){svg.setAttribute('viewBox',vb.x+' '+vb.y+' '+vb.w+' '+vb.h)}
|
|
111
|
+
function fit(){var r=stage.getBoundingClientRect();var s=Math.max(W/r.width,H/r.height,0.6);vb.w=r.width*s;vb.h=r.height*s;vb.x=(W-vb.w)/2;vb.y=(H-vb.h)/2;apply()}
|
|
112
|
+
function zoom(f,cx,cy){var r=stage.getBoundingClientRect();if(cx==null){cx=r.width/2;cy=r.height/2}
|
|
113
|
+
var px=vb.x+cx/r.width*vb.w,py=vb.y+cy/r.height*vb.h;var nw=Math.min(Math.max(vb.w*f,120),W*6);var k=nw/vb.w;
|
|
114
|
+
vb.x=px-(px-vb.x)*k;vb.y=py-(py-vb.y)*k;vb.w=nw;vb.h=vb.h*k;apply()}
|
|
115
|
+
stage.addEventListener('wheel',function(e){e.preventDefault();var r=stage.getBoundingClientRect();zoom(e.deltaY>0?1.12:1/1.12,e.clientX-r.left,e.clientY-r.top)},{passive:false});
|
|
116
|
+
var drag=null;
|
|
117
|
+
stage.addEventListener('pointerdown',function(e){if(e.target.closest('.node')||e.target.closest('.edge'))return;drag={x:e.clientX,y:e.clientY,vx:vb.x,vy:vb.y};stage.classList.add('drag');stage.setPointerCapture(e.pointerId)});
|
|
118
|
+
stage.addEventListener('pointermove',function(e){if(!drag)return;var r=stage.getBoundingClientRect();vb.x=drag.vx-(e.clientX-drag.x)*vb.w/r.width;vb.y=drag.vy-(e.clientY-drag.y)*vb.h/r.height;apply()});
|
|
119
|
+
stage.addEventListener('pointerup',function(){drag=null;stage.classList.remove('drag')});
|
|
120
|
+
// Lookups are Maps, never plain objects: ids come from the spec and the URL, and a key such as
|
|
121
|
+
// __proto__ must stay a key.
|
|
122
|
+
var nodes=new Map(),edges=new Map(),outE=new Map(),inE=new Map();
|
|
123
|
+
DATA.nodes.forEach(function(n){nodes.set(n.id,n);outE.set(n.id,[]);inE.set(n.id,[])});
|
|
124
|
+
DATA.edges.forEach(function(e){edges.set(e.id,e);if(outE.has(e.from))outE.get(e.from).push(e.id);if(inE.has(e.to))inE.get(e.to).push(e.id)});
|
|
125
|
+
function el(kind,id){return document.getElementById(kind+'-'+id)}
|
|
126
|
+
function clear(){svg.classList.remove('focus');svg.querySelectorAll('.on,.sel,.flow').forEach(function(x){x.classList.remove('on','sel','flow')})}
|
|
127
|
+
function light(ns,es,flow){clear();svg.classList.add('focus');ns.forEach(function(i){var x=el('n',i);x&&x.classList.add('on')});
|
|
128
|
+
es.forEach(function(i){var x=el('e',i);if(x){x.classList.add('on');flow&&x.classList.add('flow')}})}
|
|
129
|
+
function reach(id,dir){if(!nodes.has(id))return {n:[],e:[]};var seen=new Set([id]),es=[],q=[id];while(q.length){var u=q.shift();(dir>0?outE.get(u):inE.get(u)).forEach(function(eid){var e=edges.get(eid);es.push(eid);var v=dir>0?e.to:e.from;if(!seen.has(v)){seen.add(v);q.push(v)}})}
|
|
130
|
+
return {n:Array.from(seen),e:es}}
|
|
131
|
+
function path(a,b,directed){if(!nodes.has(a)||!nodes.has(b))return null;var prev=new Map([[a,null]]),q=[a];while(q.length){var u=q.shift();if(u===b)break;
|
|
132
|
+
var nb=outE.get(u).map(function(i){return [edges.get(i).to,i]});if(!directed)nb=nb.concat(inE.get(u).map(function(i){return [edges.get(i).from,i]}));
|
|
133
|
+
nb.forEach(function(p){if(!prev.has(p[0])){prev.set(p[0],[u,p[1]]);q.push(p[0])}})}
|
|
134
|
+
if(!prev.has(b))return null;var ns=[b],es=[],c=b;while(prev.get(c)){es.push(prev.get(c)[1]);c=prev.get(c)[0];ns.push(c)}return {n:ns.reverse(),e:es.reverse()}}
|
|
135
|
+
function esc(s){return String(s==null?'':s).replace(/[&<>"]/g,function(c){return {'&':'&','<':'<','>':'>','"':'"'}[c]})}
|
|
136
|
+
function evHtml(list){if(!list||!list.length)return '<p class="muted">No evidence cited.</p>';
|
|
137
|
+
return '<ul>'+list.map(function(e){var t=e.said!=null?(esc(e.source||'user')+': “'+esc(e.said)+'”'):esc(e.file+(e.start?':'+e.start+(e.end!==e.start?'-'+e.end:''):''));
|
|
138
|
+
if(e.file&&DATA.source_url){t='<a href="'+esc(DATA.source_url.replace(/\/?$/,'/')+e.file+(e.start?'#L'+e.start+(e.end!==e.start?'-L'+e.end:''):''))+'" target="_blank" rel="noopener">'+t+'</a>'}
|
|
139
|
+
var v=e.verified===true?' <span class="tag" style="color:var(--ok)">checked</span>':(e.verified===null&&e.file?' <span class="tag">not checked</span>':'');
|
|
140
|
+
return '<li>'+t+v+(e.snippet?'<pre>'+esc(e.snippet)+'</pre>':'')+'</li>'}).join('')+'</ul>'}
|
|
141
|
+
var selected=null;
|
|
142
|
+
function show(kind,id){var x=kind==='n'?nodes.get(id):edges.get(id);if(!x)return;var h='<button class="close" aria-label="Close">×</button>';
|
|
143
|
+
if(kind==='n'){h+='<h3>'+esc(x.label)+'</h3><div class="kind">'+esc(x.kind)+(x.group_label?' · '+esc(x.group_label):'')+(x.assumed?' · <span style="color:var(--warn)">assumption</span>':'')+(x.status?' · '+esc(x.status):'')+'</div>';
|
|
144
|
+
if(x.note)h+='<p>'+esc(x.note)+'</p>';if(x.detail)h+='<p>'+esc(x.detail)+'</p>';
|
|
145
|
+
if(x.fields&&x.fields.length)h+='<ul>'+x.fields.map(function(f){return '<li><code>'+esc(f.name)+'</code> '+esc(f.type||'')+(f.key?' <b>'+esc([].concat(f.key).join('/').toUpperCase())+'</b>':'')+(f.references?' → '+esc(f.references):'')+(f.note?' — '+esc(f.note):'')+'</li>'}).join('')+'</ul>';
|
|
146
|
+
if(x.facts&&x.facts.length)h+='<table class="facts">'+x.facts.map(function(f){return '<tr><th>'+esc(f[0])+'</th><td>'+esc(f[1])+'</td></tr>'}).join('')+'</table>';
|
|
147
|
+
h+='<div class="acts"><button data-act="out">'+esc(WD.reach_out)+'</button><button data-act="in">'+esc(WD.reach_in)+'</button><button data-act="near">Neighbours</button></div>';
|
|
148
|
+
h+='<p class="muted" style="font-size:12px">Shift-click another part to see the path between them.</p>';
|
|
149
|
+
var o=outE.get(id).map(function(i){var e=edges.get(i);return '<li><a class="jump" data-n="'+esc(e.to)+'">'+esc(nodes.get(e.to).label)+'</a>'+(e.label?' — '+esc(e.label):'')+'</li>'}).join('');
|
|
150
|
+
var n=inE.get(id).map(function(i){var e=edges.get(i);return '<li><a class="jump" data-n="'+esc(e.from)+'">'+esc(nodes.get(e.from).label)+'</a>'+(e.label?' — '+esc(e.label):'')+'</li>'}).join('');
|
|
151
|
+
if(o)h+='<b>'+esc(WD.out)+'</b><ul>'+o+'</ul>';if(n)h+='<b>'+esc(WD.in)+'</b><ul>'+n+'</ul>';
|
|
152
|
+
h+='<b>Evidence</b>'+evHtml(x.evidence);
|
|
153
|
+
var nb=new Set([id]);outE.get(id).concat(inE.get(id)).forEach(function(i){nb.add(edges.get(i).from);nb.add(edges.get(i).to)});light(Array.from(nb),outE.get(id).concat(inE.get(id)));el('n',id).classList.add('sel');
|
|
154
|
+
}else{h+='<h3>'+esc(nodes.get(x.from).label)+' → '+esc(nodes.get(x.to).label)+'</h3><div class="kind">'+(x.label?esc(x.label):'connection')+(x.assumed?' · <span style="color:var(--warn)">assumption</span>':'')+(x.status?' · '+esc(x.status):'')+'</div>';
|
|
155
|
+
if(x.detail)h+='<p>'+esc(x.detail)+'</p>';h+='<b>Evidence</b>'+evHtml(x.evidence);light([x.from,x.to],[id],true)}
|
|
156
|
+
panel.innerHTML=h;panel.classList.add('open');selected=kind==='n'?id:null;
|
|
157
|
+
panel.querySelector('.close').onclick=function(){panel.classList.remove('open');clear();selected=null};
|
|
158
|
+
panel.querySelectorAll('.jump').forEach(function(a){a.onclick=function(){show('n',a.getAttribute('data-n'))}});
|
|
159
|
+
panel.querySelectorAll('[data-act]').forEach(function(b){b.onclick=function(){var a=b.getAttribute('data-act');
|
|
160
|
+
if(a==='near'){show('n',id);return}var r=reach(id,a==='out'?1:-1);light(r.n,r.e,true);el('n',id).classList.add('sel')}})}
|
|
161
|
+
svg.addEventListener('click',function(e){var g=e.target.closest('.node');if(g){var id=g.getAttribute('data-id');
|
|
162
|
+
if(e.shiftKey&&selected&&selected!==id){var p=path(selected,id,true)||path(selected,id,false);if(p){light(p.n,p.e,true)}return}show('n',id);return}
|
|
163
|
+
var ed=e.target.closest('.edge');if(ed){show('e',ed.getAttribute('data-id'));return}});
|
|
164
|
+
svg.addEventListener('keydown',function(e){var g=e.target.closest&&e.target.closest('.node');if(g&&(e.key==='Enter'||e.key===' ')){e.preventDefault();show('n',g.getAttribute('data-id'))}});
|
|
165
|
+
document.addEventListener('keydown',function(e){if(e.key==='Escape'){panel.classList.remove('open');clear();selected=null}
|
|
166
|
+
if(e.target.tagName==='INPUT')return;if(e.key==='ArrowRight'&&tour)step(1);if(e.key==='ArrowLeft'&&tour)step(-1)});
|
|
167
|
+
var q=document.getElementById('find');q.addEventListener('input',function(){var v=q.value.trim().toLowerCase();if(!v){clear();return}
|
|
168
|
+
var ns=DATA.nodes.filter(function(n){return (n.label+' '+(n.note||'')+' '+(n.fields||[]).map(function(f){return f.name}).join(' ')).toLowerCase().indexOf(v)>=0}).map(function(n){return n.id});light(ns,[])});
|
|
169
|
+
document.getElementById('zin').onclick=function(){zoom(1/1.25)};document.getElementById('zout').onclick=function(){zoom(1.25)};document.getElementById('zfit').onclick=fit;
|
|
170
|
+
document.getElementById('theme').onclick=function(){var t=root.getAttribute('data-theme')==='dark'?'light':'dark';root.setAttribute('data-theme',t);try{localStorage.setItem('idg-theme',t)}catch(e){}};
|
|
171
|
+
function download(name,type,data){var a=document.createElement('a');a.href=URL.createObjectURL(new Blob([data],{type:type}));a.download=name;document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(a.href);a.remove()},500)}
|
|
172
|
+
function standalone(){var c=svg.cloneNode(true);c.setAttribute('viewBox','0 0 '+W+' '+H);c.setAttribute('width',W);c.setAttribute('height',H);c.classList.remove('focus');
|
|
173
|
+
c.querySelectorAll('.on,.sel,.flow').forEach(function(x){x.classList.remove('on','sel','flow')});
|
|
174
|
+
var cs=getComputedStyle(root),vars=['--bg','--panel','--ink','--muted','--line','--edge','--accent','--warn','--ok','--bad','--hl','--canvas','--group','--group2'].map(function(v){return v+':'+cs.getPropertyValue(v)}).join(';');
|
|
175
|
+
var st=document.createElementNS('http://www.w3.org/2000/svg','style');st.textContent='svg{'+vars+';font-family:system-ui,-apple-system,Segoe UI,Roboto,sans-serif}'+document.getElementById('diagram-css').textContent.split('/* diagram */')[1];
|
|
176
|
+
c.insertBefore(st,c.firstChild);return new XMLSerializer().serializeToString(c)}
|
|
177
|
+
var base=(DATA.slug||'diagram');window.interactiveDiagram={svg:standalone,mermaid:function(){return DATA.mermaid}};
|
|
178
|
+
document.getElementById('xsvg').onclick=function(){download(base+'.svg','image/svg+xml',standalone())};
|
|
179
|
+
document.getElementById('xpng').onclick=function(){var img=new Image();img.onload=function(){var k=2,cv=document.createElement('canvas');cv.width=W*k;cv.height=H*k;var ctx=cv.getContext('2d');ctx.scale(k,k);ctx.drawImage(img,0,0);cv.toBlob(function(b){download(base+'.png','image/png',b)})};
|
|
180
|
+
img.src='data:image/svg+xml;charset=utf-8,'+encodeURIComponent(standalone())};
|
|
181
|
+
document.getElementById('xmmd').onclick=function(){var t=DATA.mermaid;if(navigator.clipboard)navigator.clipboard.writeText(t).catch(function(){});download(base+'.mmd','text/plain',t)};
|
|
182
|
+
var tour=null,cap=document.getElementById('caption');
|
|
183
|
+
var steps=DATA.story&&DATA.story.length?DATA.story:(DATA.auto_steps&&DATA.auto_steps.length)?DATA.auto_steps:(DATA.type==='sequence'?DATA.edges.map(function(m,i){return {text:(i+1)+'. '+nodes.get(m.from).label+' → '+nodes.get(m.to).label+(m.label?': '+m.label:''),edges:[m.id],nodes:[m.from,m.to]}}):[]);
|
|
184
|
+
var tb=document.getElementById('tour');if(!steps.length){tb.style.display='none'}
|
|
185
|
+
function step(d){tour.i=Math.max(0,Math.min(steps.length-1,tour.i+d));var s=steps[tour.i];
|
|
186
|
+
light((s.nodes||[]).concat((s.edges||[]).map(function(i){return edges.get(i)?edges.get(i).from:null}).filter(Boolean)).concat((s.edges||[]).map(function(i){return edges.get(i)?edges.get(i).to:null}).filter(Boolean)),s.edges||[],true);
|
|
187
|
+
cap.innerHTML='<b>Step '+(tour.i+1)+' of '+steps.length+'</b> — '+esc(s.text||'')+' <span class="muted">(← → to move, Esc to stop)</span>';cap.classList.add('on')}
|
|
188
|
+
tb.onclick=function(){if(tour){tour=null;cap.classList.remove('on');clear();tb.textContent='Walk through';return}tour={i:-1};tb.textContent='Stop';step(1)};
|
|
189
|
+
document.getElementById('prev').onclick=function(){if(tour)step(-1)};document.getElementById('next').onclick=function(){if(!tour){tb.onclick()}else step(1)};
|
|
190
|
+
if(!steps.length){document.getElementById('prev').style.display='none';document.getElementById('next').style.display='none'}
|
|
191
|
+
function size(){var wr=document.querySelector('.wrap');var avail=Math.max(window.innerHeight-150,360);var r=stage.getBoundingClientRect();
|
|
192
|
+
var s=Math.max(W/Math.max(r.width,1),0.6);wr.style.height=Math.min(avail,Math.max(H/s+24,300))+'px'}
|
|
193
|
+
size();fit();window.addEventListener('resize',function(){size();fit()});
|
|
194
|
+
function hash(){var h=decodeURIComponent(location.hash.slice(1));if(!h)return;var kv=new Map();h.split('&').forEach(function(p){var i=p.indexOf('=');if(i>0)kv.set(p.slice(0,i),p.slice(i+1))});
|
|
195
|
+
if(kv.get('theme'))root.setAttribute('data-theme',kv.get('theme'));
|
|
196
|
+
if(kv.get('n')&&nodes.get(kv.get('n')))show('n',kv.get('n'));if(kv.get('e')&&edges.get(kv.get('e')))show('e',kv.get('e'));
|
|
197
|
+
if(kv.get('path')){var ab=kv.get('path').split(',');var p=path(ab[0],ab[1],true)||path(ab[0],ab[1],false);if(p)light(p.n,p.e,true)}
|
|
198
|
+
if(kv.get('reach')){var ar=kv.get('reach').split(',');var r=reach(ar[0],ar[1]==='up'?-1:1);light(r.n,r.e,true)}
|
|
199
|
+
if(kv.get('step')&&steps.length){tour={i:-1};tb.textContent='Stop';step(+kv.get('step'))}}
|
|
200
|
+
hash();window.addEventListener('hashchange',hash);
|
|
201
|
+
})();
|
|
202
|
+
</script>
|
|
203
|
+
</body></html>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{"type": "timeline",
|
|
2
|
+
"title": "Card payments failing, 3 October",
|
|
3
|
+
"question": "What happened, and how long did it take anyone to notice?",
|
|
4
|
+
"summary": "Errors climbed seven minutes after the 2.14 rollout, but the page fired 22 minutes later because the alert waits for a fixed 5% to hold. From the page, rollback took 28 minutes.",
|
|
5
|
+
"groups": [{"id": "deploys", "label": "Deploys"}, {"id": "monitoring", "label": "Monitoring"}, {"id": "oncall", "label": "On-call"}],
|
|
6
|
+
"events": [
|
|
7
|
+
{"id": "deploy", "label": "v2.14 rolled out", "at": "2026-10-03T09:12", "lane": "deploys", "kind": "change", "evidence": ["oncall-notes.md:5#v2.14 rolled out"]},
|
|
8
|
+
{"id": "climb", "label": "Card errors pass 5%", "at": "2026-10-03T09:19", "lane": "monitoring", "kind": "alert", "evidence": ["oncall-notes.md:6#pass 5%"]},
|
|
9
|
+
{"id": "page", "label": "Pager fires", "at": "2026-10-03T09:41", "lane": "monitoring", "kind": "alert", "detail": "22 minutes after the climb began: the alert waits for 5% to hold for 20 minutes.", "evidence": ["oncall-notes.md:7#22 minutes after"]},
|
|
10
|
+
{"id": "ack", "label": "Ana acknowledges", "at": "2026-10-03T09:44", "lane": "oncall", "kind": "action", "evidence": ["oncall-notes.md:8#acknowledges"]},
|
|
11
|
+
{"id": "cause", "label": "Rollout named as the cause", "at": "2026-10-03T09:51", "lane": "oncall", "kind": "action", "evidence": ["oncall-notes.md:9#started with the 2.14 rollout"]},
|
|
12
|
+
{"id": "rollback", "label": "Rollback to v2.13", "at": "2026-10-03T09:58", "end": "2026-10-03T10:09", "lane": "deploys", "kind": "change", "evidence": ["oncall-notes.md:10-11#rollback finished"]},
|
|
13
|
+
{"id": "normal", "label": "Errors back under 0.5%", "at": "2026-10-03T10:11", "lane": "monitoring", "kind": "milestone", "evidence": ["oncall-notes.md:12#back under 0.5%"]},
|
|
14
|
+
{"id": "status", "label": "Status page: resolved", "at": "2026-10-03T10:30", "lane": "oncall", "kind": "action", "evidence": ["oncall-notes.md:13#resolved"]},
|
|
15
|
+
{"id": "pm", "label": "Postmortem", "at": "2026-10-06T14:00", "lane": "oncall", "kind": "milestone", "detail": "Action: alert on the rise in the error rate, not on a fixed 5% for 20 minutes.", "evidence": ["oncall-notes.md:14#postmortem held"]}
|
|
16
|
+
],
|
|
17
|
+
"edges": [
|
|
18
|
+
{"from": "deploy", "to": "climb", "label": "caused", "evidence": ["oncall-notes.md:9#errors started with the 2.14 rollout"]},
|
|
19
|
+
{"from": "climb", "to": "page", "label": "detected 22 min later", "evidence": ["oncall-notes.md:7"]}
|
|
20
|
+
],
|
|
21
|
+
"notes": ["Customer impact (how many payments failed) is not in the notes, so it is not on the timeline."]}
|
package/packs/solution-architect/skills/interactive-diagram/examples/inputs/incident/oncall-notes.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Incident 2026-10-03: card payments failing
|
|
2
|
+
|
|
3
|
+
Notes kept in the on-call channel, times in UTC.
|
|
4
|
+
|
|
5
|
+
09:12 deploy: payments v2.14 rolled out to all regions (release bot)
|
|
6
|
+
09:19 dashboard: card authorisation errors pass 5% and keep climbing
|
|
7
|
+
09:41 pager: "card-auth-error-rate" fires, 22 minutes after the climb began
|
|
8
|
+
09:44 Ana acknowledges the page and opens the incident
|
|
9
|
+
09:51 Ana: errors started with the 2.14 rollout, nothing else changed
|
|
10
|
+
09:58 rollback to v2.13 started
|
|
11
|
+
10:09 rollback finished in all regions
|
|
12
|
+
10:11 dashboard: card errors back under 0.5%
|
|
13
|
+
10:30 status page updated: resolved
|
|
14
|
+
6 Oct 14:00 postmortem held; action: alert on the error rate's rise, not a fixed 5% for 20 minutes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
{"span": "POST /checkout", "id": "a1", "parent": null, "service": "api", "start_ms": 0, "end_ms": 1840}
|
|
2
|
+
{"span": "verify session", "id": "a2", "parent": "a1", "service": "api", "start_ms": 4, "end_ms": 38}
|
|
3
|
+
{"span": "price cart", "id": "a3", "parent": "a1", "service": "api", "start_ms": 40, "end_ms": 420}
|
|
4
|
+
{"span": "redis MGET price:*", "id": "a4", "parent": "a3", "service": "redis", "start_ms": 42, "end_ms": 61}
|
|
5
|
+
{"span": "select price from products (cache miss)", "id": "a5", "parent": "a3", "service": "postgres", "start_ms": 64, "end_ms": 410}
|
|
6
|
+
{"span": "stripe.PaymentIntent.create", "id": "a6", "parent": "a1", "service": "stripe", "start_ms": 430, "end_ms": 1620}
|
|
7
|
+
{"span": "TLS handshake api.stripe.com", "id": "a7", "parent": "a6", "service": "stripe", "start_ms": 431, "end_ms": 520}
|
|
8
|
+
{"span": "insert into orders", "id": "a8", "parent": "a1", "service": "postgres", "start_ms": 1625, "end_ms": 1790}
|
|
9
|
+
{"span": "publish fulfilment", "id": "a9", "parent": "a1", "service": "rabbitmq", "start_ms": 1792, "end_ms": 1836}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<!doctype html>
|
|
2
2
|
<html lang="en" data-theme-default="auto"><head><meta charset="utf-8">
|
|
3
|
-
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'sha256-
|
|
3
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'sha256-KdTstAM29gVIP104X7Tju5/PTeQKbRDMMG1TjJdsOyI='; style-src 'unsafe-inline'; img-src data: blob:; base-uri 'none'; form-action 'none'">
|
|
4
4
|
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
5
5
|
<!-- One self-contained file: no external scripts, styles, fonts or requests. The policy above makes the browser refuse any. -->
|
|
6
6
|
<title>How annual leave gets approved</title><meta name="generator" content="interactive-diagram (skilldrop)"></head>
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
<span class="hint">Click a part for detail and evidence · shift-click a second part for the path · drag to pan · scroll to zoom</span></nav>
|
|
18
18
|
<div class="legend"><span><i></i>connection</span><span><i class="d"></i>async / optional</span><span><i class="a"></i>assumption (not shown by the evidence)</span></div>
|
|
19
19
|
<div class="caption" id="caption" aria-live="polite"></div>
|
|
20
|
-
<div class="wrap"><div id="stage"><svg id="map" xmlns="http://www.w3.org/2000/svg" viewBox="0 0
|
|
20
|
+
<div class="wrap"><div id="stage"><svg id="map" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1071 405" width="1071" height="405" role="group" aria-label="How annual leave gets approved"><defs><marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerUnits="userSpaceOnUse" markerWidth="10" markerHeight="10" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" class="arrowhead"/></marker><marker id="arrow-start" viewBox="0 0 10 10" refX="1" refY="5" markerUnits="userSpaceOnUse" markerWidth="10" markerHeight="10" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" class="arrowhead"/></marker><marker id="tri" viewBox="0 0 12 12" refX="11" refY="6" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto-start-reverse"><path d="M1,1 L11,6 L1,11 z" class="mk-open"/></marker><marker id="open" viewBox="0 0 12 12" refX="11" refY="6" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto-start-reverse"><path d="M1,1 L11,6 L1,11" class="mk-line"/></marker><marker id="dia-solid" viewBox="0 0 16 10" refX="15" refY="5" markerUnits="userSpaceOnUse" markerWidth="18" markerHeight="11" orient="auto-start-reverse"><path d="M1,5 L8,1 L15,5 L8,9 z" class="arrowhead"/></marker><marker id="dia-open" viewBox="0 0 16 10" refX="15" refY="5" markerUnits="userSpaceOnUse" markerWidth="18" markerHeight="11" orient="auto-start-reverse"><path d="M1,5 L8,1 L15,5 L8,9 z" class="mk-open"/></marker></defs><rect class="canvas" x="0" y="0" width="1071" height="405"/><g class="edge s-solid" id="e-e1" data-id="e1" data-from="submit" data-to="manager"><title>submit → manager</title><path class="hit" d="M274.5,107.0 L303.5,107.0 Q310.5,107.0 317.5,107.0 L346.5,107.0"/><path class="line" d="M274.5,107.0 L303.5,107.0 Q310.5,107.0 317.5,107.0 L346.5,107.0" marker-end="url(#arrow)"/></g><g class="edge s-solid" id="e-e2" data-id="e2" data-from="manager" data-to="long"><title>approves</title><path class="hit" d="M525.5,93.0 L550.0,93.0 Q557.0,93.0 557.0,86.0 L557.0,79.0 Q557.0,72.0 564.0,72.0 L602.5,72.0"/><path class="line" d="M525.5,93.0 L550.0,93.0 Q557.0,93.0 557.0,86.0 L557.0,79.0 Q557.0,72.0 564.0,72.0 L602.5,72.0" marker-end="url(#arrow)"/><rect class="lbg" x="541.4" y="63.0" width="58.4" height="18.0" rx="3"/><text class="elabel" x="570.6" y="76.0" text-anchor="middle">approves</text></g><g class="edge s-solid assumed" id="e-e3" data-id="e3" data-from="manager" data-to="declined"><title>declines</title><path class="hit" d="M525.5,121.0 L550.0,121.0 Q557.0,121.0 557.0,128.0 L557.0,155.0 Q557.0,162.0 564.0,162.0 L597.5,162.0"/><path class="line" d="M525.5,121.0 L550.0,121.0 Q557.0,121.0 557.0,128.0 L557.0,155.0 Q557.0,162.0 564.0,162.0 L597.5,162.0" marker-end="url(#arrow)"/><rect class="lbg" x="530.5" y="132.5" width="52.9" height="18.0" rx="3"/><text class="elabel" x="557.0" y="145.5" text-anchor="middle">declines</text></g><g class="edge s-solid" id="e-e4" data-id="e4" data-from="long" data-to="hr"><title>yes</title><path class="hit" d="M784.8,60.2 L844.0,60.2 Q851.0,60.2 851.0,67.2 L851.0,100.0 Q851.0,107.0 858.0,107.0 L891.5,107.0"/><path class="line" d="M784.8,60.2 L844.0,60.2 Q851.0,60.2 851.0,67.2 L851.0,100.0 Q851.0,107.0 858.0,107.0 L891.5,107.0" marker-end="url(#arrow)"/><rect class="lbg" x="817.1" y="51.2" width="28.2" height="18.0" rx="3"/><text class="elabel" x="831.1" y="64.2" text-anchor="middle">yes</text></g><g class="edge s-solid" id="e-e5" data-id="e5" data-from="long" data-to="freeze"><title>no</title><path class="hit" d="M784.8,83.8 L853.0,83.8 Q860.0,83.8 860.0,90.8 L860.0,191.5 Q860.0,198.5 853.0,198.5 L37.0,198.5 Q30.0,198.5 30.0,205.5 L30.0,289.2 Q30.0,296.2 37.0,296.2 L91.6,296.2"/><path class="line" d="M784.8,83.8 L853.0,83.8 Q860.0,83.8 860.0,90.8 L860.0,191.5 Q860.0,198.5 853.0,198.5 L37.0,198.5 Q30.0,198.5 30.0,205.5 L30.0,289.2 Q30.0,296.2 37.0,296.2 L91.6,296.2" marker-end="url(#arrow)"/><rect class="lbg" x="849.0" y="132.1" width="22.1" height="18.0" rx="3"/><text class="elabel" x="860.0" y="145.1" text-anchor="middle">no</text></g><g class="edge s-solid" id="e-e6" data-id="e6" data-from="hr" data-to="freeze"><title>hr → freeze</title><path class="hit" d="M1011.5,107.0 L1024.0,107.0 Q1031.0,107.0 1031.0,114.0 L1031.0,200.5 Q1031.0,207.5 1024.0,207.5 L46.0,207.5 Q39.0,207.5 39.0,214.5 L39.0,312.8 Q39.0,319.8 46.0,319.8 L91.6,319.8"/><path class="line" d="M1011.5,107.0 L1024.0,107.0 Q1031.0,107.0 1031.0,114.0 L1031.0,200.5 Q1031.0,207.5 1024.0,207.5 L46.0,207.5 Q39.0,207.5 39.0,214.5 L39.0,312.8 Q39.0,319.8 46.0,319.8 L91.6,319.8" marker-end="url(#arrow)"/></g><g class="edge s-solid" id="e-e7" data-id="e7" data-from="freeze" data-to="director"><title>yes</title><path class="hit" d="M241.4,296.2 L304.6,296.2 Q310.5,296.2 310.5,302.1 L310.5,302.1 Q310.5,308.0 316.4,308.0 L359.5,308.0"/><path class="line" d="M241.4,296.2 L304.6,296.2 Q310.5,296.2 310.5,302.1 L310.5,302.1 Q310.5,308.0 316.4,308.0 L359.5,308.0" marker-end="url(#arrow)"/><rect class="lbg" x="275.7" y="287.2" width="28.1" height="18.0" rx="3"/><text class="elabel" x="289.8" y="300.2" text-anchor="middle">yes</text></g><g class="edge s-solid" id="e-e8" data-id="e8" data-from="freeze" data-to="booked"><title>no</title><path class="hit" d="M241.4,319.8 L303.5,319.8 Q310.5,319.8 310.5,326.8 L310.5,358.0 Q310.5,365.0 317.5,365.0 L550.0,365.0 Q557.0,365.0 557.0,358.0 L557.0,302.7 Q557.0,295.7 564.0,295.7 L608.8,295.7"/><path class="line" d="M241.4,319.8 L303.5,319.8 Q310.5,319.8 310.5,326.8 L310.5,358.0 Q310.5,365.0 317.5,365.0 L550.0,365.0 Q557.0,365.0 557.0,358.0 L557.0,302.7 Q557.0,295.7 564.0,295.7 L608.8,295.7" marker-end="url(#arrow)"/><rect class="lbg" x="422.7" y="356.0" width="22.1" height="18.0" rx="3"/><text class="elabel" x="433.8" y="369.0" text-anchor="middle">no</text></g><g class="edge s-solid" id="e-e9" data-id="e9" data-from="director" data-to="booked"><title>director → booked</title><path class="hit" d="M512.5,308.0 L559.8,308.0 Q566.0,308.0 566.0,314.2 L566.0,314.2 Q566.0,320.3 572.2,320.3 L608.8,320.3"/><path class="line" d="M512.5,308.0 L559.8,308.0 Q566.0,308.0 566.0,314.2 L566.0,314.2 Q566.0,320.3 572.2,320.3 L608.8,320.3" marker-end="url(#arrow)"/></g><g class="node c-green k-start" id="n-submit" data-id="submit" tabindex="0" role="button" aria-label="Submit request in the HR portal"><title>Submit request in the HR portal</title><rect class="shape" x="58.5" y="79.0" width="216.0" height="56.0" rx="28.0"/><text class="label" x="166.5" y="103.0" text-anchor="middle">Submit request in the HR</text><text class="label" x="166.5" y="120.0" text-anchor="middle">portal</text></g><g class="node c-rose k-manual" id="n-manager" data-id="manager" tabindex="0" role="button" aria-label="Line manager reviews"><title>Line manager reviews</title><rect class="shape" x="346.5" y="82.0" width="179.0" height="50.0" rx="6"/><text class="label" x="436.0" y="111.5" text-anchor="middle">Line manager reviews</text></g><g class="node c-amber k-decision" id="n-long" data-id="long" tabindex="0" role="button" aria-label="Over 10 working days?"><title>Over 10 working days?</title><path class="shape" d="M708.5,30.0 L814.5,72.0 L708.5,114.0 L602.5,72.0 Z"/><text class="label" x="708.5" y="68.0" text-anchor="middle">Over 10 working</text><text class="label" x="708.5" y="85.0" text-anchor="middle">days?</text></g><g class="node c-rose k-manual" id="n-hr" data-id="hr" tabindex="0" role="button" aria-label="HR approves"><title>HR approves</title><rect class="shape" x="891.5" y="82.0" width="120.0" height="50.0" rx="6"/><text class="label" x="951.5" y="111.5" text-anchor="middle">HR approves</text></g><g class="node c-amber k-decision" id="n-freeze" data-id="freeze" tabindex="0" role="button" aria-label="In the year-end freeze?"><title>In the year-end freeze?</title><path class="shape" d="M166.5,266.0 L270.5,308.0 L166.5,350.0 L62.5,308.0 Z"/><text class="label" x="166.5" y="304.0" text-anchor="middle">In the year-end</text><text class="label" x="166.5" y="321.0" text-anchor="middle">freeze?</text></g><g class="node c-rose k-manual" id="n-director" data-id="director" tabindex="0" role="button" aria-label="Director approves"><title>Director approves</title><rect class="shape" x="359.5" y="283.0" width="153.0" height="50.0" rx="6"/><text class="label" x="436.0" y="312.5" text-anchor="middle">Director approves</text></g><g class="node c-slate k-end" id="n-booked" data-id="booked" tabindex="0" role="button" aria-label="Booked in team calendar"><title>Booked in team calendar</title><rect class="shape" x="605.0" y="286.0" width="207.0" height="44.0" rx="22.0"/><text class="label" x="708.5" y="312.5" text-anchor="middle">Booked in team calendar</text></g><g class="node c-slate k-end assumed" id="n-declined" data-id="declined" tabindex="0" role="button" aria-label="Employee told it's declined (assumption)"><title>Employee told it's declined</title><rect class="shape" x="597.5" y="140.0" width="222.0" height="44.0" rx="22.0"/><text class="label" x="708.5" y="166.5" text-anchor="middle">Employee told it's declined</text><text class="badge warn" x="814.5" y="136.0" text-anchor="end">assumed</text></g></svg></div><aside id="panel" aria-live="polite"></aside></div>
|
|
21
21
|
<style id="diagram-css">
|
|
22
22
|
:root{--bg:#f7f8fa;--panel:#ffffff;--ink:#16181d;--muted:#5b6170;--line:#d5d9e0;--edge:#6b7385;--accent:#2563eb;
|
|
23
23
|
--warn:#b45309;--ok:#15803d;--bad:#b91c1c;--hl:#f59e0b;--canvas:#fbfcfd;--group:#eef1f6;--group2:#e6eaf2;--shadow:0 1px 2px rgba(0,0,0,.06)}
|
|
@@ -49,6 +49,11 @@ section.text{max-width:1100px;padding:10px 24px 40px}section.text h2{font-size:1
|
|
|
49
49
|
.legend{display:flex;flex-wrap:wrap;gap:14px;font-size:12px;color:var(--muted);padding:6px 24px 0}
|
|
50
50
|
.legend i{display:inline-block;width:22px;height:0;border-top:2px solid var(--edge);vertical-align:middle;margin-right:5px}
|
|
51
51
|
.legend i.d{border-top-style:dashed}.legend i.a{border-top:2px dashed var(--warn)}
|
|
52
|
+
.findings{margin:10px 0 0;padding:8px 14px;border-left:3px solid var(--accent);background:var(--panel);border-radius:4px;max-width:1000px}
|
|
53
|
+
.findings ul{margin:4px 0 0;padding-left:18px}.findings li{margin:2px 0}
|
|
54
|
+
table.answers{border-collapse:collapse;margin:10px 0 0;max-width:1100px;font-size:13px}
|
|
55
|
+
table.answers th,table.answers td{border:1px solid var(--line);padding:5px 9px;text-align:left;vertical-align:top}
|
|
56
|
+
table.answers th{background:var(--group2);font-weight:600}table.answers td.ev{font-size:12px;color:var(--muted)}
|
|
52
57
|
.issues{margin:8px 24px 0;padding:8px 12px;border:1px solid var(--warn);border-radius:6px;color:var(--warn);font-size:13px}
|
|
53
58
|
/* diagram */
|
|
54
59
|
.canvas{fill:var(--canvas)}
|
|
@@ -78,12 +83,23 @@ svg.focus .on,svg.focus .group.on{opacity:1}svg .node.on .shape{stroke-width:2.6
|
|
|
78
83
|
svg .node.sel .shape{stroke:var(--hl)!important;stroke-width:3}svg .node:focus-visible .shape{stroke:var(--accent)!important;stroke-width:3}
|
|
79
84
|
@media (prefers-reduced-motion:no-preference){svg .edge.on.flow .line{stroke-dasharray:8 6;animation:flow 1s linear infinite}}
|
|
80
85
|
@keyframes flow{to{stroke-dashoffset:-28}}
|
|
86
|
+
.lane rect{fill:var(--group);stroke:var(--line)}.lane-line{stroke:var(--line);stroke-width:2}
|
|
87
|
+
.axis line{stroke:var(--muted);stroke-width:1}.axis text{font-size:11px;fill:var(--muted)}.axis .grid{stroke:var(--line);stroke-dasharray:2 4}
|
|
88
|
+
.brk{stroke:var(--muted);stroke-width:1.5;fill:none}.brk-label{font-size:10px;fill:var(--muted)}
|
|
89
|
+
.node.event .shape{stroke-width:2;fill:var(--panel)}.node.event .bar{fill:var(--group2);stroke-width:1.5}
|
|
90
|
+
.node.event .leader{stroke:var(--line);stroke-width:1}.node.event.approx .shape{stroke-dasharray:3 2}.node .label.tl{font-size:12px}.node .note.tl{font-size:10px}
|
|
91
|
+
.node.span .rowbg{fill:transparent}.node.span:hover .rowbg,.node.span.on .rowbg{fill:var(--group)}
|
|
92
|
+
.node.span .bar{stroke:none;opacity:.88}.node.span.crit .bar{stroke:var(--hl);stroke-width:2.5;opacity:1}
|
|
93
|
+
.node.span.err .bar{stroke:var(--bad);stroke-width:2.5}.node .label.wf{font-size:12px;font-weight:500}.node .note.wf{font-size:11px}
|
|
94
|
+
.node .label.italic{font-style:italic}.mk-open{fill:var(--canvas);stroke:var(--edge);stroke-width:1.2}.mk-line{fill:none;stroke:var(--edge);stroke-width:1.4}
|
|
95
|
+
.edge.tree .line{stroke:var(--line);stroke-width:1.5}
|
|
96
|
+
aside table.facts{border-collapse:collapse;font-size:13px;margin:6px 0}aside table.facts th{text-align:left;color:var(--muted);font-weight:400;padding:2px 10px 2px 0}
|
|
81
97
|
@media print{.bar,aside,.caption{display:none!important}.wrap{height:auto}#stage{border:0}#stage svg{position:static;height:auto}}
|
|
82
98
|
</style>
|
|
83
|
-
<script type="application/json" id="diagram-data">{"type": "workflow", "slug": "how-annual-leave-gets-approved", "source_url": null, "story": [], "nodes": [{"id": "submit", "label": "Submit request in the HR portal", "kind": "start", "note": null, "detail": null, "fields": [], "evidence": [{"said": "you request leave in the HR portal", "source": "user"}], "assumed": false, "status": null, "group_label": null}, {"id": "manager", "label": "Line manager reviews", "kind": "manual", "note": null, "detail": null, "fields": [], "evidence": [{"said": "it goes to your line manager first", "source": "user"}], "assumed": false, "status": null, "group_label": null}, {"id": "long", "label": "Over 10 working days?", "kind": "decision", "note": null, "detail": null, "fields": [], "evidence": [{"said": "anything over ten working days also needs HR", "source": "user"}], "assumed": false, "status": null, "group_label": null}, {"id": "hr", "label": "HR approves", "kind": "manual", "note": null, "detail": null, "fields": [], "evidence": [{"said": "also needs HR", "source": "user"}], "assumed": false, "status": null, "group_label": null}, {"id": "freeze", "label": "In the year-end freeze?", "kind": "decision", "note": null, "detail": null, "fields": [], "evidence": [{"said": "nothing in the last two weeks of December without the director", "source": "user"}], "assumed": false, "status": null, "group_label": null}, {"id": "director", "label": "Director approves", "kind": "manual", "note": null, "detail": null, "fields": [], "evidence": [{"said": "without the director", "source": "user"}], "assumed": false, "status": null, "group_label": null}, {"id": "booked", "label": "Booked in team calendar", "kind": "end", "note": null, "detail": null, "fields": [], "evidence": [{"said": "once it's approved it shows up in the team calendar by itself", "source": "user"}], "assumed": false, "status": null, "group_label": null}, {"id": "declined", "label": "Employee told it's declined", "kind": "end", "note": null, "detail": "The description doesn't say what happens when someone says no.", "fields": [], "evidence": [], "assumed": true, "status": null, "group_label": null}], "edges": [{"id": "e1", "from": "submit", "to": "manager", "label": "", "detail": null, "evidence": [{"said": "it goes to your line manager first", "source": "user"}], "assumed": false, "status": null}, {"id": "e2", "from": "manager", "to": "long", "label": "approves", "detail": null, "evidence": [{"said": "it goes to your line manager first", "source": "user"}], "assumed": false, "status": null}, {"id": "e3", "from": "manager", "to": "declined", "label": "declines", "detail": null, "evidence": [], "assumed": true, "status": null}, {"id": "e4", "from": "long", "to": "hr", "label": "yes", "detail": null, "evidence": [{"said": "anything over ten working days also needs HR", "source": "user"}], "assumed": false, "status": null}, {"id": "e5", "from": "long", "to": "freeze", "label": "no", "detail": null, "evidence": [{"said": "anything over ten working days also needs HR", "source": "user"}], "assumed": false, "status": null}, {"id": "e6", "from": "hr", "to": "freeze", "label": "", "detail": null, "evidence": [{"said": "also needs HR", "source": "user"}], "assumed": false, "status": null}, {"id": "e7", "from": "freeze", "to": "director", "label": "yes", "detail": null, "evidence": [{"said": "without the director", "source": "user"}], "assumed": false, "status": null}, {"id": "e8", "from": "freeze", "to": "booked", "label": "no", "detail": null, "evidence": [{"said": "once it's approved it shows up in the team calendar by itself", "source": "user"}], "assumed": false, "status": null}, {"id": "e9", "from": "director", "to": "booked", "label": "", "detail": null, "evidence": [{"said": "once it's approved it shows up in the team calendar by itself", "source": "user"}], "assumed": false, "status": null}], "mermaid": "flowchart LR\n submit([\"Submit request in the HR portal\"])\n manager[\"Line manager reviews\"]\n long{\"Over 10 working days?\"}\n hr[\"HR approves\"]\n freeze{\"In the year-end freeze?\"}\n director[\"Director approves\"]\n booked([\"Booked in team calendar\"])\n declined([\"Employee told it's declined\"])\n submit --\u003e manager\n manager --\u003e|\"approves\"| long\n manager -.-\u003e|\"declines\"| declined\n long --\u003e|\"yes\"| hr\n long --\u003e|\"no\"| freeze\n hr --\u003e freeze\n freeze --\u003e|\"yes\"| director\n freeze --\u003e|\"no\"| booked\n director --\u003e booked\n classDef assumed stroke-dasharray: 5 4\n class declined assumed\n"}</script>
|
|
99
|
+
<script type="application/json" id="diagram-data">{"type": "workflow", "slug": "how-annual-leave-gets-approved", "source_url": null, "story": [], "nodes": [{"id": "submit", "label": "Submit request in the HR portal", "kind": "start", "note": null, "detail": null, "fields": [], "evidence": [{"said": "you request leave in the HR portal", "source": "user"}], "assumed": false, "status": null, "facts": null, "group_label": null}, {"id": "manager", "label": "Line manager reviews", "kind": "manual", "note": null, "detail": null, "fields": [], "evidence": [{"said": "it goes to your line manager first", "source": "user"}], "assumed": false, "status": null, "facts": null, "group_label": null}, {"id": "long", "label": "Over 10 working days?", "kind": "decision", "note": null, "detail": null, "fields": [], "evidence": [{"said": "anything over ten working days also needs HR", "source": "user"}], "assumed": false, "status": null, "facts": null, "group_label": null}, {"id": "hr", "label": "HR approves", "kind": "manual", "note": null, "detail": null, "fields": [], "evidence": [{"said": "also needs HR", "source": "user"}], "assumed": false, "status": null, "facts": null, "group_label": null}, {"id": "freeze", "label": "In the year-end freeze?", "kind": "decision", "note": null, "detail": null, "fields": [], "evidence": [{"said": "nothing in the last two weeks of December without the director", "source": "user"}], "assumed": false, "status": null, "facts": null, "group_label": null}, {"id": "director", "label": "Director approves", "kind": "manual", "note": null, "detail": null, "fields": [], "evidence": [{"said": "without the director", "source": "user"}], "assumed": false, "status": null, "facts": null, "group_label": null}, {"id": "booked", "label": "Booked in team calendar", "kind": "end", "note": null, "detail": null, "fields": [], "evidence": [{"said": "once it's approved it shows up in the team calendar by itself", "source": "user"}], "assumed": false, "status": null, "facts": null, "group_label": null}, {"id": "declined", "label": "Employee told it's declined", "kind": "end", "note": null, "detail": "The description doesn't say what happens when someone says no.", "fields": [], "evidence": [], "assumed": true, "status": null, "facts": null, "group_label": null}], "edges": [{"id": "e1", "from": "submit", "to": "manager", "label": "", "detail": null, "evidence": [{"said": "it goes to your line manager first", "source": "user"}], "assumed": false, "status": null}, {"id": "e2", "from": "manager", "to": "long", "label": "approves", "detail": null, "evidence": [{"said": "it goes to your line manager first", "source": "user"}], "assumed": false, "status": null}, {"id": "e3", "from": "manager", "to": "declined", "label": "declines", "detail": null, "evidence": [], "assumed": true, "status": null}, {"id": "e4", "from": "long", "to": "hr", "label": "yes", "detail": null, "evidence": [{"said": "anything over ten working days also needs HR", "source": "user"}], "assumed": false, "status": null}, {"id": "e5", "from": "long", "to": "freeze", "label": "no", "detail": null, "evidence": [{"said": "anything over ten working days also needs HR", "source": "user"}], "assumed": false, "status": null}, {"id": "e6", "from": "hr", "to": "freeze", "label": "", "detail": null, "evidence": [{"said": "also needs HR", "source": "user"}], "assumed": false, "status": null}, {"id": "e7", "from": "freeze", "to": "director", "label": "yes", "detail": null, "evidence": [{"said": "without the director", "source": "user"}], "assumed": false, "status": null}, {"id": "e8", "from": "freeze", "to": "booked", "label": "no", "detail": null, "evidence": [{"said": "once it's approved it shows up in the team calendar by itself", "source": "user"}], "assumed": false, "status": null}, {"id": "e9", "from": "director", "to": "booked", "label": "", "detail": null, "evidence": [{"said": "once it's approved it shows up in the team calendar by itself", "source": "user"}], "assumed": false, "status": null}], "mermaid": "flowchart LR\n submit([\"Submit request in the HR portal\"])\n manager[\"Line manager reviews\"]\n long{\"Over 10 working days?\"}\n hr[\"HR approves\"]\n freeze{\"In the year-end freeze?\"}\n director[\"Director approves\"]\n booked([\"Booked in team calendar\"])\n declined([\"Employee told it's declined\"])\n submit --\u003e manager\n manager --\u003e|\"approves\"| long\n manager -.-\u003e|\"declines\"| declined\n long --\u003e|\"yes\"| hr\n long --\u003e|\"no\"| freeze\n hr --\u003e freeze\n freeze --\u003e|\"yes\"| director\n freeze --\u003e|\"no\"| booked\n director --\u003e booked\n classDef assumed stroke-dasharray: 5 4\n class declined assumed\n", "words": {"out": "Goes to", "in": "Comes from", "reach_out": "What can follow", "reach_in": "What leads here"}, "auto_steps": []}</script>
|
|
84
100
|
<script>
|
|
85
101
|
(function(){
|
|
86
|
-
var DATA=JSON.parse(document.getElementById('diagram-data').textContent);
|
|
102
|
+
var DATA=JSON.parse(document.getElementById('diagram-data').textContent);var WD=DATA.words;
|
|
87
103
|
var svg=document.getElementById('map'),stage=document.getElementById('stage'),panel=document.getElementById('panel');
|
|
88
104
|
var root=document.documentElement;
|
|
89
105
|
var pref=localStorage.getItem('idg-theme')||root.getAttribute('data-theme-default');
|
|
@@ -127,11 +143,12 @@ function show(kind,id){var x=kind==='n'?nodes.get(id):edges.get(id);if(!x)return
|
|
|
127
143
|
if(kind==='n'){h+='<h3>'+esc(x.label)+'</h3><div class="kind">'+esc(x.kind)+(x.group_label?' · '+esc(x.group_label):'')+(x.assumed?' · <span style="color:var(--warn)">assumption</span>':'')+(x.status?' · '+esc(x.status):'')+'</div>';
|
|
128
144
|
if(x.note)h+='<p>'+esc(x.note)+'</p>';if(x.detail)h+='<p>'+esc(x.detail)+'</p>';
|
|
129
145
|
if(x.fields&&x.fields.length)h+='<ul>'+x.fields.map(function(f){return '<li><code>'+esc(f.name)+'</code> '+esc(f.type||'')+(f.key?' <b>'+esc([].concat(f.key).join('/').toUpperCase())+'</b>':'')+(f.references?' → '+esc(f.references):'')+(f.note?' — '+esc(f.note):'')+'</li>'}).join('')+'</ul>';
|
|
130
|
-
h+='<
|
|
146
|
+
if(x.facts&&x.facts.length)h+='<table class="facts">'+x.facts.map(function(f){return '<tr><th>'+esc(f[0])+'</th><td>'+esc(f[1])+'</td></tr>'}).join('')+'</table>';
|
|
147
|
+
h+='<div class="acts"><button data-act="out">'+esc(WD.reach_out)+'</button><button data-act="in">'+esc(WD.reach_in)+'</button><button data-act="near">Neighbours</button></div>';
|
|
131
148
|
h+='<p class="muted" style="font-size:12px">Shift-click another part to see the path between them.</p>';
|
|
132
149
|
var o=outE.get(id).map(function(i){var e=edges.get(i);return '<li><a class="jump" data-n="'+esc(e.to)+'">'+esc(nodes.get(e.to).label)+'</a>'+(e.label?' — '+esc(e.label):'')+'</li>'}).join('');
|
|
133
150
|
var n=inE.get(id).map(function(i){var e=edges.get(i);return '<li><a class="jump" data-n="'+esc(e.from)+'">'+esc(nodes.get(e.from).label)+'</a>'+(e.label?' — '+esc(e.label):'')+'</li>'}).join('');
|
|
134
|
-
if(o)h+='<b>
|
|
151
|
+
if(o)h+='<b>'+esc(WD.out)+'</b><ul>'+o+'</ul>';if(n)h+='<b>'+esc(WD.in)+'</b><ul>'+n+'</ul>';
|
|
135
152
|
h+='<b>Evidence</b>'+evHtml(x.evidence);
|
|
136
153
|
var nb=new Set([id]);outE.get(id).concat(inE.get(id)).forEach(function(i){nb.add(edges.get(i).from);nb.add(edges.get(i).to)});light(Array.from(nb),outE.get(id).concat(inE.get(id)));el('n',id).classList.add('sel');
|
|
137
154
|
}else{h+='<h3>'+esc(nodes.get(x.from).label)+' → '+esc(nodes.get(x.to).label)+'</h3><div class="kind">'+(x.label?esc(x.label):'connection')+(x.assumed?' · <span style="color:var(--warn)">assumption</span>':'')+(x.status?' · '+esc(x.status):'')+'</div>';
|
|
@@ -140,7 +157,7 @@ function show(kind,id){var x=kind==='n'?nodes.get(id):edges.get(id);if(!x)return
|
|
|
140
157
|
panel.querySelector('.close').onclick=function(){panel.classList.remove('open');clear();selected=null};
|
|
141
158
|
panel.querySelectorAll('.jump').forEach(function(a){a.onclick=function(){show('n',a.getAttribute('data-n'))}});
|
|
142
159
|
panel.querySelectorAll('[data-act]').forEach(function(b){b.onclick=function(){var a=b.getAttribute('data-act');
|
|
143
|
-
if(a==='near'){show('n',id);return}var r=reach(id,a==='
|
|
160
|
+
if(a==='near'){show('n',id);return}var r=reach(id,a==='out'?1:-1);light(r.n,r.e,true);el('n',id).classList.add('sel')}})}
|
|
144
161
|
svg.addEventListener('click',function(e){var g=e.target.closest('.node');if(g){var id=g.getAttribute('data-id');
|
|
145
162
|
if(e.shiftKey&&selected&&selected!==id){var p=path(selected,id,true)||path(selected,id,false);if(p){light(p.n,p.e,true)}return}show('n',id);return}
|
|
146
163
|
var ed=e.target.closest('.edge');if(ed){show('e',ed.getAttribute('data-id'));return}});
|
|
@@ -163,7 +180,7 @@ document.getElementById('xpng').onclick=function(){var img=new Image();img.onloa
|
|
|
163
180
|
img.src='data:image/svg+xml;charset=utf-8,'+encodeURIComponent(standalone())};
|
|
164
181
|
document.getElementById('xmmd').onclick=function(){var t=DATA.mermaid;if(navigator.clipboard)navigator.clipboard.writeText(t).catch(function(){});download(base+'.mmd','text/plain',t)};
|
|
165
182
|
var tour=null,cap=document.getElementById('caption');
|
|
166
|
-
var steps=DATA.story&&DATA.story.length?DATA.story:(DATA.type==='sequence'?DATA.edges.map(function(m,i){return {text:(i+1)+'. '+nodes.get(m.from).label+' → '+nodes.get(m.to).label+(m.label?': '+m.label:''),edges:[m.id],nodes:[m.from,m.to]}}):[]);
|
|
183
|
+
var steps=DATA.story&&DATA.story.length?DATA.story:(DATA.auto_steps&&DATA.auto_steps.length)?DATA.auto_steps:(DATA.type==='sequence'?DATA.edges.map(function(m,i){return {text:(i+1)+'. '+nodes.get(m.from).label+' → '+nodes.get(m.to).label+(m.label?': '+m.label:''),edges:[m.id],nodes:[m.from,m.to]}}):[]);
|
|
167
184
|
var tb=document.getElementById('tour');if(!steps.length){tb.style.display='none'}
|
|
168
185
|
function step(d){tour.i=Math.max(0,Math.min(steps.length-1,tour.i+d));var s=steps[tour.i];
|
|
169
186
|
light((s.nodes||[]).concat((s.edges||[]).map(function(i){return edges.get(i)?edges.get(i).from:null}).filter(Boolean)).concat((s.edges||[]).map(function(i){return edges.get(i)?edges.get(i).to:null}).filter(Boolean)),s.edges||[],true);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "interactive-diagram",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Turn a codebase, a system description, a process or a database schema into one self-contained interactive HTML diagram (architecture, workflow, data flow, lifecycle, ERD or
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Turn a codebase, a system description, a process or a database schema into one self-contained interactive HTML diagram (architecture, workflow, data flow, lifecycle, ERD, sequence, class, tree, timeline or waterfall) where every box and arrow cites the file and line or the words it came from, anything inferred is drawn dashed as an assumption, and each cited line is checked against the repository before the page is written. Laid out automatically, opens offline with no CDN, and exports SVG, PNG and Mermaid. Use when the user asks to \"visualize\", \"map\" or \"make an interactive diagram of\" a repo, a request path, a pipeline, an approval process, a set of tables, an incident's events or a slow trace, or wants a diagram they can click through and share as one file.",
|
|
5
5
|
"entrypoint": "SKILL.md",
|
|
6
6
|
"deps": {
|
|
7
7
|
"npm": [],
|