@henryqw/pi-herdr-tools 1.0.0

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.
@@ -0,0 +1,170 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Pi Herdr clone flow</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&family=Noto+Sans+KR:wght@400;500;600&family=Noto+Serif+KR:wght@400&display=swap" rel="stylesheet">
8
+ <style>
9
+ *, *::before, *::after { box-sizing: border-box; }
10
+ html, body { margin: 0; min-height: 100%; }
11
+ body {
12
+ display: grid;
13
+ place-items: center;
14
+ padding: 32px;
15
+ background: #f0eee9;
16
+ color: #101828;
17
+ font-family: 'Geist', system-ui, sans-serif;
18
+ }
19
+ main { width: min(1280px, 100%); }
20
+ header { margin-bottom: 24px; }
21
+ header p {
22
+ margin: 0 0 8px;
23
+ color: #4c5665;
24
+ font: 500 8px 'Geist Mono', ui-monospace, monospace;
25
+ letter-spacing: .16em;
26
+ text-transform: uppercase;
27
+ }
28
+ h1 { margin: 0; font: 400 28px/1.2 'Instrument Serif', serif; }
29
+ svg { display: block; width: 100%; height: auto; }
30
+ </style>
31
+ </head>
32
+ <body>
33
+ <main>
34
+ <header>
35
+ <p>Swimlane · active-path clone</p>
36
+ <h1>How Pi Herdr clone creates and recovers a target</h1>
37
+ </header>
38
+
39
+ <svg width="1280" height="720" viewBox="0 0 1280 720" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="clone-flow-title clone-flow-desc">
40
+ <title id="clone-flow-title">Pi Herdr clone flow</title>
41
+ <desc id="clone-flow-desc">Swimlane diagram showing source validation, an active-path snapshot, tab and worktree targets, clone-session launch and focus order, and what remains after failures.</desc>
42
+ <defs>
43
+ <style>
44
+ .lane-rule { stroke: rgba(16,24,40,.141); stroke-width: 1; }
45
+ .lane-rule-strong { stroke: #d5d8db; stroke-width: 1; }
46
+ .eyebrow { fill: #4c5665; font: 500 8px 'Geist Mono', monospace; letter-spacing: .16em; }
47
+ .meta { fill: #6a7282; font: 400 8px 'Geist Mono', monospace; letter-spacing: .08em; }
48
+ .node { fill: #fff; stroke: #101828; stroke-width: 1; }
49
+ .node-focal { fill: rgba(29,78,216,.08); stroke: #1d4ed8; stroke-width: 1.2; }
50
+ .decision { fill: #fff; stroke: #101828; stroke-width: 1; }
51
+ .recovery { fill: rgba(16,24,40,.05); stroke: #4c5665; stroke-width: 1; }
52
+ .name { fill: #101828; font: 600 12px 'Geist', sans-serif; text-anchor: middle; }
53
+ .name-left { fill: #101828; font: 600 12px 'Geist', sans-serif; }
54
+ .sub { fill: #4c5665; font: 400 9px 'Geist Mono', monospace; text-anchor: middle; letter-spacing: .02em; }
55
+ .sub-left { fill: #4c5665; font: 500 9px 'Geist Mono', monospace; letter-spacing: .08em; }
56
+ .edge { fill: none; stroke: #4c5665; stroke-width: 1.2; marker-end: url(#clone-flow-arrow); }
57
+ .edge-focal { fill: none; stroke: #1d4ed8; stroke-width: 1.4; marker-end: url(#clone-flow-arrow-accent); }
58
+ .edge-failure { fill: none; stroke: #6a7282; stroke-width: 1.2; stroke-dasharray: 5 4; marker-end: url(#clone-flow-arrow); }
59
+ .legend { fill: #4c5665; font: 400 8px 'Geist', sans-serif; }
60
+ </style>
61
+ <marker id="clone-flow-arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
62
+ <polygon points="0 0,8 3,0 6" fill="#4c5665"/>
63
+ </marker>
64
+ <marker id="clone-flow-arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
65
+ <polygon points="0 0,8 3,0 6" fill="#1d4ed8"/>
66
+ </marker>
67
+ <marker id="clone-flow-arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
68
+ <polygon points="0 0,8 3,0 6" fill="#1d4ed8"/>
69
+ </marker>
70
+ </defs>
71
+
72
+ <rect width="1280" height="720" fill="#f0eee9"/>
73
+
74
+ <text class="eyebrow" x="40" y="56">ACTIVE-PATH CLONE</text>
75
+ <text class="meta" x="1240" y="56" text-anchor="end">STATIC · TWO TARGETS · ONE RECOVERY BOUNDARY</text>
76
+
77
+ <!-- Swimlanes -->
78
+ <line class="lane-rule-strong" x1="40" y1="80" x2="1240" y2="80"/>
79
+ <line class="lane-rule" x1="40" y1="192" x2="1240" y2="192"/>
80
+ <line class="lane-rule" x1="40" y1="320" x2="1240" y2="320"/>
81
+ <line class="lane-rule" x1="40" y1="520" x2="1240" y2="520"/>
82
+ <line class="lane-rule-strong" x1="40" y1="620" x2="1240" y2="620"/>
83
+ <line class="lane-rule-strong" x1="176" y1="80" x2="176" y2="620"/>
84
+
85
+ <text class="eyebrow" x="56" y="140">CURRENT PI</text>
86
+ <text class="eyebrow" x="56" y="260">CLONE COMMAND</text>
87
+ <text class="eyebrow" x="56" y="420">HERDR TARGET</text>
88
+ <text class="eyebrow" x="56" y="572">RECOVERY</text>
89
+
90
+ <!-- Main flow connectors -->
91
+ <path class="edge" d="M344 140 H352 Q360 140 360 148 V248 Q360 256 368 256"/>
92
+ <path class="edge" d="M528 256 H548"/>
93
+ <path class="edge" d="M676 256 H692 Q700 256 700 264 V360 Q700 368 708 368 H716"/>
94
+ <path class="edge" d="M612 304 V448 Q612 456 620 456 H716"/>
95
+ <path class="edge" d="M828 336 V312 Q828 304 836 304 H960 Q968 304 968 296 V288"/>
96
+ <path class="edge" d="M916 460 H1024 Q1032 460 1032 452 V288"/>
97
+ <path class="edge-focal" d="M1096 256 H1112 Q1120 256 1120 264 V360 Q1120 368 1128 368"/>
98
+ <path class="edge" d="M1184 400 V424"/>
99
+
100
+ <!-- Failure outcomes -->
101
+ <path class="edge-failure" d="M916 368 H928 Q936 368 936 376 V500 Q936 508 928 508 H584 Q576 508 576 516 V548"/>
102
+ <path class="edge-failure" d="M916 476 H992 Q1000 476 1000 484 V512 Q1000 520 992 520 H632 Q624 520 624 528 V548"/>
103
+ <path class="edge-failure" d="M1128 384 H1112 Q1104 384 1104 392 V524 Q1104 532 1096 532 H920 Q912 532 912 540 V548"/>
104
+ <path class="edge-failure" d="M1184 488 V548"/>
105
+
106
+ <!-- Process nodes -->
107
+ <rect class="node" x="184" y="108" width="160" height="64" rx="6"/>
108
+ <text class="name" x="264" y="132">Validate source</text>
109
+ <text class="sub" x="264" y="148">HERDR PANE · SESSION FILE</text>
110
+ <text class="sub" x="264" y="160">ACTIVE LEAF</text>
111
+
112
+ <rect class="node" x="368" y="224" width="160" height="64" rx="6"/>
113
+ <text class="name" x="448" y="248">Snapshot active path</text>
114
+ <text class="sub" x="448" y="264">ROOT → CURRENT LEAF</text>
115
+ <text class="sub" x="448" y="276">NO SIBLINGS · NO STREAMING</text>
116
+
117
+ <polygon class="decision" points="612,208 676,256 612,304 548,256"/>
118
+ <text class="name" x="612" y="252">Choose target</text>
119
+ <text class="sub" x="612" y="268">TAB OR WORKTREE</text>
120
+
121
+ <rect class="node" x="716" y="336" width="200" height="64" rx="6"/>
122
+ <text class="name" x="816" y="360">Create clone tab</text>
123
+ <text class="sub" x="816" y="376">CURRENT WORKSPACE · CURRENT CWD</text>
124
+ <text class="sub" x="816" y="388">NO FOCUS</text>
125
+
126
+ <rect class="node" x="716" y="424" width="200" height="72" rx="6"/>
127
+ <text class="name" x="816" y="448">Create worktree workspace</text>
128
+ <text class="sub" x="816" y="464">FRESH CHECKOUT · NO FOCUS</text>
129
+ <text class="sub" x="816" y="476">ROOT PANE OR EXTRA TAB</text>
130
+ <text class="sub" x="816" y="488">IF ROOT IS OCCUPIED</text>
131
+
132
+ <rect class="node" x="936" y="224" width="160" height="64" rx="6"/>
133
+ <text class="name" x="1016" y="248">Persist clone session</text>
134
+ <text class="sub" x="1016" y="264">ACTIVE PATH ONLY</text>
135
+ <text class="sub" x="1016" y="276">WORKTREE STAMPS CHECKOUT CWD</text>
136
+
137
+ <rect class="node-focal" x="1128" y="336" width="112" height="64" rx="6"/>
138
+ <text class="name" x="1184" y="364">Start Pi</text>
139
+ <text class="sub" x="1184" y="380">--SESSION CLONE</text>
140
+
141
+ <rect class="node" x="1128" y="424" width="112" height="64" rx="6"/>
142
+ <text class="name" x="1184" y="452">Focus target</text>
143
+ <text class="sub" x="1184" y="468">AFTER START</text>
144
+
145
+ <!-- One recovery boundary with three observable outcomes -->
146
+ <rect class="recovery" x="184" y="548" width="1056" height="68" rx="8"/>
147
+ <line class="lane-rule" x1="664" y1="548" x2="664" y2="616"/>
148
+ <line class="lane-rule" x1="1024" y1="548" x2="1024" y2="616"/>
149
+ <text class="sub-left" x="208" y="568">DEFINITIVE CREATION FAILURE</text>
150
+ <text class="name-left" x="208" y="588">No clone session remains</text>
151
+ <text class="sub-left" x="208" y="604">KILLED/INCOMPLETE: partial state may remain; inspect herdr workspace list</text>
152
+ <text class="sub-left" x="688" y="568">START ATTEMPTED</text>
153
+ <text class="name-left" x="688" y="588">Retain target + session file</text>
154
+ <text class="sub-left" x="1048" y="568">FOCUS FAILS</text>
155
+ <text class="name-left" x="1048" y="588">Warn; clone keeps running</text>
156
+
157
+ <!-- Legend -->
158
+ <line class="lane-rule" x1="40" y1="640" x2="1240" y2="640"/>
159
+ <text class="eyebrow" x="40" y="668">KEY</text>
160
+ <line x1="104" y1="664" x2="140" y2="664" stroke="#4c5665" stroke-width="1.2" marker-end="url(#clone-flow-arrow)"/>
161
+ <text class="legend" x="152" y="668">Main flow</text>
162
+ <line x1="268" y1="664" x2="304" y2="664" stroke="#6a7282" stroke-width="1.2" stroke-dasharray="5 4" marker-end="url(#clone-flow-arrow)"/>
163
+ <text class="legend" x="316" y="668">Failure outcome</text>
164
+ <rect x="464" y="656" width="24" height="16" rx="4" fill="rgba(29,78,216,.08)" stroke="#1d4ed8" stroke-width="1.2"/>
165
+ <text class="legend" x="500" y="668">Launch boundary</text>
166
+ <text class="meta" x="1240" y="668" text-anchor="end">ORIGINAL SESSION STAYS ACTIVE</text>
167
+ </svg>
168
+ </main>
169
+ </body>
170
+ </html>
@@ -0,0 +1,131 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg width="1280" height="720" viewBox="0 0 1280 720" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="clone-flow-title clone-flow-desc">
3
+ <title id="clone-flow-title">Pi Herdr clone flow</title>
4
+ <desc id="clone-flow-desc">Swimlane diagram showing source validation, an active-path snapshot, tab and worktree targets, clone-session launch and focus order, and what remains after failures.</desc>
5
+ <defs>
6
+ <style>@import url('https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&amp;family=Geist:wght@400;500;600&amp;family=Geist+Mono:wght@400;500;600&amp;family=Noto+Sans+KR:wght@400;500;600&amp;family=Noto+Serif+KR:wght@400&amp;display=swap');</style>
7
+ <style>
8
+ .lane-rule { stroke: rgba(16,24,40,.141); stroke-width: 1; }
9
+ .lane-rule-strong { stroke: #d5d8db; stroke-width: 1; }
10
+ .eyebrow { fill: #4c5665; font: 500 8px 'Geist Mono', monospace; letter-spacing: .16em; }
11
+ .meta { fill: #6a7282; font: 400 8px 'Geist Mono', monospace; letter-spacing: .08em; }
12
+ .node { fill: #fff; stroke: #101828; stroke-width: 1; }
13
+ .node-focal { fill: rgba(29,78,216,.08); stroke: #1d4ed8; stroke-width: 1.2; }
14
+ .decision { fill: #fff; stroke: #101828; stroke-width: 1; }
15
+ .recovery { fill: rgba(16,24,40,.05); stroke: #4c5665; stroke-width: 1; }
16
+ .name { fill: #101828; font: 600 12px 'Geist', sans-serif; text-anchor: middle; }
17
+ .name-left { fill: #101828; font: 600 12px 'Geist', sans-serif; }
18
+ .sub { fill: #4c5665; font: 400 9px 'Geist Mono', monospace; text-anchor: middle; letter-spacing: .02em; }
19
+ .sub-left { fill: #4c5665; font: 500 9px 'Geist Mono', monospace; letter-spacing: .08em; }
20
+ .edge { fill: none; stroke: #4c5665; stroke-width: 1.2; marker-end: url(#clone-flow-arrow); }
21
+ .edge-focal { fill: none; stroke: #1d4ed8; stroke-width: 1.4; marker-end: url(#clone-flow-arrow-accent); }
22
+ .edge-failure { fill: none; stroke: #6a7282; stroke-width: 1.2; stroke-dasharray: 5 4; marker-end: url(#clone-flow-arrow); }
23
+ .legend { fill: #4c5665; font: 400 8px 'Geist', sans-serif; }
24
+ </style>
25
+ <marker id="clone-flow-arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
26
+ <polygon points="0 0,8 3,0 6" fill="#4c5665"/>
27
+ </marker>
28
+ <marker id="clone-flow-arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
29
+ <polygon points="0 0,8 3,0 6" fill="#1d4ed8"/>
30
+ </marker>
31
+ <marker id="clone-flow-arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
32
+ <polygon points="0 0,8 3,0 6" fill="#1d4ed8"/>
33
+ </marker>
34
+ </defs>
35
+
36
+ <rect width="1280" height="720" fill="#f0eee9"/>
37
+
38
+ <text class="eyebrow" x="40" y="56">ACTIVE-PATH CLONE</text>
39
+ <text class="meta" x="1240" y="56" text-anchor="end">STATIC · TWO TARGETS · ONE RECOVERY BOUNDARY</text>
40
+
41
+ <!-- Swimlanes -->
42
+ <line class="lane-rule-strong" x1="40" y1="80" x2="1240" y2="80"/>
43
+ <line class="lane-rule" x1="40" y1="192" x2="1240" y2="192"/>
44
+ <line class="lane-rule" x1="40" y1="320" x2="1240" y2="320"/>
45
+ <line class="lane-rule" x1="40" y1="520" x2="1240" y2="520"/>
46
+ <line class="lane-rule-strong" x1="40" y1="620" x2="1240" y2="620"/>
47
+ <line class="lane-rule-strong" x1="176" y1="80" x2="176" y2="620"/>
48
+
49
+ <text class="eyebrow" x="56" y="140">CURRENT PI</text>
50
+ <text class="eyebrow" x="56" y="260">CLONE COMMAND</text>
51
+ <text class="eyebrow" x="56" y="420">HERDR TARGET</text>
52
+ <text class="eyebrow" x="56" y="572">RECOVERY</text>
53
+
54
+ <!-- Main flow connectors -->
55
+ <path class="edge" d="M344 140 H352 Q360 140 360 148 V248 Q360 256 368 256"/>
56
+ <path class="edge" d="M528 256 H548"/>
57
+ <path class="edge" d="M676 256 H692 Q700 256 700 264 V360 Q700 368 708 368 H716"/>
58
+ <path class="edge" d="M612 304 V448 Q612 456 620 456 H716"/>
59
+ <path class="edge" d="M828 336 V312 Q828 304 836 304 H960 Q968 304 968 296 V288"/>
60
+ <path class="edge" d="M916 460 H1024 Q1032 460 1032 452 V288"/>
61
+ <path class="edge-focal" d="M1096 256 H1112 Q1120 256 1120 264 V360 Q1120 368 1128 368"/>
62
+ <path class="edge" d="M1184 400 V424"/>
63
+
64
+ <!-- Failure outcomes -->
65
+ <path class="edge-failure" d="M916 368 H928 Q936 368 936 376 V500 Q936 508 928 508 H584 Q576 508 576 516 V548"/>
66
+ <path class="edge-failure" d="M916 476 H992 Q1000 476 1000 484 V512 Q1000 520 992 520 H632 Q624 520 624 528 V548"/>
67
+ <path class="edge-failure" d="M1128 384 H1112 Q1104 384 1104 392 V524 Q1104 532 1096 532 H920 Q912 532 912 540 V548"/>
68
+ <path class="edge-failure" d="M1184 488 V548"/>
69
+
70
+ <!-- Process nodes -->
71
+ <rect class="node" x="184" y="108" width="160" height="64" rx="6"/>
72
+ <text class="name" x="264" y="132">Validate source</text>
73
+ <text class="sub" x="264" y="148">HERDR PANE · SESSION FILE</text>
74
+ <text class="sub" x="264" y="160">ACTIVE LEAF</text>
75
+
76
+ <rect class="node" x="368" y="224" width="160" height="64" rx="6"/>
77
+ <text class="name" x="448" y="248">Snapshot active path</text>
78
+ <text class="sub" x="448" y="264">ROOT → CURRENT LEAF</text>
79
+ <text class="sub" x="448" y="276">NO SIBLINGS · NO STREAMING</text>
80
+
81
+ <polygon class="decision" points="612,208 676,256 612,304 548,256"/>
82
+ <text class="name" x="612" y="252">Choose target</text>
83
+ <text class="sub" x="612" y="268">TAB OR WORKTREE</text>
84
+
85
+ <rect class="node" x="716" y="336" width="200" height="64" rx="6"/>
86
+ <text class="name" x="816" y="360">Create clone tab</text>
87
+ <text class="sub" x="816" y="376">CURRENT WORKSPACE · CURRENT CWD</text>
88
+ <text class="sub" x="816" y="388">NO FOCUS</text>
89
+
90
+ <rect class="node" x="716" y="424" width="200" height="72" rx="6"/>
91
+ <text class="name" x="816" y="448">Create worktree workspace</text>
92
+ <text class="sub" x="816" y="464">FRESH CHECKOUT · NO FOCUS</text>
93
+ <text class="sub" x="816" y="476">ROOT PANE OR EXTRA TAB</text>
94
+ <text class="sub" x="816" y="488">IF ROOT IS OCCUPIED</text>
95
+
96
+ <rect class="node" x="936" y="224" width="160" height="64" rx="6"/>
97
+ <text class="name" x="1016" y="248">Persist clone session</text>
98
+ <text class="sub" x="1016" y="264">ACTIVE PATH ONLY</text>
99
+ <text class="sub" x="1016" y="276">WORKTREE STAMPS CHECKOUT CWD</text>
100
+
101
+ <rect class="node-focal" x="1128" y="336" width="112" height="64" rx="6"/>
102
+ <text class="name" x="1184" y="364">Start Pi</text>
103
+ <text class="sub" x="1184" y="380">--SESSION CLONE</text>
104
+
105
+ <rect class="node" x="1128" y="424" width="112" height="64" rx="6"/>
106
+ <text class="name" x="1184" y="452">Focus target</text>
107
+ <text class="sub" x="1184" y="468">AFTER START</text>
108
+
109
+ <!-- One recovery boundary with three observable outcomes -->
110
+ <rect class="recovery" x="184" y="548" width="1056" height="68" rx="8"/>
111
+ <line class="lane-rule" x1="664" y1="548" x2="664" y2="616"/>
112
+ <line class="lane-rule" x1="1024" y1="548" x2="1024" y2="616"/>
113
+ <text class="sub-left" x="208" y="568">DEFINITIVE CREATION FAILURE</text>
114
+ <text class="name-left" x="208" y="588">No clone session remains</text>
115
+ <text class="sub-left" x="208" y="604">KILLED/INCOMPLETE: partial state may remain; inspect herdr workspace list</text>
116
+ <text class="sub-left" x="688" y="568">START ATTEMPTED</text>
117
+ <text class="name-left" x="688" y="588">Retain target + session file</text>
118
+ <text class="sub-left" x="1048" y="568">FOCUS FAILS</text>
119
+ <text class="name-left" x="1048" y="588">Warn; clone keeps running</text>
120
+
121
+ <!-- Legend -->
122
+ <line class="lane-rule" x1="40" y1="640" x2="1240" y2="640"/>
123
+ <text class="eyebrow" x="40" y="668">KEY</text>
124
+ <line x1="104" y1="664" x2="140" y2="664" stroke="#4c5665" stroke-width="1.2" marker-end="url(#clone-flow-arrow)"/>
125
+ <text class="legend" x="152" y="668">Main flow</text>
126
+ <line x1="268" y1="664" x2="304" y2="664" stroke="#6a7282" stroke-width="1.2" stroke-dasharray="5 4" marker-end="url(#clone-flow-arrow)"/>
127
+ <text class="legend" x="316" y="668">Failure outcome</text>
128
+ <rect x="464" y="656" width="24" height="16" rx="4" fill="rgba(29,78,216,.08)" stroke="#1d4ed8" stroke-width="1.2"/>
129
+ <text class="legend" x="500" y="668">Launch boundary</text>
130
+ <text class="meta" x="1240" y="668" text-anchor="end">ORIGINAL SESSION STAYS ACTIVE</text>
131
+ </svg>
@@ -0,0 +1,212 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Safe worktree completion · Sequence</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Geist:wght@400;500;600&family=Geist+Mono:wght@400;500;600&family=Noto+Sans+KR:wght@400;500;600&family=Noto+Serif+KR:wght@400&display=swap" rel="stylesheet">
8
+ <style>
9
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
10
+ :root {
11
+ --paper: #f0eee9;
12
+ --ink: #101828;
13
+ --muted: #4c5665;
14
+ --accent: #1d4ed8;
15
+ --sans: 'Geist', system-ui, sans-serif;
16
+ --serif: 'Instrument Serif', serif;
17
+ --mono: 'Geist Mono', ui-monospace, monospace;
18
+ }
19
+ body {
20
+ min-height: 100vh;
21
+ padding: 32px;
22
+ background: var(--paper);
23
+ color: var(--ink);
24
+ font-family: var(--sans);
25
+ }
26
+ main { width: max-content; margin: 0 auto; }
27
+ .eyebrow {
28
+ margin-bottom: 8px;
29
+ color: var(--muted);
30
+ font: 500 8px var(--mono);
31
+ letter-spacing: .16em;
32
+ text-transform: uppercase;
33
+ }
34
+ h1 {
35
+ margin-bottom: 24px;
36
+ font: 400 28px/1.2 var(--serif);
37
+ }
38
+ svg { display: block; width: 1280px; height: 720px; }
39
+ </style>
40
+ </head>
41
+ <body>
42
+ <main>
43
+ <p class="eyebrow">Sequence · /done safety and cleanup</p>
44
+ <h1>Safe worktree completion</h1>
45
+
46
+ <svg width="1280" height="720" viewBox="0 0 1280 720" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="done-flow-title done-flow-desc">
47
+ <title id="done-flow-title">Safe worktree completion</title>
48
+ <desc id="done-flow-desc">Sequence showing /done waiting for Pi, requiring confirmation or explicit force, refusing unsafe normal removal, then closing workspace tabs around a conditional parent fast-forward whose failure does not stop final tab closure.</desc>
49
+ <defs>
50
+ <style>
51
+ .lifeline { stroke: rgba(16,24,40,.20); stroke-width: 1; stroke-dasharray: 4 4; }
52
+ .activation { fill: rgba(16,24,40,.06); stroke: #4c5665; stroke-width: .8; }
53
+ .fragment { fill: rgba(16,24,40,.02); stroke: rgba(16,24,40,.24); stroke-width: 1; }
54
+ .fragment-tab { fill: #f0eee9; stroke: rgba(16,24,40,.24); stroke-width: 1; }
55
+ .fragment-label { fill: #4c5665; font: 500 8px 'Geist Mono', monospace; text-anchor: middle; letter-spacing: .12em; }
56
+ .guard { fill: #4c5665; font: 400 8px 'Geist Mono', monospace; letter-spacing: .04em; }
57
+ .gate-badge { fill: rgba(29,78,216,.08); stroke: #1d4ed8; stroke-width: 1; }
58
+ .gate-label { fill: #1d4ed8; font: 500 8px 'Geist Mono', monospace; text-anchor: middle; letter-spacing: .08em; }
59
+ .message { fill: none; stroke: #4c5665; stroke-width: 1.2; marker-end: url(#done-flow-arrow); }
60
+ .message-return { fill: none; stroke: #4c5665; stroke-width: 1.2; stroke-dasharray: 4 4; marker-end: url(#done-flow-arrow); }
61
+ .message-accent { fill: none; stroke: #1d4ed8; stroke-width: 1.4; marker-end: url(#done-flow-arrow-accent); }
62
+ .label-mask { fill: #f0eee9; }
63
+ .message-label { fill: #4c5665; font: 400 8px 'Geist Mono', monospace; text-anchor: middle; letter-spacing: .04em; }
64
+ .message-label-accent { fill: #1d4ed8; font: 500 8px 'Geist Mono', monospace; text-anchor: middle; letter-spacing: .04em; }
65
+ .actor { fill: #fff; stroke: #101828; stroke-width: 1; }
66
+ .actor-input { fill: rgba(76,86,101,.10); stroke: #6a7282; stroke-width: 1; }
67
+ .actor-state { fill: rgba(16,24,40,.05); stroke: #4c5665; stroke-width: 1; }
68
+ .actor-name { fill: #101828; font: 600 12px 'Geist', sans-serif; text-anchor: middle; }
69
+ .actor-sub { fill: #4c5665; font: 400 9px 'Geist Mono', monospace; text-anchor: middle; letter-spacing: .04em; }
70
+ .boundary { stroke: rgba(16,24,40,.24); stroke-width: 1; stroke-dasharray: 4 4; }
71
+ .boundary-label { fill: #4c5665; font: 500 8px 'Geist Mono', monospace; text-anchor: middle; letter-spacing: .08em; }
72
+ .legend-label { fill: #4c5665; font: 400 8px 'Geist', sans-serif; }
73
+ .legend-title { fill: #6a7282; font: 500 8px 'Geist Mono', monospace; letter-spacing: .16em; }
74
+ </style>
75
+ <marker id="done-flow-arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
76
+ <polygon points="0 0,8 3,0 6" fill="#4c5665"/>
77
+ </marker>
78
+ <marker id="done-flow-arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
79
+ <polygon points="0 0,8 3,0 6" fill="#1d4ed8"/>
80
+ </marker>
81
+ <marker id="done-flow-arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto">
82
+ <polygon points="0 0,8 3,0 6" fill="#1d4ed8"/>
83
+ </marker>
84
+ </defs>
85
+
86
+ <rect width="1280" height="720" fill="#f0eee9"/>
87
+
88
+ <!-- Combined fragment frame and region divider -->
89
+ <rect class="fragment" x="120" y="200" width="544" height="232" rx="4"/>
90
+ <line x1="128" y1="352" x2="656" y2="352" stroke="rgba(16,24,40,.20)" stroke-width="1" stroke-dasharray="4 4"/>
91
+
92
+ <!-- Lifelines -->
93
+ <line class="lifeline" x1="136" y1="96" x2="136" y2="624"/>
94
+ <line class="lifeline" x1="404" y1="96" x2="404" y2="624"/>
95
+ <line class="lifeline" x1="652" y1="96" x2="652" y2="624"/>
96
+ <line class="lifeline" x1="1124" y1="96" x2="1124" y2="624"/>
97
+
98
+ <!-- Activation bars -->
99
+ <rect class="activation" x="400" y="124" width="8" height="476"/>
100
+ <rect class="activation" x="648" y="304" width="8" height="32"/>
101
+ <rect class="activation" x="648" y="456" width="8" height="16"/>
102
+ <rect class="activation" x="1120" y="544" width="8" height="24"/>
103
+
104
+ <!-- Fragment labels sit above lifelines for legibility -->
105
+ <rect class="fragment-tab" x="120" y="200" width="40" height="16" rx="4"/>
106
+ <text class="fragment-label" x="140" y="212">ALT</text>
107
+ <rect class="gate-badge" x="168" y="200" width="104" height="16" rx="4"/>
108
+ <text class="gate-label" x="220" y="212">SAFETY GATE</text>
109
+ <rect class="label-mask" x="128" y="220" width="528" height="16"/>
110
+ <text class="guard" x="132" y="232">[normal /done]</text>
111
+ <text class="guard" x="260" y="232">CONFIRM; DIRTY OR OTHER-WORKSPACE TAB USING CHECKOUT → REFUSE</text>
112
+ <rect class="label-mask" x="128" y="360" width="528" height="16"/>
113
+ <text class="guard" x="132" y="372">[explicit --force]</text>
114
+ <text class="guard" x="260" y="372">PERMITS FORCED REMOVAL · IRREVERSIBLE</text>
115
+
116
+ <!-- Messages: time flows downward -->
117
+ <line class="message" x1="136" y1="124" x2="400" y2="124"/>
118
+ <rect class="label-mask" x="200" y="104" width="136" height="12" rx="4"/>
119
+ <text class="message-label" x="268" y="112">RUN /DONE [--FORCE]</text>
120
+
121
+ <path class="message" d="M408 148 H452 Q460 148 460 156 V172 Q460 180 452 180 H412"/>
122
+ <rect class="label-mask" x="468" y="156" width="112" height="12" rx="4"/>
123
+ <text class="message-label" x="524" y="164">WAIT FOR PI IDLE</text>
124
+
125
+ <line class="message" x1="400" y1="256" x2="136" y2="256"/>
126
+ <rect class="label-mask" x="208" y="236" width="120" height="12" rx="4"/>
127
+ <text class="message-label" x="268" y="244">CONFIRM REMOVAL?</text>
128
+
129
+ <line class="message-return" x1="136" y1="280" x2="400" y2="280"/>
130
+ <rect class="label-mask" x="228" y="260" width="80" height="12" rx="4"/>
131
+ <text class="message-label" x="268" y="268">CONFIRMED</text>
132
+
133
+ <line class="message" x1="408" y1="304" x2="648" y2="304"/>
134
+ <rect class="label-mask" x="480" y="284" width="96" height="12" rx="4"/>
135
+ <text class="message-label" x="528" y="292">CHECK SAFETY</text>
136
+
137
+ <line class="message-return" x1="648" y1="328" x2="408" y2="328"/>
138
+ <rect class="label-mask" x="484" y="308" width="88" height="12" rx="4"/>
139
+ <text class="message-label" x="528" y="316">CLEAR / REFUSE</text>
140
+
141
+ <path class="message" d="M408 396 H452 Q460 396 460 404 V412 Q460 420 452 420 H412"/>
142
+ <rect class="label-mask" x="468" y="400" width="104" height="12" rx="4"/>
143
+ <text class="message-label" x="520" y="408">ACCEPT --FORCE</text>
144
+
145
+ <line class="message-accent" x1="408" y1="464" x2="648" y2="464"/>
146
+ <rect class="label-mask" x="448" y="436" width="160" height="20" rx="4"/>
147
+ <text class="message-label-accent" x="528" y="444">CLEAR OR EXPLICIT FORCE</text>
148
+ <text class="message-label-accent" x="528" y="452">REMOVE WORKTREE</text>
149
+
150
+ <!-- Successful removal is the partial-success boundary -->
151
+ <line class="boundary" x1="40" y1="488" x2="392" y2="488"/>
152
+ <line class="boundary" x1="888" y1="488" x2="1240" y2="488"/>
153
+ <text class="boundary-label" x="640" y="492">PARTIAL-SUCCESS BOUNDARY · WORKTREE REMOVED</text>
154
+
155
+ <line class="message" x1="400" y1="516" x2="136" y2="516"/>
156
+ <rect class="label-mask" x="180" y="496" width="176" height="12" rx="4"/>
157
+ <text class="message-label" x="268" y="504">CLOSE OTHER WORKSPACE TABS</text>
158
+
159
+ <line class="message" x1="408" y1="544" x2="1120" y2="544"/>
160
+ <rect class="label-mask" x="684" y="516" width="160" height="20" rx="4"/>
161
+ <text class="message-label" x="764" y="524">IF LINKED + NON-BARE PRIMARY</text>
162
+ <text class="message-label" x="764" y="532">LOCK + FAST-FORWARD ONLY</text>
163
+
164
+ <line class="message-return" x1="1120" y1="568" x2="408" y2="568"/>
165
+ <rect class="label-mask" x="696" y="548" width="136" height="12" rx="4"/>
166
+ <text class="message-label" x="764" y="556">UPDATED OR FAILED</text>
167
+
168
+ <line class="message" x1="400" y1="600" x2="136" y2="600"/>
169
+ <rect class="label-mask" x="184" y="580" width="168" height="12" rx="4"/>
170
+ <text class="message-label" x="268" y="588">ALWAYS CLOSE CURRENT TAB</text>
171
+
172
+ <!-- Actors are drawn after messages -->
173
+ <rect x="56" y="40" width="160" height="56" rx="6" fill="#f0eee9"/>
174
+ <rect class="actor-input" x="56" y="40" width="160" height="56" rx="6"/>
175
+ <text class="actor-name" x="136" y="68">Herdr workspace</text>
176
+ <text class="actor-sub" x="136" y="84">CURRENT + OTHER TABS</text>
177
+
178
+ <rect x="324" y="40" width="160" height="56" rx="6" fill="#f0eee9"/>
179
+ <rect class="actor" x="324" y="40" width="160" height="56" rx="6"/>
180
+ <text class="actor-name" x="404" y="68">Pi /done</text>
181
+ <text class="actor-sub" x="404" y="84">IDLE BEFORE CLEANUP</text>
182
+
183
+ <rect x="572" y="40" width="160" height="56" rx="6" fill="#f0eee9"/>
184
+ <rect class="actor-state" x="572" y="40" width="160" height="56" rx="6"/>
185
+ <text class="actor-name" x="652" y="68">Removal gate</text>
186
+ <text class="actor-sub" x="652" y="84">WORKTREE + TAB USE</text>
187
+
188
+ <rect x="1044" y="40" width="160" height="56" rx="6" fill="#f0eee9"/>
189
+ <rect class="actor" x="1044" y="40" width="160" height="56" rx="6"/>
190
+ <text class="actor-name" x="1124" y="68">Parent checkout</text>
191
+ <text class="actor-sub" x="1124" y="84">CONDITIONAL UPDATE</text>
192
+
193
+ <!-- Legend strip -->
194
+ <line x1="40" y1="640" x2="1240" y2="640" stroke="rgba(16,24,40,.141)" stroke-width=".8"/>
195
+ <text class="legend-title" x="40" y="668">LEGEND</text>
196
+
197
+ <line class="message" x1="128" y1="664" x2="164" y2="664"/>
198
+ <text class="legend-label" x="176" y="668">Action</text>
199
+
200
+ <line class="message-return" x1="264" y1="664" x2="300" y2="664"/>
201
+ <text class="legend-label" x="312" y="668">Return or result</text>
202
+
203
+ <rect class="fragment" x="448" y="656" width="32" height="16" rx="4"/>
204
+ <text class="fragment-label" x="464" y="668">ALT</text>
205
+ <text class="legend-label" x="492" y="668">Safety branch</text>
206
+
207
+ <line class="message-accent" x1="624" y1="664" x2="660" y2="664"/>
208
+ <text class="legend-label" x="672" y="668">Successful removal</text>
209
+ </svg>
210
+ </main>
211
+ </body>
212
+ </html>