claude-dev-env 1.61.0 → 1.62.1
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/CLAUDE.md +8 -0
- package/bin/install.mjs +1 -1
- package/hooks/blocking/code_rules_dead_config_field.py +321 -0
- package/hooks/blocking/code_rules_enforcer.py +6 -0
- package/hooks/blocking/config/verified_commit_constants.py +1 -0
- package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +432 -0
- package/hooks/blocking/test_verified_commit_gate.py +32 -0
- package/hooks/blocking/verified_commit_gate.py +11 -1
- package/hooks/hooks_constants/dead_config_field_constants.py +39 -0
- package/package.json +1 -1
- package/skills/autoconverge/reference/gotchas.md +11 -0
- package/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +5 -0
- package/skills/autoconverge/workflow/test_render_report.py +25 -0
- package/skills/doc-gist/SKILL.md +3 -2
- package/skills/doc-gist/references/examples/21-decision-signoff.html +546 -0
- package/skills/doc-gist/references/examples/README.md +2 -2
- package/skills/pr-converge/SKILL.md +1 -0
- package/skills/task-build/SKILL.md +31 -0
|
@@ -0,0 +1,546 @@
|
|
|
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" />
|
|
6
|
+
<title>Tideglass — payments-api v3.2 cutover sign-off</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--ivory: #FAF9F5;
|
|
10
|
+
--slate: #141413;
|
|
11
|
+
--clay: #D97757;
|
|
12
|
+
--clay-d: #B85C3E;
|
|
13
|
+
--oat: #E3DACC;
|
|
14
|
+
--olive: #788C5D;
|
|
15
|
+
--olive-d: #5E7047;
|
|
16
|
+
--olive-bg: #EEF2E7;
|
|
17
|
+
--amber: #9A6700;
|
|
18
|
+
--amber-bg: #FBF3DF;
|
|
19
|
+
--gray-50: #F0EEE6;
|
|
20
|
+
--gray-200: #D1CFC5;
|
|
21
|
+
--gray-400: #ABA99F;
|
|
22
|
+
--gray-500: #87867F;
|
|
23
|
+
--gray-800: #3D3D3A;
|
|
24
|
+
--white: #ffffff;
|
|
25
|
+
--serif: ui-serif, Georgia, "Times New Roman", serif;
|
|
26
|
+
--sans: system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
27
|
+
--mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
|
|
28
|
+
}
|
|
29
|
+
* { box-sizing: border-box; }
|
|
30
|
+
html, body {
|
|
31
|
+
margin: 0;
|
|
32
|
+
background: var(--ivory);
|
|
33
|
+
color: var(--slate);
|
|
34
|
+
font-family: var(--sans);
|
|
35
|
+
font-size: 14px;
|
|
36
|
+
line-height: 1.55;
|
|
37
|
+
}
|
|
38
|
+
.wrap {
|
|
39
|
+
max-width: 940px;
|
|
40
|
+
margin: 0 auto;
|
|
41
|
+
padding: 48px 32px 132px;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/* ---------- header ---------- */
|
|
45
|
+
header { margin-bottom: 22px; }
|
|
46
|
+
.eyebrow {
|
|
47
|
+
font-family: var(--mono);
|
|
48
|
+
font-size: 11px;
|
|
49
|
+
letter-spacing: 0.08em;
|
|
50
|
+
text-transform: uppercase;
|
|
51
|
+
color: var(--gray-500);
|
|
52
|
+
}
|
|
53
|
+
h1 {
|
|
54
|
+
font-family: var(--serif);
|
|
55
|
+
font-weight: 500;
|
|
56
|
+
font-size: 30px;
|
|
57
|
+
letter-spacing: -0.01em;
|
|
58
|
+
margin: 6px 0 6px;
|
|
59
|
+
}
|
|
60
|
+
.sub { color: var(--gray-500); max-width: 660px; }
|
|
61
|
+
.how {
|
|
62
|
+
margin: 16px 0 0;
|
|
63
|
+
background: var(--white);
|
|
64
|
+
border: 1.5px solid var(--gray-200);
|
|
65
|
+
border-left: 4px solid var(--clay);
|
|
66
|
+
border-radius: 10px;
|
|
67
|
+
padding: 13px 17px;
|
|
68
|
+
font-size: 13px;
|
|
69
|
+
max-width: 720px;
|
|
70
|
+
}
|
|
71
|
+
.how b { color: var(--clay-d); }
|
|
72
|
+
|
|
73
|
+
/* ---------- zones ---------- */
|
|
74
|
+
.zone { margin-top: 26px; }
|
|
75
|
+
.zone-head {
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: baseline;
|
|
78
|
+
gap: 11px;
|
|
79
|
+
padding-bottom: 9px;
|
|
80
|
+
margin-bottom: 14px;
|
|
81
|
+
border-bottom: 1.5px solid var(--gray-200);
|
|
82
|
+
}
|
|
83
|
+
.zone-head .tag {
|
|
84
|
+
font-family: var(--mono);
|
|
85
|
+
font-size: 10px;
|
|
86
|
+
letter-spacing: 0.08em;
|
|
87
|
+
text-transform: uppercase;
|
|
88
|
+
font-weight: 600;
|
|
89
|
+
color: var(--ivory);
|
|
90
|
+
background: var(--slate);
|
|
91
|
+
border-radius: 6px;
|
|
92
|
+
padding: 3px 8px;
|
|
93
|
+
}
|
|
94
|
+
.zone-head h2 {
|
|
95
|
+
font-family: var(--serif);
|
|
96
|
+
font-weight: 500;
|
|
97
|
+
font-size: 18px;
|
|
98
|
+
margin: 0;
|
|
99
|
+
}
|
|
100
|
+
.zone-head .zcount {
|
|
101
|
+
margin-left: auto;
|
|
102
|
+
font-family: var(--mono);
|
|
103
|
+
font-size: 11px;
|
|
104
|
+
color: var(--gray-500);
|
|
105
|
+
font-variant-numeric: tabular-nums;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* ---------- card ---------- */
|
|
109
|
+
.card {
|
|
110
|
+
background: var(--white);
|
|
111
|
+
border: 1.5px solid var(--gray-200);
|
|
112
|
+
border-left: 4px solid var(--gray-400);
|
|
113
|
+
border-radius: 11px;
|
|
114
|
+
padding: 14px 16px;
|
|
115
|
+
margin: 0 0 12px;
|
|
116
|
+
transition: border-color 140ms ease, background 140ms ease;
|
|
117
|
+
}
|
|
118
|
+
.card.is-accept { border-left-color: var(--olive); background: var(--olive-bg); }
|
|
119
|
+
.card.is-change { border-left-color: var(--amber); background: var(--amber-bg); }
|
|
120
|
+
.card-top { display: flex; align-items: baseline; gap: 9px; }
|
|
121
|
+
.card-id {
|
|
122
|
+
font-family: var(--mono);
|
|
123
|
+
font-size: 11px;
|
|
124
|
+
color: var(--gray-500);
|
|
125
|
+
font-variant-numeric: tabular-nums;
|
|
126
|
+
}
|
|
127
|
+
.card .q { font-weight: 600; font-size: 14.5px; }
|
|
128
|
+
.rec {
|
|
129
|
+
margin: 9px 0 0;
|
|
130
|
+
font-size: 13px;
|
|
131
|
+
background: var(--olive-bg);
|
|
132
|
+
border: 1.5px solid #CFDDBD;
|
|
133
|
+
border-radius: 8px;
|
|
134
|
+
padding: 8px 11px;
|
|
135
|
+
}
|
|
136
|
+
.rec b { color: var(--olive-d); }
|
|
137
|
+
.card.is-change .rec { background: var(--white); }
|
|
138
|
+
.alt { margin: 7px 0 0; font-size: 12.5px; color: var(--gray-500); }
|
|
139
|
+
code {
|
|
140
|
+
font-family: var(--mono);
|
|
141
|
+
font-size: 12px;
|
|
142
|
+
background: var(--gray-50);
|
|
143
|
+
border: 1px solid var(--gray-200);
|
|
144
|
+
border-radius: 4px;
|
|
145
|
+
padding: 1px 5px;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* ---------- toggle + note ---------- */
|
|
149
|
+
.fb { display: flex; align-items: center; gap: 8px; margin: 11px 0 0; flex-wrap: wrap; }
|
|
150
|
+
.fb-btn {
|
|
151
|
+
font-family: var(--mono);
|
|
152
|
+
font-size: 12px;
|
|
153
|
+
cursor: pointer;
|
|
154
|
+
border: 1.5px solid var(--gray-200);
|
|
155
|
+
background: var(--white);
|
|
156
|
+
color: var(--gray-800);
|
|
157
|
+
border-radius: 999px;
|
|
158
|
+
padding: 5px 13px;
|
|
159
|
+
user-select: none;
|
|
160
|
+
transition: background 120ms ease, border-color 120ms ease, color 120ms ease;
|
|
161
|
+
}
|
|
162
|
+
.fb-btn:hover { border-color: var(--gray-500); }
|
|
163
|
+
.fb-btn.on[data-v="accept"] { background: var(--olive); border-color: var(--olive); color: var(--ivory); }
|
|
164
|
+
.fb-btn.on[data-v="change"] { background: var(--amber); border-color: var(--amber); color: var(--ivory); }
|
|
165
|
+
.fb-note {
|
|
166
|
+
flex: 1;
|
|
167
|
+
min-width: 240px;
|
|
168
|
+
font-family: var(--sans);
|
|
169
|
+
font-size: 13px;
|
|
170
|
+
border: 1.5px solid var(--gray-200);
|
|
171
|
+
border-radius: 8px;
|
|
172
|
+
padding: 6px 10px;
|
|
173
|
+
background: var(--white);
|
|
174
|
+
color: var(--slate);
|
|
175
|
+
}
|
|
176
|
+
.fb-note:focus { outline: 2px solid var(--clay); border-color: var(--clay); }
|
|
177
|
+
.fb-note::placeholder { color: var(--gray-400); }
|
|
178
|
+
|
|
179
|
+
/* ---------- sticky bar ---------- */
|
|
180
|
+
.bar {
|
|
181
|
+
position: fixed;
|
|
182
|
+
left: 0; right: 0; bottom: 0;
|
|
183
|
+
background: var(--slate);
|
|
184
|
+
color: var(--ivory);
|
|
185
|
+
display: flex;
|
|
186
|
+
align-items: center;
|
|
187
|
+
gap: 16px;
|
|
188
|
+
padding: 12px 24px;
|
|
189
|
+
z-index: 50;
|
|
190
|
+
box-shadow: 0 -2px 14px rgba(20, 20, 19, 0.22);
|
|
191
|
+
}
|
|
192
|
+
.bar .prog {
|
|
193
|
+
font-family: var(--mono);
|
|
194
|
+
font-size: 12.5px;
|
|
195
|
+
letter-spacing: 0.02em;
|
|
196
|
+
font-variant-numeric: tabular-nums;
|
|
197
|
+
}
|
|
198
|
+
.bar .prog b { color: var(--clay); }
|
|
199
|
+
.bar .right { margin-left: auto; display: flex; gap: 10px; align-items: center; }
|
|
200
|
+
.bar button {
|
|
201
|
+
font-family: var(--mono);
|
|
202
|
+
font-size: 12px;
|
|
203
|
+
cursor: pointer;
|
|
204
|
+
border-radius: 999px;
|
|
205
|
+
padding: 9px 17px;
|
|
206
|
+
border: 1.5px solid transparent;
|
|
207
|
+
transition: background 120ms ease, transform 80ms ease;
|
|
208
|
+
}
|
|
209
|
+
.bar .exp { background: var(--clay); color: var(--ivory); border-color: var(--clay); font-weight: 600; }
|
|
210
|
+
.bar .exp:hover { background: var(--clay-d); }
|
|
211
|
+
.bar .exp:active { transform: translateY(1px); }
|
|
212
|
+
.bar .rst { background: transparent; color: var(--gray-400); border-color: #44443F; }
|
|
213
|
+
.bar .rst:hover { color: var(--ivory); border-color: var(--gray-500); }
|
|
214
|
+
|
|
215
|
+
/* ---------- modal ---------- */
|
|
216
|
+
.modal {
|
|
217
|
+
position: fixed;
|
|
218
|
+
inset: 0;
|
|
219
|
+
background: rgba(20, 20, 19, 0.5);
|
|
220
|
+
display: none;
|
|
221
|
+
align-items: center;
|
|
222
|
+
justify-content: center;
|
|
223
|
+
z-index: 60;
|
|
224
|
+
padding: 24px;
|
|
225
|
+
}
|
|
226
|
+
.modal.show { display: flex; }
|
|
227
|
+
.modal-card {
|
|
228
|
+
background: var(--ivory);
|
|
229
|
+
border: 1.5px solid var(--gray-200);
|
|
230
|
+
border-radius: 14px;
|
|
231
|
+
max-width: 720px;
|
|
232
|
+
width: 100%;
|
|
233
|
+
padding: 24px;
|
|
234
|
+
}
|
|
235
|
+
.modal-card h3 { font-family: var(--serif); font-weight: 500; font-size: 20px; margin: 0 0 4px; }
|
|
236
|
+
.modal-card p { color: var(--gray-500); font-size: 13px; margin: 0 0 13px; }
|
|
237
|
+
.modal-card textarea {
|
|
238
|
+
width: 100%;
|
|
239
|
+
height: 330px;
|
|
240
|
+
font: 12.5px/1.6 var(--mono);
|
|
241
|
+
color: var(--slate);
|
|
242
|
+
border: 1.5px solid var(--gray-200);
|
|
243
|
+
border-radius: 10px;
|
|
244
|
+
padding: 13px;
|
|
245
|
+
background: var(--white);
|
|
246
|
+
resize: vertical;
|
|
247
|
+
}
|
|
248
|
+
.modal-actions { display: flex; gap: 10px; margin-top: 13px; align-items: center; }
|
|
249
|
+
.modal-actions button {
|
|
250
|
+
font-family: var(--mono);
|
|
251
|
+
font-size: 12px;
|
|
252
|
+
cursor: pointer;
|
|
253
|
+
border-radius: 999px;
|
|
254
|
+
padding: 9px 18px;
|
|
255
|
+
border: 1.5px solid transparent;
|
|
256
|
+
}
|
|
257
|
+
.modal-actions .copy { background: var(--slate); color: var(--ivory); font-weight: 600; }
|
|
258
|
+
.modal-actions .copy.copied { background: var(--olive); }
|
|
259
|
+
.modal-actions .close { background: var(--white); color: var(--gray-800); border-color: var(--gray-200); }
|
|
260
|
+
.modal-actions .close:hover { border-color: var(--gray-500); }
|
|
261
|
+
|
|
262
|
+
footer {
|
|
263
|
+
margin-top: 34px;
|
|
264
|
+
font-family: var(--mono);
|
|
265
|
+
font-size: 11px;
|
|
266
|
+
color: var(--gray-500);
|
|
267
|
+
letter-spacing: 0.02em;
|
|
268
|
+
}
|
|
269
|
+
</style>
|
|
270
|
+
</head>
|
|
271
|
+
<body>
|
|
272
|
+
<div class="wrap">
|
|
273
|
+
|
|
274
|
+
<header>
|
|
275
|
+
<div class="eyebrow">Tideglass / release / sign-off</div>
|
|
276
|
+
<h1>payments-api v3.2 cutover — go / no-go</h1>
|
|
277
|
+
<p class="sub">Eleven calls the launch crew raised before flipping the new payments service live. Each is pre-set to the recommended answer. Flip the ones you disagree with, then export.</p>
|
|
278
|
+
<div class="how">
|
|
279
|
+
<b>How this works:</b> every card starts on <b>Accept</b> (the recommended call, in green). Flip a card to <b>Change</b> and type your override in the note. Hit <b>Export sign-off</b> for a plain-text digest of every decision you can paste into the release ticket. Choices save in this browser, so a reload keeps your edits.
|
|
280
|
+
</div>
|
|
281
|
+
</header>
|
|
282
|
+
|
|
283
|
+
<section class="zone" data-zone="A — Rollout shape">
|
|
284
|
+
<div class="zone-head"><span class="tag">A</span><h2>Rollout shape</h2><span class="zcount"></span></div>
|
|
285
|
+
<div class="card" data-id="a1" data-label="Traffic ramp">
|
|
286
|
+
<div class="card-top"><span class="card-id">a1</span><span class="q">How do we ramp traffic onto v3.2?</span></div>
|
|
287
|
+
<div class="rec"><b>Recommend:</b> Canary at 5% for 30 minutes, then 25 / 50 / 100 with a 15-minute hold at each step. A held step lets a regression surface before it reaches everyone.</div>
|
|
288
|
+
<div class="alt">Alternative: cut 100% over at once during the low-traffic window.</div>
|
|
289
|
+
<div class="fb"></div>
|
|
290
|
+
</div>
|
|
291
|
+
<div class="card" data-id="a2" data-label="Rollback trigger">
|
|
292
|
+
<div class="card-top"><span class="card-id">a2</span><span class="q">What auto-rolls-back the cutover?</span></div>
|
|
293
|
+
<div class="rec"><b>Recommend:</b> Error rate above <code>0.5%</code> or p99 latency above <code>800ms</code> sustained for 3 minutes, evaluated per ramp step. Either trip reverts the canary weight to zero with no human in the loop.</div>
|
|
294
|
+
<div class="fb"></div>
|
|
295
|
+
</div>
|
|
296
|
+
<div class="card" data-id="a3" data-label="Old service retention">
|
|
297
|
+
<div class="card-top"><span class="card-id">a3</span><span class="q">How long does v3.1 stay warm after 100%?</span></div>
|
|
298
|
+
<div class="rec"><b>Recommend:</b> Keep v3.1 deployed and drainable for 72 hours so a revert is a weight change, not a redeploy.</div>
|
|
299
|
+
<div class="fb"></div>
|
|
300
|
+
</div>
|
|
301
|
+
</section>
|
|
302
|
+
|
|
303
|
+
<section class="zone" data-zone="B — Data & migrations">
|
|
304
|
+
<div class="zone-head"><span class="tag">B</span><h2>Data & migrations</h2><span class="zcount"></span></div>
|
|
305
|
+
<div class="card" data-id="b1" data-label="Schema migration timing">
|
|
306
|
+
<div class="card-top"><span class="card-id">b1</span><span class="q">When does the <code>ledger_entries</code> column migration run?</span></div>
|
|
307
|
+
<div class="rec"><b>Recommend:</b> Run the additive migration the day before, with both old and new columns populated by a dual-write. The cutover reads the new column; nothing blocks on a live <code>ALTER TABLE</code>.</div>
|
|
308
|
+
<div class="alt">Alternative: migrate inline during the maintenance window.</div>
|
|
309
|
+
<div class="fb"></div>
|
|
310
|
+
</div>
|
|
311
|
+
<div class="card" data-id="b2" data-label="Backfill verification">
|
|
312
|
+
<div class="card-top"><span class="card-id">b2</span><span class="q">How is the dual-write backfill verified before go?</span></div>
|
|
313
|
+
<div class="rec"><b>Recommend:</b> A reconciliation job compares old-column and new-column checksums per account; go is blocked until the mismatch count is zero across two consecutive runs.</div>
|
|
314
|
+
<div class="fb"></div>
|
|
315
|
+
</div>
|
|
316
|
+
<div class="card" data-id="b3" data-label="Idempotency keys">
|
|
317
|
+
<div class="card-top"><span class="card-id">b3</span><span class="q">Do in-flight payment requests carry their idempotency key across the cutover?</span></div>
|
|
318
|
+
<div class="rec"><b>Recommend:</b> Yes — both versions read the same idempotency store, so a retry that lands on v3.2 after starting on v3.1 is de-duplicated rather than double-charged.</div>
|
|
319
|
+
<div class="fb"></div>
|
|
320
|
+
</div>
|
|
321
|
+
</section>
|
|
322
|
+
|
|
323
|
+
<section class="zone" data-zone="C — Window & comms">
|
|
324
|
+
<div class="zone-head"><span class="tag">C</span><h2>Window & comms</h2><span class="zcount"></span></div>
|
|
325
|
+
<div class="card" data-id="c1" data-label="Cutover window">
|
|
326
|
+
<div class="card-top"><span class="card-id">c1</span><span class="q">When do we run the cutover?</span></div>
|
|
327
|
+
<div class="rec"><b>Recommend:</b> Tuesday 02:00–04:00 UTC — the weekly traffic trough, far from the Friday settlement run and with the on-call team rested.</div>
|
|
328
|
+
<div class="fb"></div>
|
|
329
|
+
</div>
|
|
330
|
+
<div class="card" data-id="c2" data-label="Customer notice">
|
|
331
|
+
<div class="card-top"><span class="card-id">c2</span><span class="q">Do customers get a maintenance notice?</span></div>
|
|
332
|
+
<div class="rec"><b>Recommend:</b> No banner — the ramp is zero-downtime, so a notice would imply an outage that is not expected. Status page stays green unless a rollback fires.</div>
|
|
333
|
+
<div class="alt">Alternative: post a low-key "scheduled maintenance" window anyway.</div>
|
|
334
|
+
<div class="fb"></div>
|
|
335
|
+
</div>
|
|
336
|
+
<div class="card" data-id="c3" data-label="Stakeholder channel">
|
|
337
|
+
<div class="card-top"><span class="card-id">c3</span><span class="q">Where does the launch crew report progress?</span></div>
|
|
338
|
+
<div class="rec"><b>Recommend:</b> One dedicated incident channel with a step-by-step checklist pinned; the release lead posts at each ramp gate so a watching exec sees progress without asking.</div>
|
|
339
|
+
<div class="fb"></div>
|
|
340
|
+
</div>
|
|
341
|
+
</section>
|
|
342
|
+
|
|
343
|
+
<section class="zone" data-zone="D — Ownership">
|
|
344
|
+
<div class="zone-head"><span class="tag">D</span><h2>Ownership</h2><span class="zcount"></span></div>
|
|
345
|
+
<div class="card" data-id="d1" data-label="Go decision owner">
|
|
346
|
+
<div class="card-top"><span class="card-id">d1</span><span class="q">Who holds the final go / no-go call at each gate?</span></div>
|
|
347
|
+
<div class="rec"><b>Recommend:</b> The release lead, with payments-eng and finance each holding a hard veto. Any one veto stops the ramp.</div>
|
|
348
|
+
<div class="fb"></div>
|
|
349
|
+
</div>
|
|
350
|
+
<div class="card is-change" data-id="d2" data-label="On-call escalation path">
|
|
351
|
+
<div class="card-top"><span class="card-id">d2</span><span class="q">Who is paged if a rollback fires mid-ramp, and what is the secondary path?</span></div>
|
|
352
|
+
<div class="rec"><b>Needs your input</b> — no safe default. Name the primary on-call and the secondary escalation for the cutover window. Pre-set to Change.</div>
|
|
353
|
+
<div class="fb"></div>
|
|
354
|
+
</div>
|
|
355
|
+
</section>
|
|
356
|
+
|
|
357
|
+
</div>
|
|
358
|
+
|
|
359
|
+
<div class="bar">
|
|
360
|
+
<span class="prog"><b id="prog-n">0</b> changed / <span id="prog-t">0</span> decisions</span>
|
|
361
|
+
<div class="right">
|
|
362
|
+
<button class="rst" id="btn-reset">Reset</button>
|
|
363
|
+
<button class="exp" id="btn-export">Export sign-off ▸</button>
|
|
364
|
+
</div>
|
|
365
|
+
</div>
|
|
366
|
+
|
|
367
|
+
<div class="modal" id="modal">
|
|
368
|
+
<div class="modal-card">
|
|
369
|
+
<h3>Sign-off digest — copy into the release ticket</h3>
|
|
370
|
+
<p>Every decision, in order, with your overrides. One line each.</p>
|
|
371
|
+
<textarea id="export-out" readonly></textarea>
|
|
372
|
+
<div class="modal-actions">
|
|
373
|
+
<button class="copy" id="btn-copy">Copy to clipboard</button>
|
|
374
|
+
<button class="close" id="btn-close">Close</button>
|
|
375
|
+
</div>
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
|
|
379
|
+
<footer>Domain-neutral demo. The Accept / Change toggle, free-text override, and exported plain-text digest are the reusable pattern; the payments cutover is invented sample content.</footer>
|
|
380
|
+
|
|
381
|
+
<script>
|
|
382
|
+
(function () {
|
|
383
|
+
"use strict";
|
|
384
|
+
|
|
385
|
+
var STORAGE_KEY = "tideglass_signoff_v1";
|
|
386
|
+
var PREMARK_CHANGE = ["d2"];
|
|
387
|
+
var DIGEST_TITLE = "PAYMENTS-API v3.2 CUTOVER — SIGN-OFF";
|
|
388
|
+
|
|
389
|
+
var state = { items: {} };
|
|
390
|
+
try {
|
|
391
|
+
var saved = localStorage.getItem(STORAGE_KEY);
|
|
392
|
+
if (saved) state = JSON.parse(saved);
|
|
393
|
+
} catch (e) { /* corrupt or unavailable storage: start clean */ }
|
|
394
|
+
if (!state.items) state.items = {};
|
|
395
|
+
|
|
396
|
+
function persist() {
|
|
397
|
+
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); }
|
|
398
|
+
catch (e) { /* storage unavailable: in-memory only */ }
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
var cards = Array.prototype.slice.call(document.querySelectorAll(".card"));
|
|
402
|
+
|
|
403
|
+
function applyCardClass(card, choice) {
|
|
404
|
+
card.classList.remove("is-accept", "is-change");
|
|
405
|
+
if (choice) card.classList.add("is-" + choice);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
cards.forEach(function (card) {
|
|
409
|
+
var id = card.dataset.id;
|
|
410
|
+
var fb = card.querySelector(".fb");
|
|
411
|
+
if (!fb) return;
|
|
412
|
+
|
|
413
|
+
fb.innerHTML =
|
|
414
|
+
'<span class="fb-btn" data-v="accept">Accept</span>' +
|
|
415
|
+
'<span class="fb-btn" data-v="change">Change</span>' +
|
|
416
|
+
'<input class="fb-note" placeholder="your override note (optional)">';
|
|
417
|
+
var note = fb.querySelector(".fb-note");
|
|
418
|
+
|
|
419
|
+
var entry = state.items[id];
|
|
420
|
+
if (!entry) {
|
|
421
|
+
entry = { v: PREMARK_CHANGE.indexOf(id) !== -1 ? "change" : "accept", note: "" };
|
|
422
|
+
state.items[id] = entry;
|
|
423
|
+
}
|
|
424
|
+
applyCardClass(card, entry.v);
|
|
425
|
+
fb.querySelectorAll(".fb-btn").forEach(function (b) {
|
|
426
|
+
if (b.dataset.v === entry.v) b.classList.add("on");
|
|
427
|
+
});
|
|
428
|
+
if (entry.note) note.value = entry.note;
|
|
429
|
+
|
|
430
|
+
fb.querySelectorAll(".fb-btn").forEach(function (btn) {
|
|
431
|
+
btn.addEventListener("click", function () {
|
|
432
|
+
var choice = btn.dataset.v;
|
|
433
|
+
var cur = state.items[id] || {};
|
|
434
|
+
cur.v = choice;
|
|
435
|
+
state.items[id] = cur;
|
|
436
|
+
fb.querySelectorAll(".fb-btn").forEach(function (b) {
|
|
437
|
+
b.classList.toggle("on", b.dataset.v === choice);
|
|
438
|
+
});
|
|
439
|
+
applyCardClass(card, choice);
|
|
440
|
+
persist();
|
|
441
|
+
updateProgress();
|
|
442
|
+
});
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
note.addEventListener("input", function () {
|
|
446
|
+
var cur = state.items[id] || {};
|
|
447
|
+
cur.note = note.value;
|
|
448
|
+
state.items[id] = cur;
|
|
449
|
+
persist();
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
persist();
|
|
453
|
+
|
|
454
|
+
function updateProgress() {
|
|
455
|
+
var total = cards.length;
|
|
456
|
+
var changed = 0;
|
|
457
|
+
Object.keys(state.items).forEach(function (k) {
|
|
458
|
+
if (state.items[k] && state.items[k].v === "change") changed++;
|
|
459
|
+
});
|
|
460
|
+
document.getElementById("prog-n").textContent = changed;
|
|
461
|
+
document.getElementById("prog-t").textContent = total;
|
|
462
|
+
document.querySelectorAll(".zone").forEach(function (zone) {
|
|
463
|
+
var zc = zone.querySelector(".zcount");
|
|
464
|
+
var n = zone.querySelectorAll(".card").length;
|
|
465
|
+
var c = 0;
|
|
466
|
+
zone.querySelectorAll(".card").forEach(function (card) {
|
|
467
|
+
var it = state.items[card.dataset.id];
|
|
468
|
+
if (it && it.v === "change") c++;
|
|
469
|
+
});
|
|
470
|
+
zc.textContent = c ? c + " of " + n + " changed" : n + " decisions";
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
updateProgress();
|
|
474
|
+
|
|
475
|
+
function buildDigest() {
|
|
476
|
+
var lines = ["### " + DIGEST_TITLE + " ###"];
|
|
477
|
+
document.querySelectorAll(".zone").forEach(function (zone) {
|
|
478
|
+
var zoneLabel = zone.getAttribute("data-zone");
|
|
479
|
+
var zoneLines = [];
|
|
480
|
+
zone.querySelectorAll(".card").forEach(function (card) {
|
|
481
|
+
var id = card.dataset.id;
|
|
482
|
+
var label = card.dataset.label || id;
|
|
483
|
+
var it = state.items[id] || {};
|
|
484
|
+
var verdict = it.v === "change" ? "CHANGE" : "ACCEPT";
|
|
485
|
+
var note = (it.note && it.note.trim()) ? ' — "' + it.note.trim() + '"' : "";
|
|
486
|
+
zoneLines.push("- [" + id + "] " + label + ": " + verdict + note);
|
|
487
|
+
});
|
|
488
|
+
if (zoneLines.length) {
|
|
489
|
+
lines.push("");
|
|
490
|
+
lines.push("## " + zoneLabel);
|
|
491
|
+
lines.push.apply(lines, zoneLines);
|
|
492
|
+
}
|
|
493
|
+
});
|
|
494
|
+
lines.push("");
|
|
495
|
+
lines.push("### END ###");
|
|
496
|
+
return lines.join("\n");
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
var modal = document.getElementById("modal");
|
|
500
|
+
var output = document.getElementById("export-out");
|
|
501
|
+
|
|
502
|
+
document.getElementById("btn-export").addEventListener("click", function () {
|
|
503
|
+
output.value = buildDigest();
|
|
504
|
+
modal.classList.add("show");
|
|
505
|
+
});
|
|
506
|
+
document.getElementById("btn-close").addEventListener("click", function () {
|
|
507
|
+
modal.classList.remove("show");
|
|
508
|
+
});
|
|
509
|
+
modal.addEventListener("click", function (e) {
|
|
510
|
+
if (e.target === modal) modal.classList.remove("show");
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
var copyBtn = document.getElementById("btn-copy");
|
|
514
|
+
var copyTimer = null;
|
|
515
|
+
function flashCopied() {
|
|
516
|
+
copyBtn.textContent = "Copied ✓";
|
|
517
|
+
copyBtn.classList.add("copied");
|
|
518
|
+
if (copyTimer) clearTimeout(copyTimer);
|
|
519
|
+
copyTimer = setTimeout(function () {
|
|
520
|
+
copyBtn.textContent = "Copy to clipboard";
|
|
521
|
+
copyBtn.classList.remove("copied");
|
|
522
|
+
}, 1400);
|
|
523
|
+
}
|
|
524
|
+
copyBtn.addEventListener("click", function () {
|
|
525
|
+
output.select();
|
|
526
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
527
|
+
navigator.clipboard.writeText(output.value).then(flashCopied, function () {
|
|
528
|
+
try { document.execCommand("copy"); } catch (e) { /* ignore */ }
|
|
529
|
+
flashCopied();
|
|
530
|
+
});
|
|
531
|
+
} else {
|
|
532
|
+
try { document.execCommand("copy"); } catch (e) { /* ignore */ }
|
|
533
|
+
flashCopied();
|
|
534
|
+
}
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
document.getElementById("btn-reset").addEventListener("click", function () {
|
|
538
|
+
if (confirm("Reset every decision to the recommended answer?")) {
|
|
539
|
+
try { localStorage.removeItem(STORAGE_KEY); } catch (e) { /* ignore */ }
|
|
540
|
+
location.reload();
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
})();
|
|
544
|
+
</script>
|
|
545
|
+
</body>
|
|
546
|
+
</html>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
Files `01`–`20` in this directory are verbatim copies of Thariq Shihipar's [html-effectiveness](https://thariqs.github.io/html-effectiveness/) prototypes, used with permission as reference material for the doc-gist skill's artifact design gallery. File `21-decision-signoff.html` is an original addition in the same house style.
|
|
2
2
|
|
|
3
|
-
Each file demonstrates a distinct HTML artifact shape — annotated PR diff, design system swatches, incident timeline, slide deck, prompt tuner — and serves as learning material for fresh per-request HTML design. The gallery teaches what shapes work; the agent adapts per request rather than copying.
|
|
3
|
+
Each file demonstrates a distinct HTML artifact shape — annotated PR diff, design system swatches, incident timeline, slide deck, prompt tuner, decision sign-off — and serves as learning material for fresh per-request HTML design. The gallery teaches what shapes work; the agent adapts per request rather than copying.
|
|
4
4
|
|
|
5
5
|
See [SKILL.md](../../SKILL.md) for the full gallery-to-artifact-type mapping.
|
|
@@ -125,6 +125,7 @@ post a fresh PR in a fresh branch based on origin main to the user.
|
|
|
125
125
|
[Step 1.5](reference/per-tick.md). Skipping this reviews and edits the
|
|
126
126
|
wrong repo. The route is routine and automatic — never a material fork
|
|
127
127
|
to pause on.
|
|
128
|
+
- **A named/teammate `code-verifier` never mints a verdict** — In a background-job session, an `Agent`-tool `code-verifier` spawned with a `name` (or otherwise as a persistent teammate) goes idle/"available" awaiting messages rather than terminating, so its `SubagentStop` never fires. `verifier_verdict_minter.py` mints the verdict only on `SubagentStop`, so no verdict file is written and `verified_commit_gate` blocks the `git commit`/`git push` with "no passing verification verdict" even though the verifier emitted `all_pass`. A `shutdown_request` is ignored and `TaskStop` cannot resolve the teammate's id. Spawn the code-verifier as a one-shot agent with NO `name` (a plain async `Agent` call) so it runs to completion and fires `SubagentStop`, minting the verdict bound to the live surface. Keep the work tree frozen between verification and the commit so the minted surface hash still matches.
|
|
128
129
|
|
|
129
130
|
## Progress checklist
|
|
130
131
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: task-build
|
|
3
|
+
description: "Gather every open task in the current session and register each one on the task list with TaskCreate. Use at the start of work, after a planning discussion, or whenever the user lists several things to do. Triggers: '/task-build', 'build my task list', 'capture these tasks', 'add open tasks to the task list', 'track these'."
|
|
4
|
+
argument-hint: "[optional: a list of tasks to capture, or omit to scan the session]"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Task Build
|
|
8
|
+
|
|
9
|
+
Collect every open task and put it on the session task list. Each open task becomes one `TaskCreate` entry, so all the work is tracked and visible.
|
|
10
|
+
|
|
11
|
+
## Instructions
|
|
12
|
+
|
|
13
|
+
1. **Read the current list first.** Call `TaskList` to see what is already tracked. This keeps you from adding the same task twice.
|
|
14
|
+
|
|
15
|
+
2. **Find every open task.** Gather all outstanding work from these sources:
|
|
16
|
+
- `$ARGUMENTS`, when the user passed a list.
|
|
17
|
+
- The current conversation: anything the user asked for that is not yet done.
|
|
18
|
+
- Plan documents, checklists, or `TodoWrite` items raised in this session.
|
|
19
|
+
|
|
20
|
+
An open task is any concrete, actionable item that is not finished. Skip anything already complete and anything already on the list.
|
|
21
|
+
|
|
22
|
+
3. **Create one task per item.** For each open task that is not already tracked, call `TaskCreate` with:
|
|
23
|
+
- `subject` — a short imperative title (e.g. "Fix login redirect").
|
|
24
|
+
- `description` — what the task involves and how to tell it is done.
|
|
25
|
+
- `activeForm` — the present-continuous form for the spinner (e.g. "Fixing login redirect"), when it adds clarity.
|
|
26
|
+
|
|
27
|
+
4. **Report the result.** State how many tasks you added and how many were already tracked, then list the new subjects.
|
|
28
|
+
|
|
29
|
+
## Scope
|
|
30
|
+
|
|
31
|
+
This skill only records tasks. It does not start, assign, or complete them. Use `TaskUpdate` to set an owner or change status once the list is built.
|