claude-dev-env 1.83.0 → 1.84.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.
Files changed (165) hide show
  1. package/_shared/pr-loop/audit-contract.md +24 -12
  2. package/_shared/pr-loop/scripts/_claude_permissions_common.py +16 -5
  3. package/_shared/pr-loop/scripts/code_rules_gate.py +2 -3
  4. package/_shared/pr-loop/scripts/reviews_disabled.py +2 -0
  5. package/_shared/pr-loop/scripts/tests/test__claude_permissions_common.py +212 -0
  6. package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +18 -0
  7. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +18 -0
  8. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +37 -0
  9. package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +14 -0
  10. package/agents/clean-coder.md +3 -0
  11. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +1 -1
  12. package/bin/install.mjs +16 -8
  13. package/bin/install.test.mjs +17 -6
  14. package/commands/CLAUDE.md +0 -1
  15. package/docs/CODE_RULES.md +1 -1
  16. package/hooks/blocking/CLAUDE.md +0 -4
  17. package/hooks/blocking/code_rules_constants_config.py +5 -0
  18. package/hooks/blocking/code_rules_docstrings.py +116 -6
  19. package/hooks/blocking/code_rules_enforcer.py +4 -0
  20. package/hooks/blocking/code_rules_magic_values.py +5 -0
  21. package/hooks/blocking/code_rules_naming_collection.py +11 -6
  22. package/hooks/blocking/code_rules_shared.py +34 -0
  23. package/hooks/blocking/duplicate_rmtree_helper_blocker.py +4 -4
  24. package/hooks/blocking/test_code_rules_enforcer_docstring_prose_wall_illustration.py +197 -0
  25. package/hooks/blocking/test_code_rules_enforcer_docstring_runon_sentence.py +27 -0
  26. package/hooks/blocking/test_code_rules_enforcer_split_constants_config.py +33 -0
  27. package/hooks/blocking/test_code_rules_enforcer_split_shared.py +23 -0
  28. package/hooks/blocking/test_code_rules_magic_values.py +54 -0
  29. package/hooks/blocking/test_duplicate_rmtree_helper_blocker.py +0 -6
  30. package/hooks/blocking/test_pr_converge_bugteam_enforcer_state_tolerance.py +184 -0
  31. package/hooks/blocking/test_shared_stdin_adoption.py +5 -30
  32. package/hooks/hooks_constants/CLAUDE.md +0 -3
  33. package/hooks/hooks_constants/blocking_check_limits.py +3 -0
  34. package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -1
  35. package/hooks/hooks_constants/duplicate_rmtree_helper_blocker_constants.py +0 -1
  36. package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -5
  37. package/hooks/validation/post_tool_use_dispatcher.py +1 -1
  38. package/hooks/validation/test_post_tool_use_dispatcher.py +51 -35
  39. package/hooks/workflow/CLAUDE.md +2 -8
  40. package/package.json +1 -1
  41. package/rules/CLAUDE.md +1 -1
  42. package/rules/plain-illustrative-docstrings.md +35 -1
  43. package/skills/CLAUDE.md +6 -1
  44. package/skills/autoconverge/SKILL.md +38 -57
  45. package/skills/autoconverge/reference/closing-report.md +6 -6
  46. package/skills/autoconverge/reference/convergence.md +17 -15
  47. package/skills/autoconverge/reference/gotchas.md +6 -3
  48. package/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +2 -9
  49. package/skills/autoconverge/workflow/converge.contract.test.mjs +87 -13
  50. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +1 -1
  51. package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +16 -9
  52. package/skills/autoconverge/workflow/converge.mjs +70 -73
  53. package/skills/autoconverge/workflow/render_report.py +7 -11
  54. package/skills/bugteam/CLAUDE.md +1 -1
  55. package/skills/bugteam/PROMPTS.md +7 -6
  56. package/skills/bugteam/SKILL.md +25 -72
  57. package/skills/bugteam/reference/CLAUDE.md +1 -3
  58. package/skills/bugteam/reference/README.md +1 -1
  59. package/skills/bugteam/reference/audit-and-teammates.md +1 -1
  60. package/skills/bugteam/reference/obstacles/fix-publish-summary.md +1 -1
  61. package/skills/bugteam/reference/team-setup.md +8 -7
  62. package/skills/bugteam/scripts/CLAUDE.md +0 -6
  63. package/skills/bugteam/scripts/README.md +0 -4
  64. package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +0 -1
  65. package/skills/code/SKILL.md +2 -0
  66. package/skills/copilot-review/CLAUDE.md +1 -1
  67. package/skills/copilot-review/SKILL.md +25 -23
  68. package/skills/findbugs/CLAUDE.md +2 -2
  69. package/skills/findbugs/SKILL.md +22 -83
  70. package/skills/fixbugs/SKILL.md +2 -4
  71. package/skills/log-audit/CLAUDE.md +20 -0
  72. package/skills/log-audit/SKILL.md +68 -0
  73. package/skills/log-audit/reference/CLAUDE.md +9 -0
  74. package/skills/log-audit/reference/charter.md +52 -0
  75. package/skills/log-audit/scripts/CLAUDE.md +27 -0
  76. package/skills/log-audit/scripts/cluster_recurrences.py +261 -0
  77. package/skills/log-audit/scripts/collect_log_window.py +199 -0
  78. package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +12 -0
  79. package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +23 -0
  80. package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +24 -0
  81. package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +49 -0
  82. package/skills/log-audit/scripts/mine_copilot_findings.py +302 -0
  83. package/skills/log-audit/scripts/test_cluster_recurrences.py +160 -0
  84. package/skills/log-audit/scripts/test_collect_log_window.py +111 -0
  85. package/skills/log-audit/scripts/test_mine_copilot_findings.py +126 -0
  86. package/skills/monitor-open-prs/SKILL.md +2 -2
  87. package/skills/post-audit-findings/SKILL.md +84 -0
  88. package/skills/pr-converge/CLAUDE.md +2 -0
  89. package/skills/pr-converge/SKILL.md +72 -59
  90. package/skills/pr-converge/reference/CLAUDE.md +1 -1
  91. package/skills/pr-converge/reference/convergence-gates.md +16 -19
  92. package/skills/pr-converge/reference/examples.md +5 -5
  93. package/skills/pr-converge/reference/fix-protocol.md +16 -43
  94. package/skills/pr-converge/reference/obstacles/fix-publish-summary.md +1 -1
  95. package/skills/pr-converge/reference/per-tick.md +24 -45
  96. package/skills/pr-converge/reference/state-schema.md +15 -0
  97. package/skills/pr-converge/scripts/README.md +3 -5
  98. package/skills/pr-fix-protocol/SKILL.md +70 -0
  99. package/skills/pr-loop-lifecycle/SKILL.md +73 -0
  100. package/skills/{bugteam → pr-loop-lifecycle}/reference/teardown-publish-permissions.md +22 -24
  101. package/skills/pr-scope-resolve/SKILL.md +48 -0
  102. package/skills/qbug/CLAUDE.md +4 -4
  103. package/skills/qbug/SKILL.md +46 -144
  104. package/skills/qbug/test_qbug_skill_audit_schema.py +2 -2
  105. package/skills/qbug/test_qbug_skill_post_fix_audit.py +1 -1
  106. package/skills/reviewer-gates/SKILL.md +96 -0
  107. package/skills/session-log/CLAUDE.md +7 -7
  108. package/skills/session-log/SKILL.md +27 -44
  109. package/skills/test_markdown_link_integrity.py +103 -0
  110. package/commands/doc-gist.md +0 -16
  111. package/hooks/blocking/_md_to_html_blocker_test_support.py +0 -65
  112. package/hooks/blocking/conftest.py +0 -30
  113. package/hooks/blocking/md_path_exemptions.py +0 -224
  114. package/hooks/blocking/md_to_html_blocker.py +0 -155
  115. package/hooks/blocking/test_md_to_html_blocker_exemptions.py +0 -434
  116. package/hooks/blocking/test_md_to_html_blocker_extensions.py +0 -157
  117. package/hooks/blocking/test_md_to_html_blocker_path_resolution.py +0 -336
  118. package/hooks/hooks_constants/doc_gist_auto_publish_constants.py +0 -18
  119. package/hooks/hooks_constants/html_companion_constants.py +0 -20
  120. package/hooks/hooks_constants/md_to_html_blocker_constants.py +0 -76
  121. package/hooks/hooks_constants/test_md_to_html_blocker_constants.py +0 -125
  122. package/hooks/workflow/doc_gist_auto_publish.py +0 -144
  123. package/hooks/workflow/md_to_html_companion.py +0 -358
  124. package/hooks/workflow/test_doc_gist_auto_publish.py +0 -117
  125. package/hooks/workflow/test_md_to_html_companion.py +0 -613
  126. package/skills/bugteam/reference/audit-contract.md +0 -163
  127. package/skills/bugteam/scripts/_bugteam_permissions_common.py +0 -455
  128. package/skills/bugteam/scripts/bugteam_scripts_constants/claude_permissions_common_constants.py +0 -69
  129. package/skills/bugteam/scripts/grant_project_claude_permissions.py +0 -280
  130. package/skills/bugteam/scripts/revoke_project_claude_permissions.py +0 -266
  131. package/skills/bugteam/scripts/test__bugteam_permissions_common.py +0 -160
  132. package/skills/bugteam/scripts/test_agent_config_carveout.py +0 -356
  133. package/skills/bugteam/scripts/test_bugteam_permissions_common.py +0 -140
  134. package/skills/doc-gist/CLAUDE.md +0 -25
  135. package/skills/doc-gist/SKILL.md +0 -97
  136. package/skills/doc-gist/references/CLAUDE.md +0 -9
  137. package/skills/doc-gist/references/examples/01-exploration-code-approaches.html +0 -453
  138. package/skills/doc-gist/references/examples/02-exploration-visual-designs.html +0 -515
  139. package/skills/doc-gist/references/examples/03-code-review-pr.html +0 -638
  140. package/skills/doc-gist/references/examples/04-code-understanding.html +0 -491
  141. package/skills/doc-gist/references/examples/05-design-system.html +0 -629
  142. package/skills/doc-gist/references/examples/06-component-variants.html +0 -605
  143. package/skills/doc-gist/references/examples/07-prototype-animation.html +0 -455
  144. package/skills/doc-gist/references/examples/08-prototype-interaction.html +0 -396
  145. package/skills/doc-gist/references/examples/09-slide-deck.html +0 -592
  146. package/skills/doc-gist/references/examples/10-svg-illustrations.html +0 -492
  147. package/skills/doc-gist/references/examples/11-status-report.html +0 -528
  148. package/skills/doc-gist/references/examples/12-incident-report.html +0 -596
  149. package/skills/doc-gist/references/examples/13-flowchart-diagram.html +0 -395
  150. package/skills/doc-gist/references/examples/14-research-feature-explainer.html +0 -381
  151. package/skills/doc-gist/references/examples/15-research-concept-explainer.html +0 -368
  152. package/skills/doc-gist/references/examples/16-implementation-plan.html +0 -702
  153. package/skills/doc-gist/references/examples/17-pr-writeup.html +0 -595
  154. package/skills/doc-gist/references/examples/18-editor-triage-board.html +0 -573
  155. package/skills/doc-gist/references/examples/19-editor-feature-flags.html +0 -663
  156. package/skills/doc-gist/references/examples/20-editor-prompt-tuner.html +0 -722
  157. package/skills/doc-gist/references/examples/21-decision-signoff.html +0 -546
  158. package/skills/doc-gist/references/examples/CLAUDE.md +0 -25
  159. package/skills/doc-gist/references/examples/README.md +0 -5
  160. package/skills/doc-gist/scripts/CLAUDE.md +0 -27
  161. package/skills/doc-gist/scripts/doc_gist_scripts_constants/CLAUDE.md +0 -10
  162. package/skills/doc-gist/scripts/doc_gist_scripts_constants/gist_upload_constants.py +0 -16
  163. package/skills/doc-gist/scripts/gist_upload.py +0 -177
  164. package/skills/doc-gist/scripts/test_gist_upload.py +0 -51
  165. /package/skills/{doc-gist/scripts/doc_gist_scripts_constants → log-audit/scripts/log_audit_constants}/__init__.py +0 -0
@@ -1,592 +0,0 @@
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>Platform Eng — Week of Mar 10</title>
7
- <style>
8
- /* ------------------------------------------------------------------
9
- Design tokens
10
- ------------------------------------------------------------------ */
11
- :root {
12
- --ivory: #FAF9F5;
13
- --slate: #141413;
14
- --clay: #D97757;
15
- --oat: #E3DACC;
16
- --olive: #788C5D;
17
- --gray-150:#F0EEE6;
18
- --gray-300:#D1CFC5;
19
- --gray-500:#87867F;
20
- --gray-700:#3D3D3A;
21
-
22
- --serif: ui-serif, Georgia, "Times New Roman", serif;
23
- --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
24
- --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
25
- }
26
-
27
- * { margin: 0; padding: 0; box-sizing: border-box; }
28
-
29
- html { scroll-behavior: smooth; }
30
-
31
- body {
32
- font-family: var(--sans);
33
- background: var(--ivory);
34
- color: var(--slate);
35
- -webkit-font-smoothing: antialiased;
36
- scroll-snap-type: y mandatory;
37
- overflow-x: hidden;
38
- }
39
-
40
- /* ------------------------------------------------------------------
41
- Slide shell
42
- ------------------------------------------------------------------ */
43
- .slide {
44
- width: 100vw;
45
- height: 100vh;
46
- scroll-snap-align: start;
47
- scroll-snap-stop: always;
48
- display: flex;
49
- align-items: center;
50
- justify-content: center;
51
- padding: 8vh 6vw;
52
- }
53
-
54
- .slide-inner {
55
- width: 100%;
56
- max-width: 780px;
57
- }
58
-
59
- .slide.invert {
60
- background: var(--slate);
61
- color: var(--ivory);
62
- }
63
-
64
- /* ------------------------------------------------------------------
65
- Typography
66
- ------------------------------------------------------------------ */
67
- .eyebrow {
68
- font-family: var(--mono);
69
- font-size: 12px;
70
- letter-spacing: 0.12em;
71
- text-transform: uppercase;
72
- color: var(--gray-500);
73
- margin-bottom: 28px;
74
- }
75
- .invert .eyebrow { color: var(--gray-300); }
76
-
77
- h1 {
78
- font-family: var(--serif);
79
- font-weight: 500;
80
- font-size: clamp(40px, 6vw, 64px);
81
- line-height: 1.08;
82
- letter-spacing: -0.01em;
83
- margin-bottom: 20px;
84
- }
85
-
86
- h2 {
87
- font-family: var(--serif);
88
- font-weight: 500;
89
- font-size: clamp(30px, 4vw, 42px);
90
- line-height: 1.15;
91
- letter-spacing: -0.005em;
92
- margin-bottom: 44px;
93
- }
94
-
95
- .subtitle {
96
- font-size: 17px;
97
- line-height: 1.6;
98
- color: var(--gray-700);
99
- max-width: 520px;
100
- }
101
-
102
- .mono { font-family: var(--mono); }
103
-
104
- /* ------------------------------------------------------------------
105
- Slide 1 — title
106
- ------------------------------------------------------------------ */
107
- .title-slide .slide-inner { text-align: left; }
108
-
109
- .ornament {
110
- display: block;
111
- margin: 0 0 40px 0;
112
- }
113
-
114
- .byline {
115
- margin-top: 56px;
116
- font-family: var(--mono);
117
- font-size: 12px;
118
- color: var(--gray-500);
119
- display: flex;
120
- gap: 24px;
121
- }
122
-
123
- /* ------------------------------------------------------------------
124
- Slide 2 — shipped list
125
- ------------------------------------------------------------------ */
126
- .ship-list {
127
- list-style: none;
128
- display: flex;
129
- flex-direction: column;
130
- gap: 28px;
131
- }
132
-
133
- .ship-item {
134
- display: grid;
135
- grid-template-columns: 14px 1fr auto;
136
- column-gap: 20px;
137
- align-items: baseline;
138
- padding-bottom: 24px;
139
- border-bottom: 1px solid var(--gray-150);
140
- }
141
- .ship-item:last-child { border-bottom: none; }
142
-
143
- .ship-dot {
144
- width: 9px;
145
- height: 9px;
146
- border-radius: 50%;
147
- background: var(--olive);
148
- margin-top: 8px;
149
- align-self: start;
150
- }
151
-
152
- .ship-title {
153
- font-family: var(--serif);
154
- font-size: 20px;
155
- font-weight: 500;
156
- margin-bottom: 6px;
157
- }
158
-
159
- .ship-desc {
160
- font-size: 14px;
161
- line-height: 1.55;
162
- color: var(--gray-700);
163
- }
164
-
165
- .ship-ref {
166
- font-family: var(--mono);
167
- font-size: 11px;
168
- color: var(--gray-500);
169
- white-space: nowrap;
170
- padding-top: 4px;
171
- }
172
-
173
- /* ------------------------------------------------------------------
174
- Slide 3 — in progress
175
- ------------------------------------------------------------------ */
176
- .prog-list {
177
- list-style: none;
178
- display: flex;
179
- flex-direction: column;
180
- gap: 40px;
181
- }
182
-
183
- .prog-item .prog-head {
184
- display: flex;
185
- justify-content: space-between;
186
- align-items: baseline;
187
- margin-bottom: 10px;
188
- }
189
-
190
- .prog-title {
191
- font-family: var(--serif);
192
- font-size: 20px;
193
- font-weight: 500;
194
- }
195
-
196
- .prog-pct {
197
- font-family: var(--mono);
198
- font-size: 12px;
199
- color: var(--gray-500);
200
- }
201
-
202
- .prog-track {
203
- width: 100%;
204
- height: 5px;
205
- background: var(--gray-150);
206
- border-radius: 3px;
207
- overflow: hidden;
208
- margin-bottom: 10px;
209
- }
210
-
211
- .prog-fill {
212
- height: 100%;
213
- background: var(--clay);
214
- border-radius: 3px;
215
- }
216
-
217
- .prog-note {
218
- font-size: 13px;
219
- line-height: 1.55;
220
- color: var(--gray-700);
221
- }
222
-
223
- /* ------------------------------------------------------------------
224
- Slide 4 — metrics
225
- ------------------------------------------------------------------ */
226
- .metrics {
227
- display: grid;
228
- grid-template-columns: 1fr 1fr;
229
- gap: 56px;
230
- margin-bottom: 48px;
231
- }
232
-
233
- .metric-label {
234
- font-family: var(--mono);
235
- font-size: 11px;
236
- letter-spacing: 0.08em;
237
- text-transform: uppercase;
238
- color: var(--gray-500);
239
- margin-bottom: 14px;
240
- }
241
-
242
- .metric-value {
243
- font-family: var(--serif);
244
- font-size: 52px;
245
- font-weight: 500;
246
- line-height: 1;
247
- letter-spacing: -0.01em;
248
- }
249
-
250
- .metric-delta {
251
- font-family: var(--mono);
252
- font-size: 13px;
253
- margin-top: 12px;
254
- }
255
- .metric-delta.down { color: var(--olive); }
256
- .metric-delta.up { color: var(--clay); }
257
-
258
- .sparkline-wrap {
259
- border-top: 1px solid var(--gray-300);
260
- padding-top: 28px;
261
- }
262
-
263
- .sparkline-caption {
264
- font-size: 12px;
265
- color: var(--gray-500);
266
- margin-top: 10px;
267
- }
268
-
269
- /* ------------------------------------------------------------------
270
- Slide 5 — decision (inverted)
271
- ------------------------------------------------------------------ */
272
- .decision-card {
273
- border: 1.5px solid var(--clay);
274
- border-radius: 14px;
275
- padding: 36px 38px;
276
- background: rgba(217, 119, 87, 0.06);
277
- }
278
-
279
- .decision-q {
280
- font-family: var(--serif);
281
- font-size: 24px;
282
- line-height: 1.4;
283
- margin-bottom: 12px;
284
- }
285
-
286
- .decision-context {
287
- font-size: 14px;
288
- line-height: 1.6;
289
- color: var(--gray-300);
290
- }
291
-
292
- .options {
293
- display: flex;
294
- gap: 14px;
295
- margin-top: 32px;
296
- flex-wrap: wrap;
297
- }
298
-
299
- .chip {
300
- font-family: var(--mono);
301
- font-size: 12px;
302
- padding: 10px 18px;
303
- border-radius: 999px;
304
- border: 1px solid var(--gray-700);
305
- color: var(--ivory);
306
- background: transparent;
307
- }
308
- .chip.lean {
309
- border-color: var(--clay);
310
- color: var(--clay);
311
- }
312
-
313
- /* ------------------------------------------------------------------
314
- Slide 6 — next week
315
- ------------------------------------------------------------------ */
316
- .next-list {
317
- list-style: none;
318
- display: flex;
319
- flex-direction: column;
320
- gap: 22px;
321
- }
322
-
323
- .next-list li {
324
- font-family: var(--serif);
325
- font-size: 21px;
326
- line-height: 1.45;
327
- padding-left: 36px;
328
- position: relative;
329
- }
330
-
331
- .next-list li::before {
332
- content: "";
333
- position: absolute;
334
- left: 0;
335
- top: 0.55em;
336
- width: 18px;
337
- height: 1.5px;
338
- background: var(--clay);
339
- }
340
-
341
- .footnote {
342
- margin-top: 56px;
343
- font-family: var(--mono);
344
- font-size: 11px;
345
- color: var(--gray-500);
346
- }
347
-
348
- /* ------------------------------------------------------------------
349
- Slide counter
350
- ------------------------------------------------------------------ */
351
- #counter {
352
- position: fixed;
353
- bottom: 22px;
354
- right: 28px;
355
- font-family: var(--mono);
356
- font-size: 12px;
357
- color: var(--gray-500);
358
- user-select: none;
359
- z-index: 10;
360
- }
361
-
362
- @media (max-width: 640px) {
363
- .metrics { grid-template-columns: 1fr; gap: 36px; }
364
- .ship-item { grid-template-columns: 14px 1fr; }
365
- .ship-ref { grid-column: 2; padding-top: 8px; }
366
- }
367
- </style>
368
- </head>
369
- <body>
370
-
371
- <!-- ============================================================
372
- Slide 1 — Title
373
- ============================================================ -->
374
- <section class="slide title-slide" id="s1">
375
- <div class="slide-inner">
376
- <svg class="ornament" width="56" height="56" viewBox="0 0 56 56" aria-hidden="true">
377
- <circle cx="28" cy="28" r="22" fill="none" stroke="#D1CFC5" stroke-width="1.5"/>
378
- <path d="M16 28 H40 M28 16 V40" stroke="#D97757" stroke-width="1.5" stroke-linecap="round"/>
379
- <circle cx="28" cy="28" r="3" fill="#141413"/>
380
- </svg>
381
- <h1>Platform Eng<br>— Week of Mar 10</h1>
382
- <p class="subtitle">
383
- What shipped, what's moving, and one decision we need from the
384
- room before the Birchline 2.4 cut.
385
- </p>
386
- <div class="byline">
387
- <span>Friday demo</span>
388
- <span>6 slides</span>
389
- <span>~4 min</span>
390
- </div>
391
- </div>
392
- </section>
393
-
394
- <!-- ============================================================
395
- Slide 2 — Shipped
396
- ============================================================ -->
397
- <section class="slide" id="s2">
398
- <div class="slide-inner">
399
- <div class="eyebrow">Shipped this week</div>
400
- <h2>Three things out the door</h2>
401
- <ul class="ship-list">
402
- <li class="ship-item">
403
- <span class="ship-dot"></span>
404
- <div>
405
- <div class="ship-title">Bulk task import</div>
406
- <div class="ship-desc">
407
- CSV and JSON uploads now land straight into a board with
408
- column mapping — no more paste-and-pray.
409
- </div>
410
- </div>
411
- <span class="ship-ref">#4211</span>
412
- </li>
413
- <li class="ship-item">
414
- <span class="ship-dot"></span>
415
- <div>
416
- <div class="ship-title">Webhook retries v2</div>
417
- <div class="ship-desc">
418
- Exponential backoff with jitter; dead-letter queue surfaces
419
- in the workspace admin panel.
420
- </div>
421
- </div>
422
- <span class="ship-ref">#4188 #4203</span>
423
- </li>
424
- <li class="ship-item">
425
- <span class="ship-dot"></span>
426
- <div>
427
- <div class="ship-title">Postgres 16 migration</div>
428
- <div class="ship-desc">
429
- All read replicas cut over Tuesday night; zero customer-facing
430
- downtime, ~9% faster aggregate queries.
431
- </div>
432
- </div>
433
- <span class="ship-ref">#4179</span>
434
- </li>
435
- </ul>
436
- </div>
437
- </section>
438
-
439
- <!-- ============================================================
440
- Slide 3 — In progress
441
- ============================================================ -->
442
- <section class="slide" id="s3">
443
- <div class="slide-inner">
444
- <div class="eyebrow">In progress</div>
445
- <h2>Carrying into next week</h2>
446
- <ul class="prog-list">
447
- <li class="prog-item">
448
- <div class="prog-head">
449
- <span class="prog-title">Recurring tasks engine</span>
450
- <span class="prog-pct">~70%</span>
451
- </div>
452
- <div class="prog-track"><div class="prog-fill" style="width:70%"></div></div>
453
- <p class="prog-note">
454
- Scheduler and RRULE parsing are done; remaining work is the
455
- timezone edge cases and the "skip holidays" toggle.
456
- </p>
457
- </li>
458
- <li class="prog-item">
459
- <div class="prog-head">
460
- <span class="prog-title">Audit log export</span>
461
- <span class="prog-pct">~35%</span>
462
- </div>
463
- <div class="prog-track"><div class="prog-fill" style="width:35%"></div></div>
464
- <p class="prog-note">
465
- Streaming NDJSON endpoint is up behind a flag; still wiring
466
- the S3 destination picker and retention policy UI.
467
- </p>
468
- </li>
469
- </ul>
470
- </div>
471
- </section>
472
-
473
- <!-- ============================================================
474
- Slide 4 — Metrics
475
- ============================================================ -->
476
- <section class="slide" id="s4">
477
- <div class="slide-inner">
478
- <div class="eyebrow">Metrics</div>
479
- <h2>Numbers we watch</h2>
480
-
481
- <div class="metrics">
482
- <div class="metric">
483
- <div class="metric-label">API p95 latency</div>
484
- <div class="metric-value">184<span style="font-size:28px">ms</span></div>
485
- <div class="metric-delta down">↓ 12% wk/wk</div>
486
- </div>
487
- <div class="metric">
488
- <div class="metric-label">Background job error rate</div>
489
- <div class="metric-value">0.21<span style="font-size:28px">%</span></div>
490
- <div class="metric-delta down">↓ 0.08pp</div>
491
- </div>
492
- </div>
493
-
494
- <div class="sparkline-wrap">
495
- <svg width="100%" height="64" viewBox="0 0 780 64" preserveAspectRatio="none" aria-hidden="true">
496
- <polyline
497
- points="0,22 110,30 220,18 330,34 440,26 550,40 660,24 780,12"
498
- fill="none"
499
- stroke="#788C5D"
500
- stroke-width="2"
501
- stroke-linejoin="round"
502
- stroke-linecap="round"/>
503
- <circle cx="780" cy="12" r="3.5" fill="#788C5D"/>
504
- <line x1="0" y1="63" x2="780" y2="63" stroke="#D1CFC5" stroke-width="1"/>
505
- </svg>
506
- <div class="sparkline-caption">
507
- p95 latency, trailing 8 days — lower is better
508
- </div>
509
- </div>
510
- </div>
511
- </section>
512
-
513
- <!-- ============================================================
514
- Slide 5 — Decision needed (inverted)
515
- ============================================================ -->
516
- <section class="slide invert" id="s5">
517
- <div class="slide-inner">
518
- <div class="eyebrow">Decision needed</div>
519
- <h2>One call to make</h2>
520
-
521
- <div class="decision-card">
522
- <p class="decision-q">
523
- Do we ship recurring tasks behind a workspace flag in 2.4, or
524
- hold one more week for the timezone fixes?
525
- </p>
526
- <p class="decision-context">
527
- Flagged rollout gets it to design partners Friday but means two
528
- code paths for ~2 weeks. Holding keeps a single path but slips
529
- the partner promise.
530
- </p>
531
- </div>
532
-
533
- <div class="options">
534
- <span class="chip lean">A — Flag it, ship Friday</span>
535
- <span class="chip">B — Hold for 2.5</span>
536
- </div>
537
- </div>
538
- </section>
539
-
540
- <!-- ============================================================
541
- Slide 6 — Next week
542
- ============================================================ -->
543
- <section class="slide" id="s6">
544
- <div class="slide-inner">
545
- <div class="eyebrow">Next week</div>
546
- <h2>On deck</h2>
547
- <ul class="next-list">
548
- <li>Finish recurring tasks — timezone matrix tests, then dogfood on the internal ops board.</li>
549
- <li>Audit log export to private beta — three workspaces lined up.</li>
550
- <li>Start scoping rate-limit headers for the public API; RFC draft by Thursday.</li>
551
- </ul>
552
- <div class="footnote">
553
- Questions → drop them in the platform channel or grab anyone after standup.
554
- </div>
555
- </div>
556
- </section>
557
-
558
- <!-- ============================================================
559
- Slide counter
560
- ============================================================ -->
561
- <div id="counter">1 / 6</div>
562
-
563
- <script>
564
- (function () {
565
- const slides = Array.from(document.querySelectorAll('.slide'));
566
- const counter = document.getElementById('counter');
567
- let current = 0;
568
-
569
- function go(i) {
570
- current = Math.max(0, Math.min(slides.length - 1, i));
571
- slides[current].scrollIntoView({ behavior: 'smooth' });
572
- }
573
-
574
- document.addEventListener('keydown', function (e) {
575
- if (e.key === 'ArrowRight' || e.key === 'ArrowDown' || e.key === ' ') { e.preventDefault(); go(current + 1); }
576
- if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') { e.preventDefault(); go(current - 1); }
577
- });
578
-
579
- const obs = new IntersectionObserver(function (entries) {
580
- entries.forEach(function (en) {
581
- if (en.isIntersecting) {
582
- current = slides.indexOf(en.target);
583
- counter.textContent = (current + 1) + ' / ' + slides.length;
584
- }
585
- });
586
- }, { threshold: 0.6 });
587
-
588
- slides.forEach(function (s) { obs.observe(s); });
589
- })();
590
- </script>
591
- </body>
592
- </html>