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,492 +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>Background jobs — header illustrations</title>
7
- <style>
8
- :root {
9
- --ivory: #FAF9F5;
10
- --slate: #141413;
11
- --clay: #D97757;
12
- --oat: #E3DACC;
13
- --olive: #788C5D;
14
- --gray-150:#F0EEE6;
15
- --gray-300:#D1CFC5;
16
- --gray-500:#87867F;
17
- --gray-700:#3D3D3A;
18
-
19
- --serif: ui-serif, Georgia, "Times New Roman", serif;
20
- --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
21
- --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
22
- }
23
-
24
- * { margin: 0; padding: 0; box-sizing: border-box; }
25
-
26
- body {
27
- background: var(--ivory);
28
- color: var(--slate);
29
- font-family: var(--sans);
30
- -webkit-font-smoothing: antialiased;
31
- padding: 64px 24px 96px;
32
- }
33
-
34
- .sheet {
35
- max-width: 760px;
36
- margin: 0 auto;
37
- }
38
-
39
- header { margin-bottom: 56px; }
40
-
41
- h1 {
42
- font-family: var(--serif);
43
- font-weight: 500;
44
- font-size: 34px;
45
- letter-spacing: -0.01em;
46
- margin-bottom: 10px;
47
- }
48
-
49
- .lead {
50
- font-size: 14px;
51
- line-height: 1.6;
52
- color: var(--gray-700);
53
- max-width: 560px;
54
- }
55
-
56
- .meta {
57
- font-family: var(--mono);
58
- font-size: 11px;
59
- color: var(--gray-500);
60
- margin-top: 18px;
61
- }
62
-
63
- /* ---------------------------------------------------------------- */
64
-
65
- figure {
66
- margin: 0 0 72px 0;
67
- }
68
-
69
- .canvas {
70
- border: 1px solid var(--gray-300);
71
- border-radius: 12px;
72
- overflow: hidden;
73
- background: var(--ivory);
74
- }
75
-
76
- .canvas svg { display: block; width: 100%; height: auto; }
77
-
78
- figcaption {
79
- display: flex;
80
- align-items: baseline;
81
- justify-content: space-between;
82
- gap: 24px;
83
- padding: 18px 4px 0;
84
- }
85
-
86
- .cap-text { flex: 1; }
87
-
88
- .cap-title {
89
- font-family: var(--serif);
90
- font-size: 19px;
91
- font-weight: 500;
92
- margin-bottom: 4px;
93
- }
94
-
95
- .cap-sub {
96
- font-size: 13px;
97
- line-height: 1.55;
98
- color: var(--gray-500);
99
- }
100
-
101
- .dl {
102
- flex-shrink: 0;
103
- font-family: var(--mono);
104
- font-size: 11px;
105
- color: var(--slate);
106
- background: var(--gray-150);
107
- border: 1px solid var(--gray-300);
108
- border-radius: 6px;
109
- padding: 7px 12px;
110
- cursor: pointer;
111
- }
112
- .dl:hover { background: var(--oat); }
113
- .dl:active { transform: translateY(1px); }
114
-
115
- /* ----------------------------------------------------------------
116
- Palette reference strip
117
- ---------------------------------------------------------------- */
118
- .palette {
119
- border-top: 1px solid var(--gray-300);
120
- padding-top: 36px;
121
- margin-top: 8px;
122
- }
123
-
124
- .palette h2 {
125
- font-family: var(--serif);
126
- font-weight: 500;
127
- font-size: 19px;
128
- margin-bottom: 18px;
129
- }
130
-
131
- .swatches {
132
- display: grid;
133
- grid-template-columns: repeat(auto-fill, minmax(112px, 1fr));
134
- gap: 10px;
135
- margin-bottom: 32px;
136
- }
137
-
138
- .swatch {
139
- border: 1px solid var(--gray-300);
140
- border-radius: 8px;
141
- overflow: hidden;
142
- background: #fff;
143
- }
144
-
145
- .swatch-chip {
146
- height: 44px;
147
- }
148
-
149
- .swatch-meta {
150
- padding: 8px 10px 10px;
151
- }
152
-
153
- .swatch-name {
154
- font-size: 12px;
155
- color: var(--gray-700);
156
- }
157
-
158
- .swatch-hex {
159
- font-family: var(--mono);
160
- font-size: 10px;
161
- color: var(--gray-500);
162
- }
163
-
164
- .notes {
165
- font-size: 13px;
166
- line-height: 1.7;
167
- color: var(--gray-700);
168
- list-style: none;
169
- }
170
-
171
- .notes li {
172
- padding-left: 18px;
173
- position: relative;
174
- margin-bottom: 6px;
175
- }
176
-
177
- .notes li::before {
178
- content: "";
179
- position: absolute;
180
- left: 0;
181
- top: 0.65em;
182
- width: 8px;
183
- height: 1px;
184
- background: var(--gray-500);
185
- }
186
- </style>
187
- </head>
188
- <body>
189
- <div class="sheet">
190
-
191
- <header>
192
- <h1>Background jobs — header illustrations</h1>
193
- <p class="lead">
194
- Three 720×320 hand-drawn SVGs for the Birchline docs section on
195
- background jobs. Flat fills, 1.5–2px strokes, palette-locked. Each
196
- exports standalone via the button below it.
197
- </p>
198
- <div class="meta">720 × 320 · inline SVG · no external assets</div>
199
- </header>
200
-
201
- <!-- ============================================================
202
- A — Queue
203
- ============================================================ -->
204
- <figure>
205
- <div class="canvas">
206
- <svg id="ill-queue" xmlns="http://www.w3.org/2000/svg" width="720" height="320" viewBox="0 0 720 320">
207
- <defs>
208
- <style>
209
- .m { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 11px; }
210
- .s { font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; font-size: 12px; }
211
- </style>
212
- <marker id="qa" markerWidth="10" markerHeight="10" refX="8" refY="4" orient="auto" markerUnits="userSpaceOnUse">
213
- <path d="M0,0 L0,8 L8,4 z" fill="#87867F"/>
214
- </marker>
215
- </defs>
216
-
217
- <rect width="720" height="320" fill="#FAF9F5"/>
218
-
219
- <!-- queue label -->
220
- <text x="60" y="96" class="s" fill="#87867F">queue</text>
221
-
222
- <!-- five job rects -->
223
- <g>
224
- <rect x="60" y="110" width="70" height="100" rx="10" fill="#F0EEE6" stroke="#3D3D3A" stroke-width="1.5"/>
225
- <text x="95" y="164" text-anchor="middle" class="m" fill="#3D3D3A">job 5</text>
226
-
227
- <rect x="140" y="110" width="70" height="100" rx="10" fill="#F0EEE6" stroke="#3D3D3A" stroke-width="1.5"/>
228
- <text x="175" y="164" text-anchor="middle" class="m" fill="#3D3D3A">job 4</text>
229
-
230
- <rect x="220" y="110" width="70" height="100" rx="10" fill="#F0EEE6" stroke="#3D3D3A" stroke-width="1.5"/>
231
- <text x="255" y="164" text-anchor="middle" class="m" fill="#3D3D3A">job 3</text>
232
-
233
- <rect x="300" y="110" width="70" height="100" rx="10" fill="#F0EEE6" stroke="#3D3D3A" stroke-width="1.5"/>
234
- <text x="335" y="164" text-anchor="middle" class="m" fill="#3D3D3A">job 2</text>
235
-
236
- <!-- head of queue, highlighted -->
237
- <rect x="380" y="110" width="70" height="100" rx="10" fill="#D97757" stroke="#141413" stroke-width="2"/>
238
- <text x="415" y="164" text-anchor="middle" class="m" fill="#FAF9F5">job 1</text>
239
- </g>
240
-
241
- <!-- arrow to worker -->
242
- <line x1="458" y1="160" x2="522" y2="160" stroke="#87867F" stroke-width="1.5" marker-end="url(#qa)"/>
243
-
244
- <!-- worker -->
245
- <rect x="530" y="90" width="130" height="140" rx="10" fill="#E3DACC" stroke="#141413" stroke-width="2"/>
246
- <text x="595" y="156" text-anchor="middle" class="m" fill="#141413">worker</text>
247
- <text x="595" y="172" text-anchor="middle" class="m" fill="#87867F">pool=4</text>
248
-
249
- <!-- annotations -->
250
- <text x="415" y="234" text-anchor="middle" class="s" fill="#87867F">next to run</text>
251
- <text x="60" y="258" class="s" fill="#87867F">Jobs are pulled FIFO; the worker leases one at a time.</text>
252
- </svg>
253
- </div>
254
- <figcaption>
255
- <div class="cap-text">
256
- <div class="cap-title">Queue</div>
257
- <div class="cap-sub">For "How jobs are picked up" — intro page header.</div>
258
- </div>
259
- <button class="dl" data-target="ill-queue" data-filename="birchline-jobs-queue.svg">Download SVG</button>
260
- </figcaption>
261
- </figure>
262
-
263
- <!-- ============================================================
264
- B — Retry with backoff
265
- ============================================================ -->
266
- <figure>
267
- <div class="canvas">
268
- <svg id="ill-retry" xmlns="http://www.w3.org/2000/svg" width="720" height="320" viewBox="0 0 720 320">
269
- <defs>
270
- <style>
271
- .m { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 11px; }
272
- .s { font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; font-size: 12px; }
273
- </style>
274
- </defs>
275
-
276
- <rect width="720" height="320" fill="#FAF9F5"/>
277
-
278
- <!-- timeline axis -->
279
- <line x1="60" y1="190" x2="660" y2="190" stroke="#D1CFC5" stroke-width="1.5"/>
280
- <text x="60" y="214" class="s" fill="#87867F">t = 0</text>
281
- <text x="660" y="214" text-anchor="end" class="s" fill="#87867F">time →</text>
282
-
283
- <!-- attempts: exponential spacing at x = 100, 170, 310, 590 -->
284
- <!-- attempt 1 (fail) -->
285
- <circle cx="100" cy="190" r="8" fill="#FAF9F5" stroke="#D97757" stroke-width="2"/>
286
- <line x1="96" y1="186" x2="104" y2="194" stroke="#D97757" stroke-width="1.5"/>
287
- <line x1="104" y1="186" x2="96" y2="194" stroke="#D97757" stroke-width="1.5"/>
288
- <text x="100" y="222" text-anchor="middle" class="m" fill="#3D3D3A">try 1</text>
289
-
290
- <!-- attempt 2 (fail) -->
291
- <circle cx="170" cy="190" r="8" fill="#FAF9F5" stroke="#D97757" stroke-width="2"/>
292
- <line x1="166" y1="186" x2="174" y2="194" stroke="#D97757" stroke-width="1.5"/>
293
- <line x1="174" y1="186" x2="166" y2="194" stroke="#D97757" stroke-width="1.5"/>
294
- <text x="170" y="222" text-anchor="middle" class="m" fill="#3D3D3A">try 2</text>
295
-
296
- <!-- attempt 3 (fail) -->
297
- <circle cx="310" cy="190" r="8" fill="#FAF9F5" stroke="#D97757" stroke-width="2"/>
298
- <line x1="306" y1="186" x2="314" y2="194" stroke="#D97757" stroke-width="1.5"/>
299
- <line x1="314" y1="186" x2="306" y2="194" stroke="#D97757" stroke-width="1.5"/>
300
- <text x="310" y="222" text-anchor="middle" class="m" fill="#3D3D3A">try 3</text>
301
-
302
- <!-- attempt 4 (success) -->
303
- <circle cx="590" cy="190" r="9" fill="#788C5D" stroke="#141413" stroke-width="1.5"/>
304
- <path d="M585,190 L589,194 L596,185" fill="none" stroke="#FAF9F5" stroke-width="1.5" stroke-linecap="round"/>
305
- <text x="590" y="222" text-anchor="middle" class="m" fill="#3D3D3A">try 4</text>
306
-
307
- <!-- dashed backoff arcs (loop back over the gap) -->
308
- <path d="M 100 182 Q 135 130 170 182" fill="none" stroke="#87867F" stroke-width="1.5" stroke-dasharray="4 4"/>
309
- <text x="135" y="124" text-anchor="middle" class="m" fill="#87867F">+1s</text>
310
-
311
- <path d="M 170 182 Q 240 110 310 182" fill="none" stroke="#87867F" stroke-width="1.5" stroke-dasharray="4 4"/>
312
- <text x="240" y="104" text-anchor="middle" class="m" fill="#87867F">+2s</text>
313
-
314
- <path d="M 310 182 Q 450 80 590 182" fill="none" stroke="#87867F" stroke-width="1.5" stroke-dasharray="4 4"/>
315
- <text x="450" y="74" text-anchor="middle" class="m" fill="#87867F">+4s</text>
316
-
317
- <!-- annotation -->
318
- <text x="60" y="262" class="s" fill="#87867F">Each failure waits twice as long before re-queuing; jitter not pictured.</text>
319
- </svg>
320
- </div>
321
- <figcaption>
322
- <div class="cap-text">
323
- <div class="cap-title">Retry with backoff</div>
324
- <div class="cap-sub">For "Handling failures" — retry policy header.</div>
325
- </div>
326
- <button class="dl" data-target="ill-retry" data-filename="birchline-jobs-retry.svg">Download SVG</button>
327
- </figcaption>
328
- </figure>
329
-
330
- <!-- ============================================================
331
- C — Fan-out
332
- ============================================================ -->
333
- <figure>
334
- <div class="canvas">
335
- <svg id="ill-fanout" xmlns="http://www.w3.org/2000/svg" width="720" height="320" viewBox="0 0 720 320">
336
- <defs>
337
- <style>
338
- .m { font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace; font-size: 11px; }
339
- .s { font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; font-size: 12px; }
340
- </style>
341
- <marker id="fa" markerWidth="9" markerHeight="9" refX="7" refY="3.5" orient="auto" markerUnits="userSpaceOnUse">
342
- <path d="M0,0 L0,7 L7,3.5 z" fill="#87867F"/>
343
- </marker>
344
- </defs>
345
-
346
- <rect width="720" height="320" fill="#FAF9F5"/>
347
-
348
- <!-- source -->
349
- <rect x="60" y="128" width="110" height="64" rx="10" fill="#E3DACC" stroke="#141413" stroke-width="2"/>
350
- <text x="115" y="158" text-anchor="middle" class="m" fill="#141413">enqueue</text>
351
- <text x="115" y="172" text-anchor="middle" class="m" fill="#87867F">batch()</text>
352
-
353
- <!-- fan-out arrows: from (170,160) to four shard boxes at x=300 -->
354
- <line x1="170" y1="160" x2="296" y2="64" stroke="#87867F" stroke-width="1.5" marker-end="url(#fa)"/>
355
- <line x1="170" y1="160" x2="296" y2="128" stroke="#87867F" stroke-width="1.5" marker-end="url(#fa)"/>
356
- <line x1="170" y1="160" x2="296" y2="192" stroke="#87867F" stroke-width="1.5" marker-end="url(#fa)"/>
357
- <line x1="170" y1="160" x2="296" y2="256" stroke="#87867F" stroke-width="1.5" marker-end="url(#fa)"/>
358
-
359
- <!-- four shard boxes -->
360
- <g>
361
- <rect x="300" y="44" width="120" height="40" rx="10" fill="#F0EEE6" stroke="#3D3D3A" stroke-width="1.5"/>
362
- <text x="360" y="69" text-anchor="middle" class="m" fill="#3D3D3A">shard 0</text>
363
-
364
- <rect x="300" y="108" width="120" height="40" rx="10" fill="#F0EEE6" stroke="#3D3D3A" stroke-width="1.5"/>
365
- <text x="360" y="133" text-anchor="middle" class="m" fill="#3D3D3A">shard 1</text>
366
-
367
- <rect x="300" y="172" width="120" height="40" rx="10" fill="#F0EEE6" stroke="#3D3D3A" stroke-width="1.5"/>
368
- <text x="360" y="197" text-anchor="middle" class="m" fill="#3D3D3A">shard 2</text>
369
-
370
- <rect x="300" y="236" width="120" height="40" rx="10" fill="#F0EEE6" stroke="#3D3D3A" stroke-width="1.5"/>
371
- <text x="360" y="261" text-anchor="middle" class="m" fill="#3D3D3A">shard 3</text>
372
- </g>
373
-
374
- <!-- converge arrows to merge -->
375
- <line x1="420" y1="64" x2="546" y2="160" stroke="#87867F" stroke-width="1.5" marker-end="url(#fa)"/>
376
- <line x1="420" y1="128" x2="546" y2="160" stroke="#87867F" stroke-width="1.5" marker-end="url(#fa)"/>
377
- <line x1="420" y1="192" x2="546" y2="160" stroke="#87867F" stroke-width="1.5" marker-end="url(#fa)"/>
378
- <line x1="420" y1="256" x2="546" y2="160" stroke="#87867F" stroke-width="1.5" marker-end="url(#fa)"/>
379
-
380
- <!-- merge box -->
381
- <rect x="550" y="128" width="110" height="64" rx="10" fill="#788C5D" stroke="#141413" stroke-width="2"/>
382
- <text x="605" y="158" text-anchor="middle" class="m" fill="#FAF9F5">merge</text>
383
- <text x="605" y="172" text-anchor="middle" class="m" fill="#E3DACC">await all</text>
384
-
385
- <!-- annotations -->
386
- <text x="232" y="42" class="s" fill="#87867F">fan-out</text>
387
- <text x="486" y="42" text-anchor="end" class="s" fill="#87867F">fan-in</text>
388
- <text x="60" y="300" class="s" fill="#87867F">Parent job spawns N children, then blocks on a completion barrier.</text>
389
- </svg>
390
- </div>
391
- <figcaption>
392
- <div class="cap-text">
393
- <div class="cap-title">Fan-out / fan-in</div>
394
- <div class="cap-sub">For "Batch and parallel work" — fan-out pattern header.</div>
395
- </div>
396
- <button class="dl" data-target="ill-fanout" data-filename="birchline-jobs-fanout.svg">Download SVG</button>
397
- </figcaption>
398
- </figure>
399
-
400
- <!-- ============================================================
401
- Palette + usage notes
402
- ============================================================ -->
403
- <section class="palette">
404
- <h2>Palette &amp; rules</h2>
405
-
406
- <div class="swatches">
407
- <div class="swatch">
408
- <div class="swatch-chip" style="background:#FAF9F5"></div>
409
- <div class="swatch-meta">
410
- <div class="swatch-name">ivory</div>
411
- <div class="swatch-hex">#FAF9F5</div>
412
- </div>
413
- </div>
414
- <div class="swatch">
415
- <div class="swatch-chip" style="background:#141413"></div>
416
- <div class="swatch-meta">
417
- <div class="swatch-name">slate</div>
418
- <div class="swatch-hex">#141413</div>
419
- </div>
420
- </div>
421
- <div class="swatch">
422
- <div class="swatch-chip" style="background:#D97757"></div>
423
- <div class="swatch-meta">
424
- <div class="swatch-name">clay</div>
425
- <div class="swatch-hex">#D97757</div>
426
- </div>
427
- </div>
428
- <div class="swatch">
429
- <div class="swatch-chip" style="background:#788C5D"></div>
430
- <div class="swatch-meta">
431
- <div class="swatch-name">olive</div>
432
- <div class="swatch-hex">#788C5D</div>
433
- </div>
434
- </div>
435
- <div class="swatch">
436
- <div class="swatch-chip" style="background:#E3DACC"></div>
437
- <div class="swatch-meta">
438
- <div class="swatch-name">oat</div>
439
- <div class="swatch-hex">#E3DACC</div>
440
- </div>
441
- </div>
442
- <div class="swatch">
443
- <div class="swatch-chip" style="background:#F0EEE6"></div>
444
- <div class="swatch-meta">
445
- <div class="swatch-name">gray-150</div>
446
- <div class="swatch-hex">#F0EEE6</div>
447
- </div>
448
- </div>
449
- <div class="swatch">
450
- <div class="swatch-chip" style="background:#D1CFC5"></div>
451
- <div class="swatch-meta">
452
- <div class="swatch-name">gray-300</div>
453
- <div class="swatch-hex">#D1CFC5</div>
454
- </div>
455
- </div>
456
- <div class="swatch">
457
- <div class="swatch-chip" style="background:#87867F"></div>
458
- <div class="swatch-meta">
459
- <div class="swatch-name">gray-500</div>
460
- <div class="swatch-hex">#87867F</div>
461
- </div>
462
- </div>
463
- </div>
464
-
465
- <ul class="notes">
466
- <li>Strokes are 1.5px for neutral boxes, 2px for emphasised containers.</li>
467
- <li>All rectangles use <code>rx="10"</code>; no drop shadows or gradients.</li>
468
- <li>Labels inside boxes are 11px mono; annotations outside are 12px sans, gray-500.</li>
469
- <li>Clay marks "the thing in focus"; olive marks "success / done".</li>
470
- <li>Each SVG carries its own <code>&lt;style&gt;</code> block so the download stands alone.</li>
471
- </ul>
472
- </section>
473
-
474
- </div>
475
-
476
- <script>
477
- document.querySelectorAll('.dl').forEach(function (btn) {
478
- btn.addEventListener('click', function () {
479
- var svg = document.getElementById(btn.dataset.target);
480
- var src = new XMLSerializer().serializeToString(svg);
481
- var blob = new Blob([src], { type: 'image/svg+xml;charset=utf-8' });
482
- var url = URL.createObjectURL(blob);
483
- var a = document.createElement('a');
484
- a.href = url;
485
- a.download = btn.dataset.filename || 'illustration.svg';
486
- a.click();
487
- setTimeout(function () { URL.revokeObjectURL(url); }, 1000);
488
- });
489
- });
490
- </script>
491
- </body>
492
- </html>