konpeki 0.3.0 → 0.4.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 (162) hide show
  1. package/AGENTS.md +21 -28
  2. package/AUTHORING.md +68 -229
  3. package/CONTRIBUTING.md +10 -23
  4. package/README.md +96 -87
  5. package/SETUP.md +50 -114
  6. package/docs/development.md +43 -248
  7. package/docs/workflow.md +6 -108
  8. package/html/README.md +140 -0
  9. package/html/browser.ts +103 -0
  10. package/html/document.ts +38 -0
  11. package/html/floor.ts +34 -0
  12. package/html/index.html +5 -0
  13. package/html/inspect.ts +313 -0
  14. package/html/preview.css +62 -0
  15. package/html/preview.tsx +463 -0
  16. package/html/review-hints.ts +63 -0
  17. package/html/server.ts +99 -0
  18. package/html/source.ts +104 -0
  19. package/html/starter.ts +6 -0
  20. package/html/theme-authoring.md +176 -0
  21. package/html/theme.md +61 -0
  22. package/index.html +9 -9
  23. package/package.json +21 -35
  24. package/plugin.json +1 -1
  25. package/public/og.png +0 -0
  26. package/runtime/browser-B-29TH1a.mjs +595 -0
  27. package/runtime/floor-Cmk7G3pU.mjs +41 -0
  28. package/runtime/konpeki.mjs +90 -1410
  29. package/runtime/server-BAqC_5WD.mjs +2 -0
  30. package/runtime/server-DfRpfcY9.mjs +205 -0
  31. package/runtime/source-B_Ui9tMp.mjs +141 -0
  32. package/runtime/source-CZQq9GUO.mjs +2 -0
  33. package/skills/konpeki/SKILL.md +88 -172
  34. package/skills/konpeki/assets/blank.html +17 -0
  35. package/skills/konpeki/floor.md +76 -0
  36. package/skills/konpeki/references/cover.md +27 -0
  37. package/skills/konpeki/references/long-document.md +40 -0
  38. package/skills/konpeki/references/one-pager.md +27 -0
  39. package/skills/konpeki/references/patterns.md +118 -0
  40. package/skills/konpeki/references/resume.md +28 -0
  41. package/skills/konpeki/references/slides.md +28 -0
  42. package/skills/konpeki/scripts/ensure-runtime.mjs +10 -31
  43. package/skills/konpeki/scripts/prepare-document.mjs +25 -14
  44. package/src/components/PageBoard.tsx +156 -0
  45. package/src/lib/alignment.ts +21 -0
  46. package/src/lib/page-board.ts +25 -0
  47. package/src/lib/review-position.ts +19 -0
  48. package/src/styles/base.css +4 -6
  49. package/src/styles/feedback.css +97 -61
  50. package/src/styles/shell.css +95 -323
  51. package/theme-base.css +90 -0
  52. package/theme.css +56 -0
  53. package/vite.config.ts +2 -5
  54. package/composition/README.md +0 -156
  55. package/composition/compile.ts +0 -227
  56. package/composition/document.ts +0 -600
  57. package/composition/schema.json +0 -3001
  58. package/composition/schema.ts +0 -437
  59. package/composition/theme-tokens.ts +0 -16
  60. package/composition/types.ts +0 -269
  61. package/composition/validate.ts +0 -226
  62. package/composition/vector.ts +0 -143
  63. package/composition/visualizations.ts +0 -319
  64. package/design/README.md +0 -17
  65. package/design/palettes/README.md +0 -14
  66. package/design/palettes/base.ts +0 -14
  67. package/design/palettes/candidates.ts +0 -19
  68. package/design/palettes/index.ts +0 -78
  69. package/design/review/color-theme.md +0 -44
  70. package/design/review/layout.md +0 -16
  71. package/design/review/text.md +0 -18
  72. package/design/review/typography.md +0 -15
  73. package/design/review/visuals.md +0 -31
  74. package/design/semantic-patterns.md +0 -43
  75. package/design/themes/README.md +0 -40
  76. package/design/themes/index.ts +0 -24
  77. package/design/visual-languages/technical-product.md +0 -17
  78. package/design/visual-review.md +0 -88
  79. package/lib/assets.d.ts +0 -8
  80. package/lib/charts.ts +0 -18
  81. package/lib/contrast.ts +0 -16
  82. package/lib/layouts.ts +0 -50
  83. package/lib/slide.tsx +0 -42
  84. package/lib/taste.ts +0 -17
  85. package/lib/text.tsx +0 -89
  86. package/lib/typeface.ts +0 -44
  87. package/scripts/migrate-react-page.ts +0 -120
  88. package/skills/konpeki/assets/blank.json +0 -23
  89. package/slides/README.md +0 -153
  90. package/slides/architecture/PROMPT.md +0 -31
  91. package/slides/architecture/index.tsx +0 -102
  92. package/slides/article-brief/PROMPT.md +0 -35
  93. package/slides/article-brief/index.tsx +0 -71
  94. package/slides/bar-chart/PROMPT.md +0 -39
  95. package/slides/bar-chart/index.tsx +0 -97
  96. package/slides/comparison/PROMPT.md +0 -29
  97. package/slides/comparison/index.tsx +0 -95
  98. package/slides/decision-memo/PROMPT.md +0 -34
  99. package/slides/decision-memo/index.tsx +0 -85
  100. package/slides/delivery-plan/PROMPT.md +0 -45
  101. package/slides/delivery-plan/index.tsx +0 -105
  102. package/slides/experiment/PROMPT.md +0 -44
  103. package/slides/experiment/index.tsx +0 -127
  104. package/slides/incident-workflow/PROMPT.md +0 -57
  105. package/slides/incident-workflow/index.tsx +0 -78
  106. package/slides/introducing-konpeki/PROMPT.md +0 -40
  107. package/slides/introducing-konpeki/README.md +0 -76
  108. package/slides/introducing-konpeki/SOURCE.md +0 -26
  109. package/slides/introducing-konpeki/author.ts +0 -165
  110. package/slides/introducing-konpeki/composition.json +0 -3270
  111. package/slides/line-chart/PROMPT.md +0 -40
  112. package/slides/line-chart/index.tsx +0 -72
  113. package/slides/migration/PROMPT.md +0 -38
  114. package/slides/migration/index.tsx +0 -89
  115. package/slides/og-images/PROMPT.md +0 -21
  116. package/slides/og-images/index.tsx +0 -76
  117. package/slides/product-introduction/PROMPT.md +0 -24
  118. package/slides/product-introduction/index.tsx +0 -105
  119. package/slides/research-brief/PROMPT.md +0 -40
  120. package/slides/research-brief/index.tsx +0 -104
  121. package/slides/results-explanation/PROMPT.md +0 -32
  122. package/slides/results-explanation/index.tsx +0 -96
  123. package/slides/retrospective/PROMPT.md +0 -43
  124. package/slides/retrospective/index.tsx +0 -105
  125. package/slides/sankey/PROMPT.md +0 -11
  126. package/slides/sankey/index.tsx +0 -93
  127. package/slides/teaching/PROMPT.md +0 -45
  128. package/slides/teaching/index.tsx +0 -124
  129. package/slides/vertical-bar-charts/PROMPT.md +0 -13
  130. package/slides/vertical-bar-charts/index.tsx +0 -97
  131. package/src/app/App.tsx +0 -820
  132. package/src/components/BuildOrb.tsx +0 -40
  133. package/src/components/Canvas.tsx +0 -1185
  134. package/src/components/DiagramTypeIcon.tsx +0 -78
  135. package/src/components/InspectorPanel.tsx +0 -773
  136. package/src/components/LeftPanel.tsx +0 -120
  137. package/src/components/PageSizePicker.tsx +0 -30
  138. package/src/components/Presentation.tsx +0 -105
  139. package/src/components/RevisionNotes.tsx +0 -56
  140. package/src/components/RightPanel.tsx +0 -201
  141. package/src/components/VectorOverflowWarning.tsx +0 -46
  142. package/src/components/WorkspaceChrome.tsx +0 -288
  143. package/src/components/ui.tsx +0 -53
  144. package/src/lib/examples/react-page-migration.json +0 -1295
  145. package/src/lib/examples.ts +0 -42
  146. package/src/lib/export-png.ts +0 -104
  147. package/src/lib/file-session.ts +0 -87
  148. package/src/lib/history.ts +0 -53
  149. package/src/lib/model.ts +0 -188
  150. package/src/lib/page-size.ts +0 -24
  151. package/src/lib/presentation.ts +0 -17
  152. package/src/lib/review.ts +0 -26
  153. package/src/lib/storage.ts +0 -71
  154. package/src/lib/theme.ts +0 -25
  155. package/src/lib/use-file-session.ts +0 -227
  156. package/src/main.tsx +0 -29
  157. package/src/styles/canvas.css +0 -299
  158. package/src/styles/chrome.css +0 -384
  159. package/src/styles/component-previews.css +0 -386
  160. package/src/styles/left-panel.css +0 -166
  161. package/src/styles/presentation.css +0 -72
  162. package/src/styles/right-panel.css +0 -1215
@@ -1,95 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- import '../../lib/typeface.ts';
3
-
4
- const ink = '#172B26';
5
- const muted = '#4F605A';
6
- const accent = '#236B50';
7
- const rule = '#C4CEC9';
8
-
9
- function Copy({ x = 112, y, lines, size = 36, weight = 400, color = ink }: {
10
- x?: number; y: number; lines: string[]; size?: number; weight?: number; color?: string;
11
- }) {
12
- return <text x={x} y={y} fill={color} fontSize={size} fontWeight={weight}>
13
- {lines.map((line, i) => <tspan key={i} x={x} dy={i ? size * 1.35 : 0}>{line}</tspan>)}
14
- </text>;
15
- }
16
-
17
- function Canvas({ title, description, number, children }: {
18
- title: string; description: string; number: number; children: ReactNode;
19
- }) {
20
- return <svg xmlns="http://www.w3.org/2000/svg" width="1920" height="1080" viewBox="0 0 1920 1080"
21
- fontFamily="IBM Plex Sans" role="img" aria-label={`${title}. ${description}`} data-moss-page={number}>
22
- <rect width="1920" height="1080" fill="#FFFFFF" />
23
- <Copy y={154} lines={[title]} size={72} weight={600} />
24
- {children}
25
- <Copy x={1752} y={1022} lines={[`${number} / 3`]} size={28} color={muted} />
26
- </svg>;
27
- }
28
-
29
- const Shortlist = () => <Canvas number={1} title="Moss should trial Postbox first"
30
- description="Fictional scenario. Six agents need email, shared assignment and complete CSV history export. Postbox includes all three for $108 per month before tax, leaving $42 of the $150 subscription budget. Threadline costs $144 and adds chat. OwnDesk requires server operations Moss cannot supply.">
31
- <Copy y={234} lines={['Six people. Email intake, shared assignment and complete CSV history export.']} color={muted} />
32
- <Copy y={332} lines={['All three list the required features. Cost and operating capacity decide the shortlist.']} size={38} weight={500} />
33
- <Copy x={112} y={423} lines={['Option']} size={30} color={muted} />
34
- <Copy x={510} y={423} lines={['Monthly cost · six agents']} size={30} color={muted} />
35
- <Copy x={1038} y={423} lines={['Fit for Moss now']} size={30} color={muted} />
36
- <line x1={112} x2={1808} y1={452} y2={452} stroke={rule} strokeWidth={2} />
37
- <Copy y={512} lines={['Postbox']} size={44} weight={600} color={accent} />
38
- <Copy x={510} y={512} lines={['$108 subscription', '$18 × 6 agents']} size={36} />
39
- <Copy x={1038} y={512} lines={['Hosted · trial first', 'Meets today’s needs; no chat channel.']} size={36} />
40
- <line x1={112} x2={1808} y1={594} y2={594} stroke={rule} strokeWidth={2} />
41
- <Copy y={653} lines={['Threadline']} size={44} weight={600} />
42
- <Copy x={510} y={653} lines={['$144 subscription', '$24 × 6 agents']} size={36} />
43
- <Copy x={1038} y={653} lines={['Hosted · conditional alternative', 'Adds chat, useful later rather than now.']} size={36} />
44
- <line x1={112} x2={1808} y1={735} y2={735} stroke={rule} strokeWidth={2} />
45
- <Copy y={794} lines={['OwnDesk']} size={44} weight={600} />
46
- <Copy x={510} y={794} lines={['$0 software subscription', 'Operating costs unknown']} size={36} />
47
- <Copy x={1038} y={794} lines={['Self-hosted · exclude for now', 'Moss cannot operate its own server.']} size={36} />
48
- <line x1={112} x2={1808} y1={876} y2={876} stroke={rule} strokeWidth={2} />
49
- <Copy y={936} lines={['Fictional, hand-authored specifications—not vendor research. Hosted prices exclude tax;', 'both hosted options have no setup fee. Listed features do not establish quality.']} size={30} color={muted} />
50
- </Canvas>;
51
-
52
- const Tradeoff = () => <Canvas number={2} title="Chat must earn its $36 monthly premium"
53
- description="Postbox is $108 per month with $42 subscription headroom; Threadline is $144 with $6 headroom against a $150 budget, before tax. Trial Threadline if a concrete near-term chat workflow is valuable enough to justify the premium. OwnDesk includes chat but requires hosting, backups, upgrades and incident response with unknown costs.">
54
- <Copy y={239} lines={['Both hosted options fit the $150 subscription budget before tax.']} size={38} color={muted} />
55
- <Copy y={358} lines={['Postbox']} size={42} weight={600} color={accent} />
56
- <Copy y={442} lines={['$108 / month']} size={64} weight={600} />
57
- <Copy y={505} lines={['$42 budget headroom']} size={34} color={muted} />
58
- <Copy x={982} y={358} lines={['Threadline']} size={42} weight={600} />
59
- <Copy x={982} y={442} lines={['$144 / month']} size={64} weight={600} />
60
- <Copy x={982} y={505} lines={['$6 budget headroom']} size={34} color={muted} />
61
- <Copy y={624} lines={['Start with the current workflow']} size={40} weight={600} />
62
- <Copy y={682} lines={['Postbox covers the three required capabilities.', 'Do not pay for chat on a feature list alone.']} size={36} />
63
- <Copy x={982} y={624} lines={['Trial Threadline when chat has a job']} size={40} weight={600} />
64
- <Copy x={982} y={682} lines={['Name a near-term chat workflow and test it.', 'Choose Threadline only if the added value', 'justifies $36/month and the final bill fits.']} size={36} />
65
- <line x1={112} x2={1808} y1={847} y2={847} stroke={rule} strokeWidth={2} />
66
- <Copy y={911} lines={['OwnDesk also lists chat, but Moss would own hosting, backups, upgrades and incident', 'response. With no operating capacity and unknown costs, it is not a zero-cost alternative.']} size={34} />
67
- <Copy y={1014} lines={['Fictional scenario · subscription calculations for six paid agents, before tax.']} size={28} color={muted} />
68
- </Canvas>;
69
-
70
- const Trial = () => <Canvas number={3} title="Use the trial to test the purchase decision"
71
- description="Start Postbox with a representative sample. Verify email intake and shared assignment with all six agents; reconcile a CSV export to ticket history; confirm six-agent pricing and tax. No usability, reliability or migration results are measured. Purchase only after required workflow and export checks pass; compare Threadline if chat becomes a justified need.">
72
- <Copy y={239} lines={['Begin with Postbox and representative tickets, then make a go/no-go decision.']} size={38} color={muted} />
73
- <Copy y={369} lines={['Verify in the trial']} size={32} color={muted} />
74
- <Copy x={788} y={369} lines={['Evidence to collect before purchase']} size={32} color={muted} />
75
- <line x1={112} x2={1808} y1={397} y2={397} stroke={rule} strokeWidth={2} />
76
- <Copy y={463} lines={['Email + assignment']} size={40} weight={600} />
77
- <Copy x={788} y={463} lines={['Send incoming email; assign and reassign tickets.', 'Have all six agents confirm ownership is clear.']} size={36} />
78
- <line x1={112} x2={1808} y1={549} y2={549} stroke={rule} strokeWidth={2} />
79
- <Copy y={613} lines={['Complete CSV history']} size={40} weight={600} />
80
- <Copy x={788} y={613} lines={['Export the sample and reconcile it to ticket history.', 'Check records, fields and chronological completeness.']} size={36} />
81
- <line x1={112} x2={1808} y1={699} y2={699} stroke={rule} strokeWidth={2} />
82
- <Copy y={763} lines={['Final cost + transition']} size={40} weight={600} />
83
- <Copy x={788} y={763} lines={['Confirm the six-agent quote and tax against budget.', 'Test a small migration before committing the inbox.']} size={36} />
84
- <line x1={112} x2={1808} y1={849} y2={849} stroke={rule} strokeWidth={2} />
85
- <Copy y={915} lines={['Buy only after required workflow and export checks pass.']} size={40} weight={600} color={accent} />
86
- <Copy y={974} lines={['No measured usability, reliability or migration results exist in this fictional scenario.', 'Review reliability evidence separately; a short trial cannot establish long-term reliability.']} size={30} color={muted} />
87
- </Canvas>;
88
-
89
- export const meta = { title: 'Moss — choosing a support inbox', createdAt: '2026-09-12T14:20:00Z' };
90
- export const notes = [
91
- 'Recommendation based only on supplied fictional specifications, not observed product performance.',
92
- 'Derived arithmetic: 18 × 6 = 108; 24 × 6 = 144; 144 − 108 = 36; 150 − 108 = 42; 150 − 144 = 6.',
93
- 'Trial checks are proposed decision criteria, not measured results or claims about product behavior.',
94
- ];
95
- export default [Shortlist, Tradeoff, Trial];
@@ -1,34 +0,0 @@
1
- Make a three-page presentation for the operations lead at Cedar Support to help
2
- decide whether to extend a trial of suggested replies. Explain the trade-off
3
- between handling time and quality, what the evidence supports, and what the
4
- team should learn next.
5
-
6
- ---
7
-
8
- Cedar Support is a fictional customer-support team. These figures are synthetic,
9
- hand-authored examples rather than results of an actual trial.
10
-
11
- Two groups each handled 120 tickets during one week. Assignment was not
12
- randomized, and ticket difficulty and agent experience were not controlled.
13
-
14
- - Standard workflow: median handling time of 12 minutes; 108 tickets passed
15
- quality review; 9 reopened within seven days of closure.
16
- - Suggested replies: median handling time of 9 minutes; 102 tickets passed
17
- quality review; 15 reopened within seven days of closure.
18
-
19
- Quality review and reopening are separate measures: a ticket can pass review
20
- and later reopen. Each rate uses all 120 tickets in its group. The quality rates
21
- are 90% and 85%; the reopen rates are 7.5% and 12.5%.
22
-
23
- We have no raw timing observations, variance estimates, confidence intervals,
24
- labor costs or customer-satisfaction results. The groups cannot establish
25
- causality, and their median times cannot tell us total hours saved.
26
-
27
- The proposed next step is a longer randomized trial with comparable ticket
28
- categories and human review retained. Quality and reopen-rate acceptance
29
- criteria would be agreed before starting; no numerical thresholds have been set.
30
-
31
- ---
32
-
33
- Give it a light editorial feel, with a white background, a deep forest-green
34
- accent, serif headlines and clean sans-serif body text.
@@ -1,85 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- import '@fontsource/newsreader/latin-400.css';
3
- import '@fontsource/ibm-plex-sans/latin-400.css';
4
- import '@fontsource/ibm-plex-sans/latin-600.css';
5
-
6
- const green = '#164D3A';
7
- const ink = '#202B26';
8
- const muted = '#526159';
9
- function Copy({ x = 112, y, lines, size = 36, color = ink, bold = false }: {
10
- x?: number; y: number; lines: string[]; size?: number; color?: string; bold?: boolean;
11
- }) {
12
- return <text x={x} y={y} fill={color} fontSize={size} fontWeight={bold ? 600 : 400}>
13
- {lines.map((line, i) => <tspan key={i} x={x} dy={i ? size * 1.35 : 0}>{line}</tspan>)}
14
- </text>;
15
- }
16
- function Frame({ number, title, summary, children }: {
17
- number: number; title: string[]; summary: string; children: ReactNode;
18
- }) {
19
- return <svg xmlns="http://www.w3.org/2000/svg" width="1920" height="1080" viewBox="0 0 1920 1080"
20
- role="img" aria-label={`${title.join(' ')}. ${summary}`} fontFamily="IBM Plex Sans" data-cedar-page={number}>
21
- <rect width="1920" height="1080" fill="#FFFFFF" />
22
- <text x="112" y="160" fontFamily="Newsreader" fontSize="82" fill={green}>
23
- {title.map((line, i) => <tspan key={i} x="112" dy={i ? 90 : 0}>{line}</tspan>)}
24
- </text>
25
- {children}
26
- <text x="1808" y="1024" textAnchor="end" fontSize="28" fill={muted}>{number} / 3</text>
27
- </svg>;
28
- }
29
-
30
- const Tradeoff = () => <Frame number={1} title={['Suggested replies were faster;', 'quality signals were weaker.']}
31
- summary="Cedar Support fictional trial, synthetic data. One week, 120 tickets per group. Standard versus suggested replies: median handling time 12 versus 9 minutes; quality passed 108 of 120 (90%) versus 102 of 120 (85%); reopened within seven days of closure 9 of 120 (7.5%) versus 15 of 120 (12.5%). Quality and reopening are separate measures; a passing ticket can reopen. Each rate uses all 120 tickets in its group.">
32
- <Copy y={335} lines={['Cedar Support · One week · 120 tickets per group']} color={muted} />
33
- <Copy x={900} y={443} lines={['Standard workflow']} size={32} bold />
34
- <Copy x={1335} y={443} lines={['Suggested replies']} size={32} color={green} bold />
35
- <line x1="112" x2="1808" y1="478" y2="478" stroke="#A4B2AA" />
36
- <Copy y={553} lines={['Median handling time']} />
37
- <Copy x={900} y={553} lines={['12 min']} size={46} />
38
- <Copy x={1335} y={553} lines={['9 min']} size={46} color={green} />
39
- <line x1="112" x2="1808" y1="601" y2="601" stroke="#D7DED9" />
40
- <Copy y={676} lines={['Passed quality review']} />
41
- <Copy x={900} y={676} lines={['108 / 120 · 90%']} size={40} />
42
- <Copy x={1335} y={676} lines={['102 / 120 · 85%']} size={40} color={green} />
43
- <line x1="112" x2="1808" y1="724" y2="724" stroke="#D7DED9" />
44
- <Copy y={791} lines={['Reopened within 7 days', 'of closure']} size={34} />
45
- <Copy x={900} y={791} lines={['9 / 120 · 7.5%']} size={40} />
46
- <Copy x={1335} y={791} lines={['15 / 120 · 12.5%']} size={40} color={green} />
47
- <line x1="112" x2="1808" y1="861" y2="861" stroke="#A4B2AA" />
48
- <Copy y={922} lines={['Quality and reopening are separate measures: a passing ticket can reopen.',
49
- 'Fictional team; synthetic, hand-authored figures—not an actual trial.']} size={30} color={muted} />
50
- </Frame>;
51
-
52
- const Evidence = () => <Frame number={2} title={['The pattern warrants a better test,', 'not a causal claim.']}
53
- summary="The observed median is 3 minutes lower, while quality pass rate is 5 percentage points lower and reopen rate 5 points higher. These are descriptive differences in synthetic data, not causal effects. Assignment was not randomized; ticket difficulty and agent experience were not controlled. There are no raw timing observations, variance estimates or confidence intervals. Medians cannot establish total hours saved. Labor costs and customer satisfaction were not measured.">
54
- <Copy y={360} lines={['What the comparison shows']} size={38} color={green} bold />
55
- <Copy y={433} lines={['3 minutes lower median handling time', '5 percentage points lower quality pass rate', '5 percentage points higher reopen rate']} size={37} />
56
- <Copy x={1035} y={360} lines={['What could explain it']} size={38} color={green} bold />
57
- <Copy x={1035} y={433} lines={['Assignment was not randomized.', 'Ticket difficulty and agent experience', 'were not controlled.']} size={37} />
58
- <Copy y={677} lines={['Precision is unknown']} size={38} color={green} bold />
59
- <Copy y={750} lines={['No raw timing observations, variance', 'estimates or confidence intervals.']} size={37} />
60
- <Copy x={1035} y={677} lines={['Business impact is unknown']} size={38} color={green} bold />
61
- <Copy x={1035} y={750} lines={['Medians cannot yield total hours saved.', 'No labor-cost or customer-satisfaction', 'results are available.']} size={37} />
62
- <Copy y={948} lines={['These are descriptive differences in synthetic data, not estimates of treatment effects.']} size={30} color={muted} />
63
- </Frame>;
64
-
65
- const Decision = () => <Frame number={3} title={['Extend with a longer randomized trial', 'and retain human review.']}
66
- summary="Recommendation to Cedar Support’s operations lead: authorize a longer randomized trial with comparable ticket categories and human review retained, rather than broad rollout. Agree quality and reopen-rate acceptance criteria before starting; no numerical thresholds have been set. Collect raw handling-time observations and both quality measures, retaining seven-day reopening follow-up. Decide whether speed improves while both quality guardrails are met; revise or stop expansion if either fails. These collection and decision details are recommendations, not observed results.">
67
- <Copy y={347} lines={['Recommendation to Cedar Support’s operations lead']} size={34} color={muted} />
68
- <Copy y={475} lines={['Before launch']} size={40} color={green} bold />
69
- <Copy x={650} y={475} lines={['Agree quality and reopen-rate acceptance criteria.', 'No numerical thresholds have been set.']} size={38} />
70
- <line x1="112" x2="1808" y1="578" y2="578" stroke="#D7DED9" />
71
- <Copy y={651} lines={['During the trial']} size={40} color={green} bold />
72
- <Copy x={650} y={651} lines={['Randomize across comparable ticket categories; retain human review.', 'Collect raw handling times and both quality measures.', 'Allow seven-day reopening follow-up after closure.']} size={35} />
73
- <line x1="112" x2="1808" y1="798" y2="798" stroke="#D7DED9" />
74
- <Copy y={871} lines={['At the decision']} size={40} color={green} bold />
75
- <Copy x={650} y={871} lines={['Does speed improve while both quality guardrails are met?', 'Use the results to judge rollout; revise or stop expansion if either fails.']} size={35} />
76
- <line x1="112" x2="1808" y1="968" y2="968" stroke="#A4B2AA" />
77
- </Frame>;
78
-
79
- export const meta = { title: 'Cedar Support — suggested replies decision', createdAt: '2026-09-12T14:00:00Z' };
80
- export const notes = [
81
- 'All figures are synthetic and supplied in PROMPT.md. Rates use 120 tickets in each group; quality review and reopening are not mutually exclusive.',
82
- 'Differences are arithmetic descriptions only. Non-random assignment and uncontrolled difficulty/experience prevent causal attribution.',
83
- 'The longer randomized trial, comparable categories, human review, and pre-agreed criteria are supplied proposals. Raw-time collection, complete follow-up and the decision sequence are author recommendations. Trial duration, sample size and thresholds require planning; none is invented here.',
84
- ];
85
- export default [Tradeoff, Evidence, Decision];
@@ -1,45 +0,0 @@
1
- Make a three-page delivery-plan briefing for the sponsor and delivery team
2
- at Harbor. Show the original schedule, the current forecast and the decision
3
- needed now. Make dependencies and release gates clear.
4
-
5
- Include a baseline-versus-forecast timeline on a shared date axis, distinguishing
6
- completed work from forecast work. Show dependency arrows and release gates.
7
-
8
- ---
9
-
10
- Harbor is a fictional membership service moving its member directory to a new
11
- system. This is a hand-authored status packet as of the end of 14 October 2026.
12
- The schedule uses Monday–Friday working days, with no holidays in this scenario.
13
-
14
- Original baseline, agreed 2 October:
15
- - Import: 5–9 October, owned by data lead Ana.
16
- - Validation: 12–14 October, owned by QA lead Ben.
17
- - Staff training: 15–16 October, owned by operations lead Cora.
18
- - Launch: 19 October, subject to sponsor approval.
19
-
20
- Actual progress: import started on 5 October and finished on 14 October.
21
- The input file contained duplicate member identifiers; resolving them delayed
22
- completion. Import completion does not mean validation has passed. Validation
23
- and training have not started. Training materials are ready.
24
-
25
- Dependencies: validation starts after import completion and requires three
26
- working days. Training starts after validation passes and requires two working
27
- days. Launch is no earlier than the next working day after training completes.
28
- No work overlaps these stages in the current plan.
29
-
30
- Current forecast, assuming no new defects: validation on 15, 16 and 19 October;
31
- training on 20–21 October; launch on 22 October. These are forecast dates, not
32
- completed work or a launch commitment.
33
-
34
- ---
35
-
36
- Validation must reconcile member counts and required fields against the approved
37
- input and resolve every duplicate identifier. An unresolved discrepancy blocks
38
- training and launch. The operations lead must confirm staff readiness after
39
- training; the sponsor then decides whether to release. No validation result is
40
- available yet.
41
-
42
- Ask the sponsor to accept the revised forecast and reserve staff for 20–21
43
- October. If the original date is immovable, the team needs a separate scope
44
- decision and revised plan; no safe shortcut has been established. Do not present
45
- skipping validation as a way to recover the schedule.
@@ -1,105 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- import { fontsReady } from '../../lib/typeface.ts';
3
-
4
- await fontsReady;
5
- const ink = '#172B36', muted = '#50616B', accent = '#006B74', line = '#CAD3D7';
6
- function Label({ x, y, children, size = 32, color = ink, weight = 400 }: { x: number; y: number; children: ReactNode; size?: number; color?: string; weight?: number }) {
7
- return <text x={x} y={y} fontSize={size} fill={color} fontWeight={weight}>{children}</text>;
8
- }
9
- function Frame({ title, subtitle, page, description, children }: { title: string; subtitle: string; page: number; description: string; children: ReactNode }) {
10
- return <svg xmlns="http://www.w3.org/2000/svg" width="1920" height="1080" viewBox="0 0 1920 1080" fontFamily="IBM Plex Sans" role="img" aria-label={`${title}. ${description}`} data-page={page}>
11
- <rect width="1920" height="1080" fill="#FFFFFF" />
12
- <Label x={100} y={140} size={64} weight={600}>{title}</Label>
13
- <Label x={100} y={207} size={32} color={muted}>{subtitle}</Label>
14
- {children}
15
- <Label x={1705} y={1020} size={26} color={muted}>{page} / 3</Label>
16
- </svg>;
17
- }
18
- const x = (day: number) => 440 + (day - 5) * 70;
19
- function Gate({ day, y }: { day: number; y: number }) {
20
- return <path d={`M${x(day)} ${y - 12} l12 12 -12 12 -12 -12 Z`} fill="white" stroke={accent} strokeWidth={3} />;
21
- }
22
- function Arrow({ from, to, y }: { from: number; to: number; y: number }) {
23
- return <path d={`M${x(from)} ${y} V${y + 29} H${x(to)} V${y + 82} m-7 -10 7 10 7 -10`} stroke={accent} strokeWidth={3} fill="none" />;
24
- }
25
- const Schedule = () => <Frame page={1} title="Launch moves from 19 to 22 October" subtitle="Import is complete. The remaining dates are a forecast, assuming no new defects." description="Shared calendar axis, October 5–22. Baseline agreed October 2: import October 5–9, validation 12–14, training 15–16, launch 19 subject to sponsor approval. Actual import October 5–14. Forecast validation October 15, 16 and 19; training 20–21; launch 22. Arrows connect non-overlapping forecast stages; diamonds mark validation pass, readiness confirmation and sponsor release gates.">
26
- <rect x={100} y={263} width={38} height={16} fill="none" stroke={muted} strokeWidth={2} />
27
- <Label x={153} y={282} size={27}>Baseline · agreed 2 Oct</Label>
28
- <rect x={500} y={263} width={38} height={16} fill={accent} />
29
- <Label x={553} y={282} size={27}>Completed</Label>
30
- <rect x={755} y={263} width={38} height={16} fill="none" stroke={accent} strokeDasharray="6 4" strokeWidth={3} />
31
- <Label x={808} y={282} size={27}>Forecast</Label>
32
- <path d="M1050 259 l12 12 -12 12 -12 -12 Z" stroke={accent} strokeWidth={3} fill="white" />
33
- <Label x={1076} y={282} size={27}>Release gate</Label>
34
- <Label x={1440} y={282} size={27} color={muted}>Shaded = weekend</Label>
35
- {[10, 11, 17, 18].map(day => <rect key={day} x={x(day)} y={326} width={70} height={499} fill="#F0F3F4" />)}
36
- {Array.from({ length: 18 }, (_, i) => i + 5).map(day => <g key={day}>
37
- <Label x={x(day) + 18} y={352} size={26} color={muted}>{day}</Label>
38
- <line x1={x(day)} x2={x(day)} y1={367} y2={825} stroke="#E6EBED" />
39
- </g>)}
40
- <Label x={100} y={320} size={25} color={muted}>October 2026</Label>
41
- <Label x={100} y={355} size={25} color={muted}>As of end of 14 Oct</Label>
42
- {[['Import', 'Ana · data lead'], ['Validation', 'Ben · QA lead'], ['Training', 'Cora · operations'], ['Launch', 'Sponsor approval']].map(([stage, owner], i) => <g key={stage}>
43
- <Label x={100} y={409 + i * 108} size={34} weight={600}>{stage}</Label>
44
- <Label x={100} y={447 + i * 108} size={26} color={muted}>{owner}</Label>
45
- <line x1={100} x2={1770} y1={474 + i * 108} y2={474 + i * 108} stroke={line} />
46
- </g>)}
47
- {[[5, 10, 389], [12, 15, 497], [15, 17, 605]].map(([a, b, y]) => <rect key={y} x={x(a)} y={y} width={x(b) - x(a)} height={17} fill="white" stroke={muted} strokeWidth={2} />)}
48
- <path d={`M${x(19) + 35} 711 l10 10 -10 10 -10 -10 Z`} fill="white" stroke={muted} strokeWidth={2} />
49
- <rect x={x(5)} y={424} width={x(15) - x(5)} height={26} fill={accent} />
50
- <Label x={x(5) + 18} y={445} size={23} color="white" weight={500}>5–14 Oct · complete</Label>
51
- {[[15, 17, 532], [19, 20, 532], [20, 22, 640]].map(([a, b, y]) => <rect key={`${a}-${y}`} x={x(a)} y={y} width={x(b) - x(a)} height={26} fill="white" stroke={accent} strokeWidth={3} strokeDasharray="7 4" />)}
52
- <Arrow from={15} to={15} y={450} />
53
- <Arrow from={20} to={20} y={558} />
54
- <Arrow from={22} to={22.5} y={666} />
55
- <Gate day={20} y={545} /><Gate day={22} y={653} /><Gate day={22.5} y={761} />
56
- <Label x={100} y={885} size={32} weight={500}>Duplicate member identifiers delayed import completion.</Label>
57
- <Label x={100} y={931} size={29} color={muted}>Mon–Fri working days; no holidays. No stage overlap. Import complete does not mean validation passed.</Label>
58
- <Label x={100} y={972} size={25} color={muted}>Diamonds at bar ends mark completion gates; launch diamonds mark release dates.</Label>
59
- </Frame>;
60
-
61
- const Gates = () => <Frame page={2} title="Every release gate still needs evidence" subtitle="Validation and training have not started. No validation result is available yet." description="Validation must reconcile member counts and required fields against approved input and resolve every duplicate identifier. An unresolved discrepancy blocks training and launch. Training follows validation pass and lasts two working days. Cora confirms staff readiness after training; sponsor then decides whether to release. Launch no earlier than next working day after training completes.">
62
- <Label x={100} y={331} size={28} color={muted}>FORECAST STAGE</Label>
63
- <Label x={670} y={331} size={28} color={muted}>EVIDENCE REQUIRED TO PROCEED</Label>
64
- <line x1={100} x2={1800} y1={357} y2={357} stroke={line} />
65
- <Label x={100} y={413} size={38} weight={600}>Validation · Ben</Label>
66
- <Label x={100} y={461} size={30} color={accent}>15, 16 & 19 Oct · 3 working days</Label>
67
- <Label x={670} y={413} size={34}>Reconcile member counts and required fields</Label>
68
- <Label x={670} y={459} size={34}>against approved input; resolve every duplicate ID.</Label>
69
- <Label x={670} y={511} size={29} color={accent} weight={500}>Any unresolved discrepancy blocks training and launch.</Label>
70
- <line x1={100} x2={1800} y1={553} y2={553} stroke={line} />
71
- <Label x={100} y={611} size={38} weight={600}>Staff training · Cora</Label>
72
- <Label x={100} y={659} size={30} color={accent}>20–21 Oct · 2 working days</Label>
73
- <Label x={670} y={611} size={34}>Start only after validation passes.</Label>
74
- <Label x={670} y={657} size={34}>Operations lead confirms staff readiness afterward.</Label>
75
- <Label x={670} y={709} size={29} color={muted}>Training materials are ready; staff readiness is not yet confirmed.</Label>
76
- <line x1={100} x2={1800} y1={751} y2={751} stroke={line} />
77
- <Label x={100} y={809} size={38} weight={600}>Release · Sponsor</Label>
78
- <Label x={100} y={857} size={30} color={accent}>22 Oct · conditional forecast</Label>
79
- <Label x={670} y={809} size={34}>Sponsor decides after staff readiness is confirmed.</Label>
80
- <Label x={670} y={855} size={34}>No earlier than the next working day after training.</Label>
81
- <line x1={100} x2={1800} y1={901} y2={901} stroke={line} />
82
- </Frame>;
83
-
84
- const Decision = () => <Frame page={3} title="Accept the forecast and reserve staff now" subtitle="Sponsor decision requested · Keep the release gates intact." description="Ask sponsor to accept the revised conditional launch forecast for October 22 and reserve staff for training October 20–21. Forecast assumes no new defects and is not a launch commitment. If October 19 is immovable, a separate scope decision and revised plan are needed. No safe shortcut has been established; do not skip validation.">
85
- <Label x={100} y={367} size={48} weight={600}>Accept 22 October</Label>
86
- <Label x={100} y={424} size={34}>as the revised launch forecast,</Label>
87
- <Label x={100} y={470} size={34}>assuming no new defects.</Label>
88
- <Label x={100} y={540} size={30} color={accent} weight={500}>A forecast, not a launch commitment.</Label>
89
- <Label x={1010} y={367} size={48} weight={600}>Reserve 20–21 October</Label>
90
- <Label x={1010} y={424} size={34}>for staff training with Cora.</Label>
91
- <Label x={1010} y={470} size={34}>Materials are ready.</Label>
92
- <Label x={1010} y={540} size={30} color={accent} weight={500}>Training starts only after validation passes.</Label>
93
- <line x1={100} x2={1800} y1={641} y2={641} stroke={line} />
94
- <Label x={100} y={723} size={42} weight={600}>If 19 October is immovable</Label>
95
- <Label x={100} y={785} size={36}>The team needs a separate scope decision and a revised plan.</Label>
96
- <Label x={100} y={846} size={36}>No safe shortcut has been established. Do not skip validation.</Label>
97
- </Frame>;
98
-
99
- export default [Schedule, Gates, Decision];
100
- export const meta = { title: 'Harbor — Delivery plan', createdAt: '2026-09-12T00:00:00Z' };
101
- export const notes = [
102
- 'Fictional, hand-authored packet as of end of 14 October 2026. Baseline agreed 2 October. Calendar axis is continuous, weekends shaded; forecast validation has no work on 17–18 October. Diamonds occur at stage completion boundaries or the launch date. All future stages and gates are conditional.',
103
- 'Import started 5 October and finished 14 October after duplicate member identifiers were resolved. Import completion is not evidence of validation passing. The validation gate still requires all stated checks.',
104
- 'Request acceptance of the revised forecast and reserve staff, not unconditional release authorization. No safe shortcut has been established for retaining the original launch date.',
105
- ];
@@ -1,44 +0,0 @@
1
- Make a three-page presentation for the engineering team at Fieldnote explaining
2
- an experiment with batching search-index updates. Help them weigh indexing
3
- throughput against how long users wait for a document to become searchable,
4
- and decide what to test next.
5
-
6
- ---
7
-
8
- Fieldnote is a fictional document workspace. Its current indexer processes
9
- document updates individually. The team tested batches of 20 and 100 updates
10
- against that baseline.
11
-
12
- Each configuration processed the same fixed set of 60,000 document updates
13
- on the same machine, starting with an empty queue and an identical index.
14
- An update's visibility delay runs from submission until the updated document
15
- appears in search. Each configuration was run once.
16
-
17
- Individual updates:
18
- - Total processing time: 300 seconds.
19
- - Median visibility delay: 1.2 seconds.
20
- - 95th-percentile visibility delay: 2.4 seconds.
21
-
22
- Batches of 20:
23
- - Total processing time: 200 seconds.
24
- - Median visibility delay: 1.8 seconds.
25
- - 95th-percentile visibility delay: 4.1 seconds.
26
-
27
- Batches of 100:
28
- - Total processing time: 150 seconds.
29
- - Median visibility delay: 4.9 seconds.
30
- - 95th-percentile visibility delay: 11.8 seconds.
31
-
32
- All configurations produced the expected final document versions, with no
33
- missing updates in the final check. These are synthetic, hand-authored results.
34
- There are no repeated runs, uncertainty estimates or production measurements.
35
-
36
- ---
37
-
38
- The team's proposed acceptance criteria are at least 250 updates per second
39
- and a 95th-percentile visibility delay no greater than 5 seconds.
40
-
41
- Recommend which configuration deserves a follow-up test. Explain why the
42
- fastest configuration is not necessarily the best fit. The next experiment
43
- should repeat the runs and include both sparse arrivals and bursts, since
44
- a fixed workload does not establish behavior under live traffic.
@@ -1,127 +0,0 @@
1
- import type { ReactNode } from 'react';
2
- import '@fontsource/ibm-plex-sans/latin-400.css';
3
- import '@fontsource/ibm-plex-sans/latin-500.css';
4
- import '@fontsource/ibm-plex-sans/latin-600.css';
5
-
6
- const ink = '#172B3A';
7
- const muted = '#52616C';
8
- const accent = '#096F78';
9
- const rule = '#B9C4CA';
10
- const results = [
11
- { name: 'Individual', time: 300, median: 1.2, p95: 2.4 },
12
- { name: 'Batch of 20', time: 200, median: 1.8, p95: 4.1 },
13
- { name: 'Batch of 100', time: 150, median: 4.9, p95: 11.8 },
14
- ];
15
-
16
- function Copy({ x = 112, y, size = 36, weight = 400, color = ink, children, anchor = 'start' }: {
17
- x?: number; y: number; size?: number; weight?: number; color?: string;
18
- children: ReactNode; anchor?: 'start' | 'middle' | 'end';
19
- }) {
20
- return <text x={x} y={y} fontSize={size} fontWeight={weight} fill={color} textAnchor={anchor}>{children}</text>;
21
- }
22
-
23
- function Frame({ page, title, description, children }: {
24
- page: number; title: string; description: string; children: ReactNode;
25
- }) {
26
- return <svg xmlns="http://www.w3.org/2000/svg" width="1920" height="1080" viewBox="0 0 1920 1080"
27
- fontFamily="IBM Plex Sans, sans-serif" role="img" aria-label={`${title}. ${description}`} data-fieldnote-page={page}>
28
- <rect width="1920" height="1080" fill="#FFFFFF" />
29
- <Copy y={158} size={68} weight={600}>{title}</Copy>
30
- {children}
31
- <Copy x={1808} y={1020} size={28} color={muted} anchor="end">{page} / 3</Copy>
32
- </svg>;
33
- }
34
-
35
- const Evidence = () => <Frame page={1} title="Batching buys speed at a freshness cost"
36
- description="Fieldnote is a fictional document workspace. Synthetic, hand-authored results, one run per configuration. The same 60,000 updates ran on the same machine, starting with an empty queue and identical index. Individual: 300 seconds, 200 updates per second, median delay 1.2 seconds, p95 2.4 seconds. Batch of 20: 200 seconds, 300 updates per second, median 1.8 seconds, p95 4.1 seconds. Batch of 100: 150 seconds, 400 updates per second, median 4.9 seconds, p95 11.8 seconds. Visibility delay is submission to search. All final versions were correct, with no missing updates; no repeated runs, uncertainty estimates or production measurements.">
37
- <Copy y={237} size={36} color={muted}>Fieldnote is a fictional workspace; its indexer handles updates individually.</Copy>
38
- <Copy y={291} size={36} color={muted}>Same 60,000 updates · same machine · empty queue · identical starting index</Copy>
39
-
40
- <Copy y={403} size={30} weight={500}>Configuration</Copy>
41
- <Copy x={770} y={383} size={30} weight={500} anchor="end">Processing</Copy>
42
- <Copy x={770} y={425} size={30} color={muted} anchor="end">seconds</Copy>
43
- <Copy x={1090} y={383} size={30} weight={500} anchor="end">Throughput</Copy>
44
- <Copy x={1090} y={425} size={30} color={muted} anchor="end">updates / sec</Copy>
45
- <Copy x={1450} y={383} size={30} weight={500} anchor="end">Median delay</Copy>
46
- <Copy x={1450} y={425} size={30} color={muted} anchor="end">seconds</Copy>
47
- <Copy x={1808} y={383} size={30} weight={500} anchor="end">95th-percentile delay</Copy>
48
- <Copy x={1808} y={425} size={30} color={muted} anchor="end">seconds</Copy>
49
- {[460, 565, 670, 775].map(y => <line key={y} x1={112} x2={1808} y1={y} y2={y} stroke={rule} strokeWidth={2} />)}
50
- {results.map((r, i) => <g key={r.name}>
51
- <Copy y={528 + i * 105} size={40} weight={500}>{r.name}</Copy>
52
- <Copy x={770} y={528 + i * 105} size={42} anchor="end">{r.time}</Copy>
53
- <Copy x={1090} y={528 + i * 105} size={42} anchor="end">{60000 / r.time}</Copy>
54
- <Copy x={1450} y={528 + i * 105} size={42} anchor="end">{r.median.toFixed(1)}</Copy>
55
- <Copy x={1808} y={528 + i * 105} size={42} anchor="end">{r.p95.toFixed(1)}</Copy>
56
- </g>)}
57
- <Copy y={832} size={30} color={muted}>Visibility delay: submission → searchable document. Throughput: 60,000 ÷ processing seconds.</Copy>
58
- <Copy y={884} size={30}>All final document versions matched expectations; no missing updates in the final check.</Copy>
59
- <Copy y={949} size={30} color={muted}>Synthetic, hand-authored results · one run each · no uncertainty estimates or production measurements</Copy>
60
- </Frame>;
61
-
62
- function Bars({ kind, x, width }: { kind: 'rate' | 'delay'; x: number; width: number }) {
63
- const rate = kind === 'rate';
64
- const max = rate ? 400 : 12;
65
- const target = rate ? 250 : 5;
66
- const thresholdX = x + target / max * width;
67
- return <g>
68
- <Copy x={x} y={348} size={38} weight={500}>{rate ? 'Throughput · updates / sec' : '95th-percentile delay · seconds'}</Copy>
69
- <Copy x={x} y={397} size={30} color={muted}>{rate ? 'At least 250 required' : 'No more than 5 required'}</Copy>
70
- <line x1={thresholdX} x2={thresholdX} y1={440} y2={762} stroke={ink} strokeDasharray="9 9" strokeWidth={2} />
71
- {results.map((r, i) => {
72
- const value = rate ? 60000 / r.time : r.p95;
73
- const pass = rate ? value >= target : value <= target;
74
- const y = 468 + i * 110;
75
- return <g key={r.name}>
76
- <rect x={x} y={y} width={value / max * width} height={44} fill={i === 1 ? accent : muted} />
77
- <Copy x={x + width + 24} y={y + 34} size={30} weight={500}>{value}{pass ? ' ✓' : ' ×'}</Copy>
78
- </g>;
79
- })}
80
- <line x1={x} x2={x + width} y1={768} y2={768} stroke={rule} strokeWidth={2} />
81
- {(rate ? [0, 100, 200, 300, 400] : [0, 3, 6, 9, 12]).map(tick => <g key={tick}>
82
- <line x1={x + tick / max * width} x2={x + tick / max * width} y1={768} y2={780} stroke={rule} strokeWidth={2} />
83
- <Copy x={x + tick / max * width} y={820} size={28} anchor="middle" color={muted}>{tick}</Copy>
84
- </g>)}
85
- </g>;
86
- }
87
-
88
- const Decision = () => <Frame page={2} title="Batches of 20 deserve the follow-up test"
89
- description="Only batches of 20 meet both proposed acceptance criteria in these single synthetic runs: at least 250 updates per second and p95 delay no greater than 5 seconds. Individual: 200 updates per second fails throughput, 2.4 seconds passes delay. Batch of 20: 300 and 4.1 both pass. Batch of 100: 400 passes throughput, 11.8 fails delay. The fastest configuration exceeds the delay ceiling by 6.8 seconds. These results support a follow-up test, not a production rollout.">
90
- <Copy y={237} size={36} color={muted}>Only this configuration meets both proposed criteria in the single-run results.</Copy>
91
- {results.map((r, i) => <Copy key={r.name} y={502 + i * 110} size={34} weight={i === 1 ? 600 : 400} color={i === 1 ? accent : ink}>{r.name}</Copy>)}
92
- <Bars kind="rate" x={445} width={485} />
93
- <Bars kind="delay" x={1170} width={485} />
94
- <Copy x={112} y={893} size={34}>Batch of 100 is fastest, but its p95 delay exceeds the ceiling by 6.8 seconds.</Copy>
95
- <Copy x={112} y={949} size={30} color={muted}>Synthetic, one run each. ✓ meets criterion · × misses criterion. This supports testing, not rollout.</Copy>
96
- </Frame>;
97
-
98
- const Next = () => <Frame page={3} title="Repeat the runs, then vary the arrival pattern"
99
- description="Prioritize batches of 20 and retain individual and batch-of-100 controls. Repeat runs under the original fixed-workload conditions and report run-to-run variability rather than selecting the best run. Include sparse arrivals to measure waits for a partial batch, recording the flush policy; include bursts to measure queue growth and recovery. Measure throughput, median and p95 submission-to-search delay, and final-version correctness. A fixed workload does not establish live-traffic behavior. Reassess batches of 20 against at least 250 updates per second under offered load and p95 no greater than 5 seconds; revise batching or flushing and retest if either fails.">
100
- <Copy y={237} size={36} color={muted}>Prioritize batches of 20; retain individual updates and batches of 100 as controls.</Copy>
101
-
102
- <Copy y={363} size={42} weight={600}>Repeat the fixed workload</Copy>
103
- <Copy y={425} size={34}>Keep the machine, starting index and</Copy>
104
- <Copy y={471} size={34}>60,000 updates comparable. Report</Copy>
105
- <Copy y={517} size={34}>run-to-run variability, not the best run.</Copy>
106
-
107
- <Copy x={1000} y={363} size={42} weight={600}>Exercise sparse arrivals and bursts</Copy>
108
- <Copy x={1000} y={425} size={34}>Sparse: measure waiting for a partial batch;</Copy>
109
- <Copy x={1000} y={471} size={34}>record the policy for flushing that batch.</Copy>
110
- <Copy x={1000} y={539} size={34}>Bursts: track queue growth and recovery,</Copy>
111
- <Copy x={1000} y={585} size={34}>including submission-to-search delay.</Copy>
112
-
113
- <line x1={112} x2={1808} y1={650} y2={650} stroke={rule} strokeWidth={2} />
114
- <Copy y={717} size={40} weight={600} color={accent}>Use both gates before advancing</Copy>
115
- <Copy y={777} size={34}>Recheck ≥250 updates/sec under offered load and p95 delay ≤5 sec;</Copy>
116
- <Copy y={823} size={34}>track median delay and final-version correctness. If either gate fails, revise</Copy>
117
- <Copy y={869} size={34}>batching or flushing and retest. Sparse traffic tests freshness, not peak capacity.</Copy>
118
- <Copy y={949} size={30} color={muted}>The fixed workload does not establish live-traffic behavior. No production measurements exist yet.</Copy>
119
- </Frame>;
120
-
121
- export const meta = { title: 'Fieldnote: batching search-index updates' };
122
- export const notes = [
123
- 'Source: the unchanged PROMPT.md. Fieldnote and all results are fictional. Values are not production evidence.',
124
- 'Recommendation is limited to a follow-up test. Rates are derived from 60,000 updates divided by processing time.',
125
- 'Repeated controls, recording the flush policy and queue recovery are proposed implementation details, not measured findings. No flush policy or repetition count was supplied.',
126
- ];
127
- export default [Evidence, Decision, Next];
@@ -1,57 +0,0 @@
1
- Make a three-page presentation walking a new on-call engineer through a
2
- webhook delivery incident at Lantern. Show how the team moved from symptoms
3
- to a supported diagnosis, mitigated the problem and checked recovery.
4
-
5
- ---
6
-
7
- Lantern is a fictional notification service. Its API accepts events into a
8
- durable queue, and workers deliver them to customer webhook endpoints.
9
- Queue age measures how long the oldest pending event has been waiting.
10
- The delivery objective is for 99% of accepted events to receive a successful
11
- endpoint response within five minutes.
12
-
13
- This fictional incident happened on 18 August 2026. All times are UTC.
14
-
15
- 09:00 — A worker release increased concurrent deliveries per worker from
16
- 20 to 80. Worker count and incoming event volume stayed unchanged.
17
-
18
- 09:05 — An alert fired: oldest pending event age reached six minutes.
19
- The queue contained 18,000 events.
20
-
21
- 09:08 — The on-call engineer confirmed that API acceptance remained healthy.
22
- Worker CPU was below 50%, but outbound requests were timing out more often.
23
- The team considered a traffic surge, customer endpoint failures and a
24
- worker-side connection bottleneck.
25
-
26
- 09:12 — Incoming volume matched its pre-release level. Timeouts appeared
27
- across many unrelated customer endpoints. Worker traces showed requests
28
- waiting for an available outbound connection. Each worker's connection pool
29
- was still limited to 20 connections.
30
-
31
- 09:15 — The team rolled back the concurrency setting to 20. It did not purge
32
- the queue or replay already completed jobs.
33
-
34
- 09:20 — Connection waits and timeout rates returned to their pre-release
35
- levels. The queue still contained 24,000 events, down from a peak of 30,000.
36
- The oldest pending event was 14 minutes old.
37
-
38
- 09:40 — Queue depth returned to its usual range of 0–500 events. The oldest
39
- pending event was 40 seconds old.
40
-
41
- 10:10 — For the preceding 30 minutes, 99.3% of newly accepted events received
42
- a successful endpoint response within five minutes.
43
-
44
- ---
45
-
46
- The evidence supports a mismatch between worker concurrency and the connection
47
- pool as the incident's cause. Recovery after rollback strengthens that
48
- explanation, but the team has not yet reproduced the failure in a controlled test.
49
-
50
- Separate mitigation from recovery: lower timeout rates at 09:20 did not mean
51
- the backlog had cleared. The 10:10 result describes newly accepted events;
52
- it does not erase the delivery-objective breach during the incident.
53
-
54
- The follow-up work is to reproduce the mismatch under load, validate concurrency
55
- and pool settings together before release, and add connection-wait time to the
56
- rollout dashboard. No data loss was observed, but the team has not completed
57
- an event-by-event delivery audit.