claude-dev-env 1.83.0 → 1.85.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 (186) 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/pr_loop_shared_constants/CLAUDE.md +1 -1
  5. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +8 -0
  6. package/_shared/pr-loop/scripts/reviews_disabled.py +2 -0
  7. package/_shared/pr-loop/scripts/terminology_sweep.py +85 -39
  8. package/_shared/pr-loop/scripts/tests/test__claude_permissions_common.py +212 -0
  9. package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +18 -0
  10. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +18 -0
  11. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +37 -0
  12. package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +14 -0
  13. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +94 -4
  14. package/agents/clean-coder.md +3 -0
  15. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +1 -1
  16. package/bin/install.mjs +16 -8
  17. package/bin/install.test.mjs +17 -6
  18. package/commands/CLAUDE.md +0 -1
  19. package/docs/CODE_RULES.md +1 -1
  20. package/hooks/blocking/CLAUDE.md +0 -4
  21. package/hooks/blocking/claude_md_orphan_file_blocker.py +1 -1
  22. package/hooks/blocking/code_rules_constants_config.py +5 -0
  23. package/hooks/blocking/code_rules_docstrings.py +133 -19
  24. package/hooks/blocking/code_rules_enforcer.py +4 -0
  25. package/hooks/blocking/code_rules_imports_logging.py +10 -6
  26. package/hooks/blocking/code_rules_magic_values.py +5 -0
  27. package/hooks/blocking/code_rules_naming_collection.py +15 -8
  28. package/hooks/blocking/code_rules_paired_test.py +3 -2
  29. package/hooks/blocking/code_rules_shared.py +34 -0
  30. package/hooks/blocking/code_rules_string_magic.py +1 -1
  31. package/hooks/blocking/code_rules_unused_imports.py +2 -2
  32. package/hooks/blocking/duplicate_rmtree_helper_blocker.py +4 -4
  33. package/hooks/blocking/test_code_rules_enforcer_docstring_args_span_scope.py +29 -0
  34. package/hooks/blocking/test_code_rules_enforcer_docstring_prose_wall_illustration.py +197 -0
  35. package/hooks/blocking/test_code_rules_enforcer_docstring_runon_sentence.py +27 -0
  36. package/hooks/blocking/test_code_rules_enforcer_naive_datetime.py +24 -0
  37. package/hooks/blocking/test_code_rules_enforcer_split_constants_config.py +33 -0
  38. package/hooks/blocking/test_code_rules_enforcer_split_shared.py +23 -0
  39. package/hooks/blocking/test_code_rules_magic_values.py +54 -0
  40. package/hooks/blocking/test_duplicate_rmtree_helper_blocker.py +0 -6
  41. package/hooks/blocking/test_pr_converge_bugteam_enforcer_state_tolerance.py +184 -0
  42. package/hooks/blocking/test_shared_stdin_adoption.py +5 -30
  43. package/hooks/hooks_constants/CLAUDE.md +0 -3
  44. package/hooks/hooks_constants/blocking_check_limits.py +4 -0
  45. package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -1
  46. package/hooks/hooks_constants/duplicate_rmtree_helper_blocker_constants.py +0 -1
  47. package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -5
  48. package/hooks/validation/post_tool_use_dispatcher.py +1 -1
  49. package/hooks/validation/test_post_tool_use_dispatcher.py +51 -35
  50. package/hooks/workflow/CLAUDE.md +2 -8
  51. package/package.json +1 -1
  52. package/rules/CLAUDE.md +1 -1
  53. package/rules/docstring-prose-matches-implementation.md +1 -1
  54. package/rules/env-var-table-code-drift.md +1 -1
  55. package/rules/package-inventory-stale-entry.md +1 -1
  56. package/rules/paired-test-coverage.md +1 -1
  57. package/rules/plain-illustrative-docstrings.md +35 -1
  58. package/skills/CLAUDE.md +6 -1
  59. package/skills/_shared/pr-loop/CLAUDE.md +1 -0
  60. package/skills/_shared/pr-loop/scripts/CLAUDE.md +1 -0
  61. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +1 -0
  62. package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +45 -0
  63. package/skills/_shared/pr-loop/scripts/test_write_handoff.py +201 -0
  64. package/skills/_shared/pr-loop/scripts/write_handoff.py +309 -0
  65. package/skills/autoconverge/SKILL.md +82 -58
  66. package/skills/autoconverge/reference/closing-report.md +6 -6
  67. package/skills/autoconverge/reference/convergence.md +17 -15
  68. package/skills/autoconverge/reference/gotchas.md +6 -3
  69. package/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +2 -9
  70. package/skills/autoconverge/workflow/converge.contract.test.mjs +87 -13
  71. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +1 -1
  72. package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +16 -9
  73. package/skills/autoconverge/workflow/converge.mjs +71 -74
  74. package/skills/autoconverge/workflow/render_report.py +7 -11
  75. package/skills/bugteam/CLAUDE.md +1 -1
  76. package/skills/bugteam/PROMPTS.md +7 -6
  77. package/skills/bugteam/SKILL.md +25 -72
  78. package/skills/bugteam/reference/CLAUDE.md +1 -3
  79. package/skills/bugteam/reference/README.md +1 -1
  80. package/skills/bugteam/reference/audit-and-teammates.md +1 -1
  81. package/skills/bugteam/reference/obstacles/fix-publish-summary.md +1 -1
  82. package/skills/bugteam/reference/team-setup.md +8 -7
  83. package/skills/bugteam/scripts/CLAUDE.md +0 -6
  84. package/skills/bugteam/scripts/README.md +0 -4
  85. package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +0 -1
  86. package/skills/code/SKILL.md +2 -0
  87. package/skills/copilot-review/CLAUDE.md +1 -1
  88. package/skills/copilot-review/SKILL.md +25 -23
  89. package/skills/findbugs/CLAUDE.md +2 -2
  90. package/skills/findbugs/SKILL.md +22 -83
  91. package/skills/fixbugs/SKILL.md +2 -4
  92. package/skills/log-audit/CLAUDE.md +20 -0
  93. package/skills/log-audit/SKILL.md +68 -0
  94. package/skills/log-audit/reference/CLAUDE.md +9 -0
  95. package/skills/log-audit/reference/charter.md +52 -0
  96. package/skills/log-audit/scripts/CLAUDE.md +27 -0
  97. package/skills/log-audit/scripts/cluster_recurrences.py +261 -0
  98. package/skills/log-audit/scripts/collect_log_window.py +199 -0
  99. package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +12 -0
  100. package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +23 -0
  101. package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +24 -0
  102. package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +49 -0
  103. package/skills/log-audit/scripts/mine_copilot_findings.py +302 -0
  104. package/skills/log-audit/scripts/test_cluster_recurrences.py +160 -0
  105. package/skills/log-audit/scripts/test_collect_log_window.py +111 -0
  106. package/skills/log-audit/scripts/test_mine_copilot_findings.py +126 -0
  107. package/skills/monitor-open-prs/SKILL.md +2 -2
  108. package/skills/post-audit-findings/SKILL.md +84 -0
  109. package/skills/pr-converge/CLAUDE.md +2 -0
  110. package/skills/pr-converge/SKILL.md +99 -60
  111. package/skills/pr-converge/reference/CLAUDE.md +1 -1
  112. package/skills/pr-converge/reference/convergence-gates.md +16 -19
  113. package/skills/pr-converge/reference/examples.md +5 -5
  114. package/skills/pr-converge/reference/fix-protocol.md +16 -43
  115. package/skills/pr-converge/reference/obstacles/fix-publish-summary.md +1 -1
  116. package/skills/pr-converge/reference/per-tick.md +24 -45
  117. package/skills/pr-converge/reference/state-schema.md +27 -0
  118. package/skills/pr-converge/scripts/README.md +3 -5
  119. package/skills/pr-fix-protocol/SKILL.md +70 -0
  120. package/skills/pr-loop-lifecycle/SKILL.md +73 -0
  121. package/skills/{bugteam → pr-loop-lifecycle}/reference/teardown-publish-permissions.md +22 -24
  122. package/skills/pr-scope-resolve/SKILL.md +48 -0
  123. package/skills/qbug/CLAUDE.md +4 -4
  124. package/skills/qbug/SKILL.md +46 -144
  125. package/skills/qbug/test_qbug_skill_audit_schema.py +2 -2
  126. package/skills/qbug/test_qbug_skill_post_fix_audit.py +1 -1
  127. package/skills/reviewer-gates/SKILL.md +96 -0
  128. package/skills/session-log/CLAUDE.md +7 -7
  129. package/skills/session-log/SKILL.md +27 -44
  130. package/skills/test_markdown_link_integrity.py +103 -0
  131. package/commands/doc-gist.md +0 -16
  132. package/hooks/blocking/_md_to_html_blocker_test_support.py +0 -65
  133. package/hooks/blocking/conftest.py +0 -30
  134. package/hooks/blocking/md_path_exemptions.py +0 -224
  135. package/hooks/blocking/md_to_html_blocker.py +0 -155
  136. package/hooks/blocking/test_md_to_html_blocker_exemptions.py +0 -434
  137. package/hooks/blocking/test_md_to_html_blocker_extensions.py +0 -157
  138. package/hooks/blocking/test_md_to_html_blocker_path_resolution.py +0 -336
  139. package/hooks/hooks_constants/doc_gist_auto_publish_constants.py +0 -18
  140. package/hooks/hooks_constants/html_companion_constants.py +0 -20
  141. package/hooks/hooks_constants/md_to_html_blocker_constants.py +0 -76
  142. package/hooks/hooks_constants/test_md_to_html_blocker_constants.py +0 -125
  143. package/hooks/workflow/doc_gist_auto_publish.py +0 -144
  144. package/hooks/workflow/md_to_html_companion.py +0 -358
  145. package/hooks/workflow/test_doc_gist_auto_publish.py +0 -117
  146. package/hooks/workflow/test_md_to_html_companion.py +0 -613
  147. package/skills/bugteam/reference/audit-contract.md +0 -163
  148. package/skills/bugteam/scripts/_bugteam_permissions_common.py +0 -455
  149. package/skills/bugteam/scripts/bugteam_scripts_constants/claude_permissions_common_constants.py +0 -69
  150. package/skills/bugteam/scripts/grant_project_claude_permissions.py +0 -280
  151. package/skills/bugteam/scripts/revoke_project_claude_permissions.py +0 -266
  152. package/skills/bugteam/scripts/test__bugteam_permissions_common.py +0 -160
  153. package/skills/bugteam/scripts/test_agent_config_carveout.py +0 -356
  154. package/skills/bugteam/scripts/test_bugteam_permissions_common.py +0 -140
  155. package/skills/doc-gist/CLAUDE.md +0 -25
  156. package/skills/doc-gist/SKILL.md +0 -97
  157. package/skills/doc-gist/references/CLAUDE.md +0 -9
  158. package/skills/doc-gist/references/examples/01-exploration-code-approaches.html +0 -453
  159. package/skills/doc-gist/references/examples/02-exploration-visual-designs.html +0 -515
  160. package/skills/doc-gist/references/examples/03-code-review-pr.html +0 -638
  161. package/skills/doc-gist/references/examples/04-code-understanding.html +0 -491
  162. package/skills/doc-gist/references/examples/05-design-system.html +0 -629
  163. package/skills/doc-gist/references/examples/06-component-variants.html +0 -605
  164. package/skills/doc-gist/references/examples/07-prototype-animation.html +0 -455
  165. package/skills/doc-gist/references/examples/08-prototype-interaction.html +0 -396
  166. package/skills/doc-gist/references/examples/09-slide-deck.html +0 -592
  167. package/skills/doc-gist/references/examples/10-svg-illustrations.html +0 -492
  168. package/skills/doc-gist/references/examples/11-status-report.html +0 -528
  169. package/skills/doc-gist/references/examples/12-incident-report.html +0 -596
  170. package/skills/doc-gist/references/examples/13-flowchart-diagram.html +0 -395
  171. package/skills/doc-gist/references/examples/14-research-feature-explainer.html +0 -381
  172. package/skills/doc-gist/references/examples/15-research-concept-explainer.html +0 -368
  173. package/skills/doc-gist/references/examples/16-implementation-plan.html +0 -702
  174. package/skills/doc-gist/references/examples/17-pr-writeup.html +0 -595
  175. package/skills/doc-gist/references/examples/18-editor-triage-board.html +0 -573
  176. package/skills/doc-gist/references/examples/19-editor-feature-flags.html +0 -663
  177. package/skills/doc-gist/references/examples/20-editor-prompt-tuner.html +0 -722
  178. package/skills/doc-gist/references/examples/21-decision-signoff.html +0 -546
  179. package/skills/doc-gist/references/examples/CLAUDE.md +0 -25
  180. package/skills/doc-gist/references/examples/README.md +0 -5
  181. package/skills/doc-gist/scripts/CLAUDE.md +0 -27
  182. package/skills/doc-gist/scripts/doc_gist_scripts_constants/CLAUDE.md +0 -10
  183. package/skills/doc-gist/scripts/doc_gist_scripts_constants/gist_upload_constants.py +0 -16
  184. package/skills/doc-gist/scripts/gist_upload.py +0 -177
  185. package/skills/doc-gist/scripts/test_gist_upload.py +0 -51
  186. /package/skills/{doc-gist/scripts/doc_gist_scripts_constants → log-audit/scripts/log_audit_constants}/__init__.py +0 -0
@@ -1,596 +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>INC-2025-0412 — Elevated 502s on task sync</title>
7
- <style>
8
- :root {
9
- --ivory: #FAF9F5;
10
- --slate: #141413;
11
- --clay: #D97757;
12
- --oat: #E3DACC;
13
- --olive: #788C5D;
14
- --rust: #B04A3F;
15
- --gray-100: #F0EEE6;
16
- --gray-300: #D1CFC5;
17
- --gray-500: #87867F;
18
- --gray-700: #3D3D3A;
19
- --white: #FFFFFF;
20
-
21
- --serif: ui-serif, Georgia, serif;
22
- --sans: system-ui, -apple-system, sans-serif;
23
- --mono: ui-monospace, 'SF Mono', Menlo, monospace;
24
-
25
- --radius-panel: 12px;
26
- --radius-row: 8px;
27
- --border: 1.5px solid var(--gray-300);
28
- }
29
-
30
- * { box-sizing: border-box; }
31
-
32
- html { scroll-behavior: smooth; }
33
-
34
- body {
35
- margin: 0;
36
- padding: 56px 24px 120px;
37
- background: var(--ivory);
38
- color: var(--slate);
39
- font-family: var(--sans);
40
- font-size: 15px;
41
- line-height: 1.6;
42
- -webkit-font-smoothing: antialiased;
43
- }
44
-
45
- .page {
46
- max-width: 820px;
47
- margin: 0 auto;
48
- }
49
-
50
- /* ---------- Header ---------- */
51
- header {
52
- margin-bottom: 32px;
53
- }
54
- .inc-id {
55
- font-family: var(--mono);
56
- font-size: 13px;
57
- color: var(--gray-500);
58
- letter-spacing: 0.02em;
59
- margin-bottom: 8px;
60
- }
61
- h1 {
62
- font-family: var(--serif);
63
- font-weight: 500;
64
- font-size: 36px;
65
- letter-spacing: -0.01em;
66
- margin: 0 0 18px;
67
- line-height: 1.2;
68
- }
69
- .meta-row {
70
- display: flex;
71
- flex-wrap: wrap;
72
- align-items: center;
73
- gap: 10px 12px;
74
- }
75
- .pill {
76
- display: inline-flex;
77
- align-items: baseline;
78
- gap: 6px;
79
- font-family: var(--sans);
80
- font-size: 12px;
81
- font-weight: 600;
82
- border-radius: 999px;
83
- padding: 5px 12px;
84
- line-height: 1;
85
- }
86
- .pill .k {
87
- font-weight: 400;
88
- opacity: 0.75;
89
- }
90
- .pill.sev {
91
- background: var(--clay);
92
- color: var(--white);
93
- letter-spacing: 0.03em;
94
- }
95
- .pill.resolved {
96
- background: var(--olive);
97
- color: var(--white);
98
- }
99
- .pill.neutral {
100
- background: var(--gray-100);
101
- color: var(--gray-700);
102
- border: var(--border);
103
- }
104
- .pill.neutral .v {
105
- font-family: var(--mono);
106
- }
107
-
108
- /* ---------- TL;DR ---------- */
109
- .tldr {
110
- background: var(--slate);
111
- color: var(--ivory);
112
- border-radius: var(--radius-panel);
113
- padding: 22px 26px;
114
- margin-bottom: 48px;
115
- }
116
- .tldr-label {
117
- font-family: var(--mono);
118
- font-size: 11px;
119
- text-transform: uppercase;
120
- letter-spacing: 0.1em;
121
- color: var(--oat);
122
- margin-bottom: 10px;
123
- }
124
- .tldr p {
125
- margin: 0;
126
- font-size: 15.5px;
127
- line-height: 1.65;
128
- }
129
- .tldr code {
130
- font-family: var(--mono);
131
- font-size: 13.5px;
132
- background: rgba(250, 249, 245, 0.12);
133
- padding: 1px 5px;
134
- border-radius: 4px;
135
- }
136
-
137
- /* ---------- Sections ---------- */
138
- section {
139
- margin-bottom: 52px;
140
- scroll-margin-top: 24px;
141
- }
142
- h2 {
143
- font-family: var(--serif);
144
- font-weight: 500;
145
- font-size: 24px;
146
- letter-spacing: -0.01em;
147
- margin: 0 0 6px;
148
- }
149
- hr.rule {
150
- border: none;
151
- border-top: 1px solid var(--gray-300);
152
- margin: 0 0 22px;
153
- }
154
- p { margin: 0 0 14px; }
155
- code.inline {
156
- font-family: var(--mono);
157
- font-size: 13px;
158
- background: var(--gray-100);
159
- padding: 1.5px 5px;
160
- border-radius: 4px;
161
- }
162
-
163
- /* ---------- Timeline ---------- */
164
- .timeline {
165
- position: relative;
166
- padding: 4px 0 4px 16px;
167
- }
168
- .timeline::before {
169
- content: "";
170
- position: absolute;
171
- left: 16px;
172
- top: 8px;
173
- bottom: 8px;
174
- width: 2px;
175
- background: var(--gray-300);
176
- }
177
- .tl-entry {
178
- position: relative;
179
- padding: 0 0 22px 28px;
180
- }
181
- .tl-entry:last-child {
182
- padding-bottom: 0;
183
- }
184
- .tl-dot {
185
- position: absolute;
186
- left: -5px;
187
- top: 6px;
188
- width: 12px;
189
- height: 12px;
190
- border-radius: 50%;
191
- background: var(--gray-500);
192
- border: 2px solid var(--ivory);
193
- box-sizing: content-box;
194
- }
195
- .tl-dot.impact { background: var(--clay); }
196
- .tl-dot.mitigated { background: var(--olive); }
197
- .tl-time {
198
- display: inline-block;
199
- font-family: var(--mono);
200
- font-size: 12px;
201
- color: var(--gray-700);
202
- background: var(--gray-100);
203
- border: 1px solid var(--gray-300);
204
- border-radius: 6px;
205
- padding: 2px 8px;
206
- margin-bottom: 6px;
207
- }
208
- .tl-body {
209
- font-size: 14px;
210
- color: var(--gray-700);
211
- }
212
- .tl-body strong {
213
- color: var(--slate);
214
- font-weight: 600;
215
- }
216
-
217
- /* ---------- Code diff ---------- */
218
- .code-panel {
219
- background: var(--slate);
220
- color: var(--gray-100);
221
- border-radius: var(--radius-panel);
222
- padding: 18px 20px;
223
- font-family: var(--mono);
224
- font-size: 13px;
225
- line-height: 1.7;
226
- overflow-x: auto;
227
- margin: 8px 0 4px;
228
- }
229
- .code-panel .path {
230
- color: var(--gray-500);
231
- font-size: 12px;
232
- margin-bottom: 10px;
233
- display: block;
234
- }
235
- .diff-line { white-space: pre; }
236
- .diff-line.ctx { color: var(--gray-300); }
237
- .diff-line.del { color: #E0897A; }
238
- .diff-line.add { color: #A3B88A; }
239
-
240
- /* ---------- Impact table ---------- */
241
- table.impact {
242
- width: 100%;
243
- max-width: 460px;
244
- border-collapse: separate;
245
- border-spacing: 0;
246
- background: var(--white);
247
- border: var(--border);
248
- border-radius: var(--radius-panel);
249
- overflow: hidden;
250
- }
251
- table.impact th,
252
- table.impact td {
253
- text-align: left;
254
- padding: 12px 18px;
255
- font-size: 14px;
256
- border-bottom: 1px solid var(--gray-100);
257
- }
258
- table.impact tr:last-child th,
259
- table.impact tr:last-child td {
260
- border-bottom: none;
261
- }
262
- table.impact th {
263
- font-weight: 400;
264
- color: var(--gray-500);
265
- width: 55%;
266
- }
267
- table.impact td {
268
- font-family: var(--mono);
269
- color: var(--slate);
270
- }
271
-
272
- /* ---------- Action items ---------- */
273
- .actions {
274
- background: var(--white);
275
- border: var(--border);
276
- border-radius: var(--radius-panel);
277
- overflow: hidden;
278
- }
279
- .ai-row {
280
- display: grid;
281
- grid-template-columns: 36px 36px 1fr 96px;
282
- align-items: center;
283
- gap: 14px;
284
- padding: 14px 18px;
285
- border-bottom: 1px solid var(--gray-100);
286
- }
287
- .ai-row:last-child { border-bottom: none; }
288
- .ai-check {
289
- width: 18px;
290
- height: 18px;
291
- border: 1.5px solid var(--gray-300);
292
- border-radius: 5px;
293
- background: var(--white);
294
- position: relative;
295
- }
296
- .ai-row.done .ai-check {
297
- background: var(--olive);
298
- border-color: var(--olive);
299
- }
300
- .ai-row.done .ai-check::after {
301
- content: "";
302
- position: absolute;
303
- left: 4px;
304
- top: 1px;
305
- width: 5px;
306
- height: 9px;
307
- border: solid var(--white);
308
- border-width: 0 2px 2px 0;
309
- transform: rotate(40deg);
310
- }
311
- .ai-row.done .ai-desc {
312
- color: var(--gray-500);
313
- text-decoration: line-through;
314
- text-decoration-color: var(--gray-300);
315
- }
316
- .ai-avatar {
317
- width: 30px;
318
- height: 30px;
319
- border-radius: 50%;
320
- background: var(--oat);
321
- color: var(--gray-700);
322
- font-size: 11px;
323
- font-weight: 600;
324
- display: flex;
325
- align-items: center;
326
- justify-content: center;
327
- letter-spacing: 0.02em;
328
- }
329
- .ai-desc {
330
- font-size: 14px;
331
- color: var(--slate);
332
- }
333
- .ai-due {
334
- font-family: var(--mono);
335
- font-size: 12px;
336
- color: var(--gray-500);
337
- text-align: right;
338
- }
339
-
340
- /* ---------- Fixed TOC ---------- */
341
- .toc {
342
- display: none;
343
- }
344
- @media (min-width: 1100px) {
345
- .toc {
346
- display: block;
347
- position: fixed;
348
- top: 72px;
349
- right: max(24px, calc(50vw - 410px - 200px));
350
- width: 170px;
351
- font-size: 13px;
352
- }
353
- .toc-title {
354
- font-family: var(--mono);
355
- font-size: 10px;
356
- text-transform: uppercase;
357
- letter-spacing: 0.1em;
358
- color: var(--gray-500);
359
- margin-bottom: 12px;
360
- }
361
- .toc a {
362
- display: block;
363
- color: var(--gray-500);
364
- text-decoration: none;
365
- padding: 5px 0 5px 12px;
366
- border-left: 1.5px solid var(--gray-300);
367
- line-height: 1.3;
368
- }
369
- .toc a:hover {
370
- color: var(--slate);
371
- border-left-color: var(--clay);
372
- }
373
- }
374
-
375
- footer {
376
- margin-top: 64px;
377
- padding-top: 20px;
378
- border-top: 1px solid var(--gray-300);
379
- font-family: var(--mono);
380
- font-size: 12px;
381
- color: var(--gray-500);
382
- }
383
- </style>
384
- </head>
385
- <body>
386
-
387
- <nav class="toc">
388
- <div class="toc-title">On this page</div>
389
- <a href="#tldr">TL;DR</a>
390
- <a href="#timeline">Timeline</a>
391
- <a href="#root-cause">Root cause</a>
392
- <a href="#impact">Impact</a>
393
- <a href="#actions">Action items</a>
394
- </nav>
395
-
396
- <div class="page">
397
-
398
- <header>
399
- <div class="inc-id">INC-2025-0412</div>
400
- <h1>Elevated 502s on task sync</h1>
401
- <div class="meta-row">
402
- <span class="pill sev">SEV-2</span>
403
- <span class="pill resolved">Resolved</span>
404
- <span class="pill neutral"><span class="k">Duration</span> <span class="v">47 min</span></span>
405
- <span class="pill neutral"><span class="k">Detected</span> <span class="v">Apr 12 &middot; 14:07</span></span>
406
- <span class="pill neutral"><span class="k">Owner</span> <span class="v">Devon Park</span></span>
407
- </div>
408
- </header>
409
-
410
- <div class="tldr" id="tldr">
411
- <div class="tldr-label">TL;DR</div>
412
- <p>
413
- A config rollout lowered the database connection-pool limit on the
414
- <code>sync-worker</code> tier from 64 to 8, exhausting connections under
415
- normal afternoon load. The sync API returned 502s for roughly 21% of
416
- requests over 47 minutes. We mitigated by reverting the config and
417
- cycling the worker fleet; no data was lost.
418
- </p>
419
- </div>
420
-
421
- <!-- Timeline -->
422
- <section id="timeline">
423
- <h2>Timeline</h2>
424
- <hr class="rule">
425
- <div class="timeline">
426
-
427
- <div class="tl-entry">
428
- <span class="tl-dot"></span>
429
- <span class="tl-time">14:02</span>
430
- <div class="tl-body">
431
- Config change <code class="inline">cfg-9a12</code> promoted to
432
- production via the standard rollout pipeline.
433
- </div>
434
- </div>
435
-
436
- <div class="tl-entry">
437
- <span class="tl-dot impact"></span>
438
- <span class="tl-time">14:06</span>
439
- <div class="tl-body">
440
- <strong>Impact starts.</strong> Sync workers begin queueing on pool
441
- checkout; p95 latency climbs past 4s and the load balancer starts
442
- returning 502s.
443
- </div>
444
- </div>
445
-
446
- <div class="tl-entry">
447
- <span class="tl-dot"></span>
448
- <span class="tl-time">14:07</span>
449
- <div class="tl-body">
450
- Alert fires: <code class="inline">sync_5xx_rate &gt; 2%</code> for 60s.
451
- On-call (Devon) acknowledges.
452
- </div>
453
- </div>
454
-
455
- <div class="tl-entry">
456
- <span class="tl-dot"></span>
457
- <span class="tl-time">14:18</span>
458
- <div class="tl-body">
459
- Initial hypothesis is a bad deploy of the API service; last two
460
- application deploys are rolled back with no effect.
461
- </div>
462
- </div>
463
-
464
- <div class="tl-entry">
465
- <span class="tl-dot"></span>
466
- <span class="tl-time">14:31</span>
467
- <div class="tl-body">
468
- Mira joins and notices pool-wait saturation in the worker dashboard.
469
- Investigation pivots to infra config rather than app code.
470
- </div>
471
- </div>
472
-
473
- <div class="tl-entry">
474
- <span class="tl-dot mitigated"></span>
475
- <span class="tl-time">14:44</span>
476
- <div class="tl-body">
477
- <strong>Mitigated.</strong> <code class="inline">cfg-9a12</code>
478
- reverted; worker fleet cycled. 5xx rate drops below 0.2% within
479
- three minutes.
480
- </div>
481
- </div>
482
-
483
- <div class="tl-entry">
484
- <span class="tl-dot"></span>
485
- <span class="tl-time">14:49</span>
486
- <div class="tl-body">
487
- Monitors green for 5 minutes. Incident declared resolved; status
488
- page updated.
489
- </div>
490
- </div>
491
-
492
- </div>
493
- </section>
494
-
495
- <!-- Root cause -->
496
- <section id="root-cause">
497
- <h2>Root cause</h2>
498
- <hr class="rule">
499
- <p>
500
- PR <code class="inline">#4888</code> made connection-pool limits
501
- configurable per worker tier. The default for the new
502
- <code class="inline">sync-worker</code> key was meant to inherit the
503
- global value (64) but was hard-coded to 8 during a local test and
504
- committed. The config linter only validates type, not magnitude, so the
505
- change passed CI.
506
- </p>
507
- <p>
508
- Because config rollouts and code deploys go through separate pipelines,
509
- the on-call's first instinct &mdash; rolling back the most recent
510
- application deploys &mdash; had no effect and cost roughly 13 minutes of
511
- diagnosis time.
512
- </p>
513
-
514
- <div class="code-panel">
515
- <span class="path">infra/config/workers.yaml</span>
516
- <div class="diff-line ctx"> pool:</div>
517
- <div class="diff-line ctx"> global_max_connections: 64</div>
518
- <div class="diff-line ctx"> tiers:</div>
519
- <div class="diff-line del">- sync-worker: { max_connections: 64 }</div>
520
- <div class="diff-line add">+ sync-worker: { max_connections: 8 } # debug value, do not ship</div>
521
- <div class="diff-line ctx"> webhook-worker: { max_connections: 32 }</div>
522
- </div>
523
- </section>
524
-
525
- <!-- Impact -->
526
- <section id="impact">
527
- <h2>Impact</h2>
528
- <hr class="rule">
529
- <table class="impact">
530
- <tr>
531
- <th>Requests failed (502)</th>
532
- <td>~41,200</td>
533
- </tr>
534
- <tr>
535
- <th>Peak error rate</th>
536
- <td>21.4%</td>
537
- </tr>
538
- <tr>
539
- <th>Users affected</th>
540
- <td>~2,300 workspaces</td>
541
- </tr>
542
- <tr>
543
- <th>Data loss</th>
544
- <td>None &mdash; clients retried</td>
545
- </tr>
546
- <tr>
547
- <th>SLA breach</th>
548
- <td>No (within monthly budget)</td>
549
- </tr>
550
- </table>
551
- </section>
552
-
553
- <!-- Action items -->
554
- <section id="actions">
555
- <h2>Action items</h2>
556
- <hr class="rule">
557
- <div class="actions">
558
-
559
- <div class="ai-row done">
560
- <span class="ai-check"></span>
561
- <span class="ai-avatar" title="Devon Park">DP</span>
562
- <span class="ai-desc">Revert cfg-9a12 and restore pool limit to 64</span>
563
- <span class="ai-due">Apr 12</span>
564
- </div>
565
-
566
- <div class="ai-row">
567
- <span class="ai-check"></span>
568
- <span class="ai-avatar" title="Mira Okafor">MO</span>
569
- <span class="ai-desc">Add config-linter range check for <code class="inline">max_connections</code> (warn &lt; 32)</span>
570
- <span class="ai-due">Apr 18</span>
571
- </div>
572
-
573
- <div class="ai-row">
574
- <span class="ai-check"></span>
575
- <span class="ai-avatar" title="Sam Reyes">SR</span>
576
- <span class="ai-desc">Surface &ldquo;recent config rollouts&rdquo; alongside deploys in the on-call dashboard</span>
577
- <span class="ai-due">Apr 25</span>
578
- </div>
579
-
580
- <div class="ai-row">
581
- <span class="ai-check"></span>
582
- <span class="ai-avatar" title="Priya Anand">PA</span>
583
- <span class="ai-desc">Canary config changes to one worker AZ for 10 min before fleet-wide promote</span>
584
- <span class="ai-due">May 02</span>
585
- </div>
586
-
587
- </div>
588
- </section>
589
-
590
- <footer>
591
- Authored from on-call notes + alert history &middot; reviewed by Devon Park, Mira Okafor
592
- </footer>
593
-
594
- </div>
595
- </body>
596
- </html>