konpeki 0.1.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.
- package/.agents/skills/authoring-visuals/SKILL.md +82 -0
- package/AGENTS.md +33 -0
- package/AUTHORING.md +233 -0
- package/LICENSE +201 -0
- package/README.md +70 -0
- package/SETUP.md +86 -0
- package/composition/README.md +166 -0
- package/composition/compile.ts +227 -0
- package/composition/document.ts +709 -0
- package/composition/schema.json +2992 -0
- package/composition/schema.ts +435 -0
- package/composition/theme-tokens.ts +16 -0
- package/composition/types.ts +286 -0
- package/composition/validate.ts +684 -0
- package/composition/vector.ts +143 -0
- package/composition/visualizations.ts +335 -0
- package/design/README.md +17 -0
- package/design/palettes/README.md +14 -0
- package/design/palettes/base.ts +14 -0
- package/design/palettes/candidates.ts +19 -0
- package/design/palettes/index.ts +78 -0
- package/design/review/color-theme.md +44 -0
- package/design/review/layout.md +16 -0
- package/design/review/text.md +18 -0
- package/design/review/typography.md +15 -0
- package/design/review/visuals.md +31 -0
- package/design/semantic-patterns.md +43 -0
- package/design/themes/README.md +22 -0
- package/design/themes/index.ts +13 -0
- package/design/visual-languages/technical-product.md +17 -0
- package/design/visual-review.md +88 -0
- package/docs/development.md +140 -0
- package/docs/workflow.md +61 -0
- package/index.html +17 -0
- package/lib/assets.d.ts +8 -0
- package/lib/charts.ts +18 -0
- package/lib/contrast.ts +16 -0
- package/lib/layouts.ts +50 -0
- package/lib/slide.tsx +42 -0
- package/lib/taste.ts +17 -0
- package/lib/text.tsx +89 -0
- package/lib/typeface.ts +44 -0
- package/package.json +85 -0
- package/runtime/konpeki.mjs +1685 -0
- package/scripts/migrate-react-page.ts +120 -0
- package/slides/README.md +153 -0
- package/slides/architecture/PROMPT.md +31 -0
- package/slides/architecture/index.tsx +102 -0
- package/slides/article-brief/PROMPT.md +35 -0
- package/slides/article-brief/index.tsx +71 -0
- package/slides/bar-chart/PROMPT.md +39 -0
- package/slides/bar-chart/index.tsx +97 -0
- package/slides/comparison/PROMPT.md +29 -0
- package/slides/comparison/index.tsx +95 -0
- package/slides/decision-memo/PROMPT.md +34 -0
- package/slides/decision-memo/index.tsx +85 -0
- package/slides/delivery-plan/PROMPT.md +45 -0
- package/slides/delivery-plan/index.tsx +105 -0
- package/slides/experiment/PROMPT.md +44 -0
- package/slides/experiment/index.tsx +127 -0
- package/slides/incident-workflow/PROMPT.md +57 -0
- package/slides/incident-workflow/index.tsx +78 -0
- package/slides/introducing-konpeki/PROMPT.md +19 -0
- package/slides/introducing-konpeki/README.md +54 -0
- package/slides/introducing-konpeki/SOURCE.md +20 -0
- package/slides/introducing-konpeki/author.ts +163 -0
- package/slides/introducing-konpeki/composition.json +3265 -0
- package/slides/line-chart/PROMPT.md +40 -0
- package/slides/line-chart/index.tsx +72 -0
- package/slides/migration/PROMPT.md +38 -0
- package/slides/migration/index.tsx +89 -0
- package/slides/og-images/PROMPT.md +21 -0
- package/slides/og-images/index.tsx +76 -0
- package/slides/product-introduction/PROMPT.md +24 -0
- package/slides/product-introduction/index.tsx +105 -0
- package/slides/research-brief/PROMPT.md +40 -0
- package/slides/research-brief/index.tsx +104 -0
- package/slides/results-explanation/PROMPT.md +32 -0
- package/slides/results-explanation/index.tsx +96 -0
- package/slides/retrospective/PROMPT.md +43 -0
- package/slides/retrospective/index.tsx +105 -0
- package/slides/sankey/PROMPT.md +11 -0
- package/slides/sankey/index.tsx +93 -0
- package/slides/teaching/PROMPT.md +45 -0
- package/slides/teaching/index.tsx +124 -0
- package/slides/vertical-bar-charts/PROMPT.md +13 -0
- package/slides/vertical-bar-charts/index.tsx +97 -0
- package/src/app/App.tsx +694 -0
- package/src/assets/konpeki-mark.png +0 -0
- package/src/components/Canvas.tsx +1168 -0
- package/src/components/DiagramTypeIcon.tsx +78 -0
- package/src/components/InspectorPanel.tsx +687 -0
- package/src/components/LeftPanel.tsx +107 -0
- package/src/components/PageSizePicker.tsx +30 -0
- package/src/components/Presentation.tsx +105 -0
- package/src/components/RightPanel.tsx +201 -0
- package/src/components/VectorOverflowWarning.tsx +46 -0
- package/src/components/WorkspaceChrome.tsx +199 -0
- package/src/components/ui.tsx +53 -0
- package/src/lib/examples/react-page-migration.json +1295 -0
- package/src/lib/examples.ts +42 -0
- package/src/lib/export-png.ts +101 -0
- package/src/lib/file-session.ts +74 -0
- package/src/lib/history.ts +53 -0
- package/src/lib/model.ts +188 -0
- package/src/lib/page-size.ts +24 -0
- package/src/lib/presentation.ts +17 -0
- package/src/lib/storage.ts +43 -0
- package/src/lib/theme.ts +25 -0
- package/src/lib/use-file-session.ts +162 -0
- package/src/main.tsx +26 -0
- package/src/styles/base.css +105 -0
- package/src/styles/canvas.css +268 -0
- package/src/styles/chrome.css +214 -0
- package/src/styles/component-previews.css +386 -0
- package/src/styles/feedback.css +71 -0
- package/src/styles/left-panel.css +125 -0
- package/src/styles/presentation.css +72 -0
- package/src/styles/right-panel.css +1172 -0
- package/src/styles/shell.css +247 -0
- package/vite.config.ts +6 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, relative, resolve } from "node:path";
|
|
3
|
+
import { createElement, type ComponentType } from "react";
|
|
4
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
5
|
+
import { createServer } from "vite";
|
|
6
|
+
import { compositionSchema } from "../composition/types.ts";
|
|
7
|
+
import { validateComposition } from "../composition/validate.ts";
|
|
8
|
+
import { parseEditableSvg } from "../composition/vector.ts";
|
|
9
|
+
|
|
10
|
+
if (process.argv.includes("--help")) {
|
|
11
|
+
console.log("Usage: pnpm example:migrate-page [trusted-react-file] [all|zero-based-page] [output.json]\nExecutes trusted local React source; imports its supported SVG primitives as editable composition vectors.");
|
|
12
|
+
process.exit(0);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const root = process.cwd();
|
|
16
|
+
const input = resolve(root, process.argv[2] ?? "slides/architecture/index.tsx");
|
|
17
|
+
const pageSelection = process.argv[3] ?? "all";
|
|
18
|
+
const output = resolve(
|
|
19
|
+
root,
|
|
20
|
+
process.argv[4] ?? "src/lib/examples/react-page-migration.json",
|
|
21
|
+
);
|
|
22
|
+
const moduleId = `/${relative(root, input).replaceAll("\\", "/")}`;
|
|
23
|
+
|
|
24
|
+
if (
|
|
25
|
+
pageSelection !== "all" &&
|
|
26
|
+
(!Number.isInteger(Number(pageSelection)) || Number(pageSelection) < 0)
|
|
27
|
+
)
|
|
28
|
+
throw new Error('Page selection must be "all" or a non-negative index.');
|
|
29
|
+
|
|
30
|
+
const server = await createServer({
|
|
31
|
+
root,
|
|
32
|
+
appType: "custom",
|
|
33
|
+
server: { middlewareMode: true },
|
|
34
|
+
plugins: [
|
|
35
|
+
{
|
|
36
|
+
name: "konpeki-react-page-migration",
|
|
37
|
+
enforce: "pre",
|
|
38
|
+
resolveId(source) {
|
|
39
|
+
if (source.endsWith("/lib/typeface.ts")) return "\0konpeki-typeface";
|
|
40
|
+
},
|
|
41
|
+
load(id) {
|
|
42
|
+
if (id === "\0konpeki-typeface")
|
|
43
|
+
return "export const fontsReady = Promise.resolve([]);";
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
try {
|
|
50
|
+
const loaded = await server.ssrLoadModule(moduleId) as {
|
|
51
|
+
default?: ComponentType[];
|
|
52
|
+
meta?: { title?: string };
|
|
53
|
+
};
|
|
54
|
+
const pages = loaded.default ?? [];
|
|
55
|
+
const indexes = pageSelection === "all"
|
|
56
|
+
? pages.map((_, index) => index)
|
|
57
|
+
: [Number(pageSelection)];
|
|
58
|
+
if (!indexes.length || indexes.some((index) => !pages[index]))
|
|
59
|
+
throw new Error(`No selected page exported by ${moduleId}.`);
|
|
60
|
+
const title = loaded.meta?.title ?? "Migrated React page";
|
|
61
|
+
const document = {
|
|
62
|
+
schema: compositionSchema,
|
|
63
|
+
title: `${title} — migrated React deck`,
|
|
64
|
+
theme: { id: "plex", mode: "paper" },
|
|
65
|
+
slides: indexes.map((pageIndex, outputIndex) => {
|
|
66
|
+
const source = renderToStaticMarkup(createElement(pages[pageIndex]));
|
|
67
|
+
const parsed = parseEditableSvg(source);
|
|
68
|
+
const id = `migrated-page-${outputIndex + 1}`;
|
|
69
|
+
const slotId = `${id}-content`;
|
|
70
|
+
return {
|
|
71
|
+
id: `slide-${outputIndex + 1}`,
|
|
72
|
+
name: `Migrated page ${pageIndex + 1}`,
|
|
73
|
+
canvas: { width: 1920, height: 1080 },
|
|
74
|
+
innerPadding: { top: 72, right: 112, bottom: 0, left: 112 },
|
|
75
|
+
pageNumber: { style: "none", color: "muted" },
|
|
76
|
+
audience: "Reviewers of the migrated slide",
|
|
77
|
+
question: "Does the former React page survive in the shared canvas?",
|
|
78
|
+
intendedViewingSize: "presentation",
|
|
79
|
+
contentSlots: [
|
|
80
|
+
{
|
|
81
|
+
id: slotId,
|
|
82
|
+
label: "Migrated page",
|
|
83
|
+
required: true,
|
|
84
|
+
instruction: "Preserve the trusted React page as editable vector elements.",
|
|
85
|
+
role: "entity",
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
components: [
|
|
89
|
+
{
|
|
90
|
+
id,
|
|
91
|
+
kind: "diagram",
|
|
92
|
+
preferredRect: { x: 0, y: 0, width: 1920, height: 1080 },
|
|
93
|
+
slotIds: [slotId],
|
|
94
|
+
intent: "A former React page converted to editable vector elements.",
|
|
95
|
+
appearance: { type: "architecture", border: "none" },
|
|
96
|
+
customVisual: {
|
|
97
|
+
format: "vector",
|
|
98
|
+
elements: parsed.elements,
|
|
99
|
+
viewBox: parsed.viewBox,
|
|
100
|
+
description: `Migrated page ${pageIndex + 1} from ${title}`,
|
|
101
|
+
fit: "stretch",
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
groups: [],
|
|
106
|
+
readingOrder: [{ kind: "component", id }],
|
|
107
|
+
paintOrder: [id],
|
|
108
|
+
relationships: [],
|
|
109
|
+
};
|
|
110
|
+
}),
|
|
111
|
+
};
|
|
112
|
+
const validation = validateComposition(document);
|
|
113
|
+
if (!validation.ok)
|
|
114
|
+
throw new Error(`Generated composition is invalid: ${JSON.stringify(validation.issues)}`);
|
|
115
|
+
await mkdir(dirname(output), { recursive: true });
|
|
116
|
+
await writeFile(output, `${JSON.stringify(validation.document, null, 2)}\n`);
|
|
117
|
+
console.log(`${relative(root, input)} (${indexes.length} pages) → ${relative(root, output)}`);
|
|
118
|
+
} finally {
|
|
119
|
+
await server.close();
|
|
120
|
+
}
|
package/slides/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Decks and examples
|
|
2
|
+
|
|
3
|
+
The image gallery below is for the repository checkout. The npm package includes
|
|
4
|
+
editable examples and prompts but omits gallery PNGs; view this page and its
|
|
5
|
+
screenshots in the repository, or render the source locally.
|
|
6
|
+
|
|
7
|
+
## Introducing Konpeki
|
|
8
|
+
|
|
9
|
+
[Introducing Konpeki](introducing-konpeki/README.md) is a six-slide factual product
|
|
10
|
+
introduction authored in the shared canvas's editable composition format.
|
|
11
|
+
Open `?example=introducing-konpeki` in the running application, or download
|
|
12
|
+
its [composition JSON](introducing-konpeki/composition.json) and use **Open**.
|
|
13
|
+
|
|
14
|
+
## Retained drawing references
|
|
15
|
+
|
|
16
|
+
Each retained example contains its approved `PROMPT.md`, editable `index.tsx`, and final
|
|
17
|
+
PNGs in `screenshots/`. Generation logs, environment records, review reports and
|
|
18
|
+
superseded captures are not included. Subjects and datasets in these retained
|
|
19
|
+
references are fictional; they are not another presentation runtime.
|
|
20
|
+
|
|
21
|
+
## Representative charts
|
|
22
|
+
|
|
23
|
+
### Line chart — Tide, page 1
|
|
24
|
+
|
|
25
|
+
Two time series on a shared scale, direct labels, a target line, an event marker
|
|
26
|
+
and an explicit gap for missing data. [Editable source](line-chart/index.tsx).
|
|
27
|
+
|
|
28
|
+

|
|
29
|
+
|
|
30
|
+
### Grouped bar chart — Birch, page 1
|
|
31
|
+
|
|
32
|
+
Horizontal before/after bars with a shared zero baseline and direct values.
|
|
33
|
+
[Editable source](bar-chart/index.tsx).
|
|
34
|
+
|
|
35
|
+

|
|
36
|
+
|
|
37
|
+
### Vertical bar chart — Fieldnote, page 1
|
|
38
|
+
|
|
39
|
+
Zero-based columns compare processing throughput with direct values.
|
|
40
|
+
[Editable source](vertical-bar-charts/index.tsx).
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
### Two charts side by side — Fieldnote, page 2
|
|
45
|
+
|
|
46
|
+
Throughput on the left and visibility delay on the right show the trade-off,
|
|
47
|
+
with separate units, zero-based scales and acceptance thresholds.
|
|
48
|
+
[Editable source](vertical-bar-charts/index.tsx).
|
|
49
|
+
|
|
50
|
+

|
|
51
|
+
|
|
52
|
+
### Stacked bar chart — Fern, page 1
|
|
53
|
+
|
|
54
|
+
Direct-count segments compare completion before and after a hint, with the same
|
|
55
|
+
denominator for every task. [Editable source](research-brief/index.tsx).
|
|
56
|
+
|
|
57
|
+

|
|
58
|
+
|
|
59
|
+
## Representative content layouts
|
|
60
|
+
|
|
61
|
+
### Two columns — Fern, page 2
|
|
62
|
+
|
|
63
|
+
Parallel columns separate observed evidence from researchers' interpretations.
|
|
64
|
+
[Editable source](research-brief/index.tsx).
|
|
65
|
+
|
|
66
|
+

|
|
67
|
+
|
|
68
|
+
### Three columns — Lantern, page 2
|
|
69
|
+
|
|
70
|
+
Three aligned time-based columns distinguish rollback, partial recovery and
|
|
71
|
+
backlog clearance. [Editable source](incident-workflow/index.tsx).
|
|
72
|
+
|
|
73
|
+

|
|
74
|
+
|
|
75
|
+
### Main panel and sidebar — Patchnote, page 2
|
|
76
|
+
|
|
77
|
+
A large illustrative draft panel pairs with a narrower human-review checklist.
|
|
78
|
+
[Editable source](product-introduction/index.tsx).
|
|
79
|
+
|
|
80
|
+

|
|
81
|
+
|
|
82
|
+
## Representative diagrams and structured information
|
|
83
|
+
|
|
84
|
+
### Architecture diagram — Parcel Relay, page 1
|
|
85
|
+
|
|
86
|
+
Named components and attached connectors explain acceptance and delivery.
|
|
87
|
+
[Editable source](architecture/index.tsx).
|
|
88
|
+
|
|
89
|
+

|
|
90
|
+
|
|
91
|
+
### Sequence diagram — Parcel Relay, page 3
|
|
92
|
+
|
|
93
|
+
Two participant lifelines show a lost response and retry of the same event.
|
|
94
|
+
[Editable source](architecture/index.tsx).
|
|
95
|
+
|
|
96
|
+

|
|
97
|
+
|
|
98
|
+
### Gantt chart — Harbor, page 1
|
|
99
|
+
|
|
100
|
+
A shared calendar axis compares baseline, completed and forecast work, with
|
|
101
|
+
dependencies and release gates. [Editable source](delivery-plan/index.tsx).
|
|
102
|
+
|
|
103
|
+

|
|
104
|
+
|
|
105
|
+
### Sankey chart — Brook, page 1
|
|
106
|
+
|
|
107
|
+
Proportional flows carry 120 tickets from intake through handling to outcomes,
|
|
108
|
+
with direct counts and conservation at each stage.
|
|
109
|
+
[Editable source](sankey/index.tsx).
|
|
110
|
+
|
|
111
|
+

|
|
112
|
+
|
|
113
|
+
### Evidence table — Harbor, page 2
|
|
114
|
+
|
|
115
|
+
Aligned stage–evidence rows use horizontal rules to make gate requirements easy
|
|
116
|
+
to compare. [Editable source](delivery-plan/index.tsx).
|
|
117
|
+
|
|
118
|
+

|
|
119
|
+
|
|
120
|
+
## All examples
|
|
121
|
+
|
|
122
|
+
The selections above demonstrate distinct visual patterns rather than ranking
|
|
123
|
+
decks. All final screenshots remain available below.
|
|
124
|
+
|
|
125
|
+
| Example | Prompt | Final screenshots |
|
|
126
|
+
| --- | --- | --- |
|
|
127
|
+
| Patchnote — product introduction | [Prompt](product-introduction/PROMPT.md) | [Images](product-introduction/screenshots/) |
|
|
128
|
+
| Parcel Relay — architecture | [Prompt](architecture/PROMPT.md) | [Images](architecture/screenshots/) |
|
|
129
|
+
| Fieldnote — experiment | [Prompt](experiment/PROMPT.md) | [Images](experiment/screenshots/) |
|
|
130
|
+
| Lantern — incident workflow | [Prompt](incident-workflow/PROMPT.md) | [Images](incident-workflow/screenshots/) |
|
|
131
|
+
| Moss — comparison | [Prompt](comparison/PROMPT.md) | [Images](comparison/screenshots/) |
|
|
132
|
+
| Juniper — migration | [Prompt](migration/PROMPT.md) | [Images](migration/screenshots/) |
|
|
133
|
+
| Clearpath — OG images | [Prompt](og-images/PROMPT.md) | [Images](og-images/screenshots/) |
|
|
134
|
+
| Alder — article briefing | [Prompt](article-brief/PROMPT.md) | [Images](article-brief/screenshots/) |
|
|
135
|
+
| Waymark — results explanation | [Prompt](results-explanation/PROMPT.md) | [Images](results-explanation/screenshots/) |
|
|
136
|
+
| Dockline — retrospective | [Prompt](retrospective/PROMPT.md) | [Images](retrospective/screenshots/) |
|
|
137
|
+
| Fern — research briefing | [Prompt](research-brief/PROMPT.md) | [Images](research-brief/screenshots/) |
|
|
138
|
+
| Cedar Support — decision memo | [Prompt](decision-memo/PROMPT.md) | [Images](decision-memo/screenshots/) |
|
|
139
|
+
| Harbor — delivery plan | [Prompt](delivery-plan/PROMPT.md) | [Images](delivery-plan/screenshots/) |
|
|
140
|
+
| Folio — teaching | [Prompt](teaching/PROMPT.md) | [Images](teaching/screenshots/) |
|
|
141
|
+
| Tide — line chart | [Prompt](line-chart/PROMPT.md) | [Images](line-chart/screenshots/) |
|
|
142
|
+
| Birch — bar chart | [Prompt](bar-chart/PROMPT.md) | [Images](bar-chart/screenshots/) |
|
|
143
|
+
| Fieldnote — vertical and paired bar charts | [Prompt](vertical-bar-charts/PROMPT.md) | [Images](vertical-bar-charts/screenshots/) |
|
|
144
|
+
| Brook — Sankey chart | [Prompt](sankey/PROMPT.md) | [Images](sankey/screenshots/) |
|
|
145
|
+
|
|
146
|
+
The `index.tsx` files are retained presentation-runtime-independent React/SVG
|
|
147
|
+
drawing references, not runnable routes or a second deck format. Agents can
|
|
148
|
+
inspect and adapt their techniques, then convert supported SVG primitives into
|
|
149
|
+
editable vector elements in the owning composition component.
|
|
150
|
+
Shared authoring resources live in `design/` and `lib/`.
|
|
151
|
+
|
|
152
|
+
Screenshots reflect the final reviewed browser renders, including manual repairs.
|
|
153
|
+
They do not establish PDF/PPTX, cross-browser or cross-application fidelity.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
Make a three-page presentation explaining Parcel Relay to engineers joining its
|
|
2
|
+
team. Help them follow an event from acceptance to delivery and understand why
|
|
3
|
+
a timeout can lead to a duplicate request.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Parcel Relay is a fictional webhook delivery service. A producer submits an
|
|
8
|
+
event with an event ID. The API saves the event and its delivery job in one
|
|
9
|
+
database transaction and returns Accepted after the transaction commits. If
|
|
10
|
+
the transaction cannot commit, the API returns an error and the event has not
|
|
11
|
+
been accepted.
|
|
12
|
+
|
|
13
|
+
Workers claim jobs from that database using time-limited leases, then POST the
|
|
14
|
+
events to customer endpoints. Each request includes the event ID. The database
|
|
15
|
+
holds the job queue; there is no separate message broker.
|
|
16
|
+
|
|
17
|
+
A 2xx response marks the job delivered. A timeout or 5xx schedules another
|
|
18
|
+
attempt, up to three attempts in total. A third unsuccessful attempt marks the
|
|
19
|
+
job failed for human investigation. A 4xx is terminal in this example. If a
|
|
20
|
+
worker crashes, another worker can claim its unfinished job after the lease
|
|
21
|
+
expires, even if the customer already received the earlier request.
|
|
22
|
+
|
|
23
|
+
Customers must handle repeated event IDs. Relay cannot make their side effects
|
|
24
|
+
atomic or guarantee exactly-once delivery.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
Walk through this failure: evt-204 reaches the customer, which applies its
|
|
29
|
+
update, but the response is lost. Relay times out and retries evt-204. The
|
|
30
|
+
customer recognizes the event ID and skips the repeated update. That final
|
|
31
|
+
step depends on the customer's implementation; Relay does not enforce it.
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import '../../lib/typeface.ts';
|
|
3
|
+
|
|
4
|
+
const ink = '#172633', muted = '#4D5D6B', accent = '#006EAC', line = '#A7B2BC';
|
|
5
|
+
function Text({ x, y, children, size = 34, weight = 400, color = ink }: { x: number; y: number; children: ReactNode; size?: number; weight?: number; color?: string }) {
|
|
6
|
+
return <text x={x} y={y} fontSize={size} fontWeight={weight} fill={color}>{children}</text>;
|
|
7
|
+
}
|
|
8
|
+
function Arrow({ x1, x2, y, dashed = false }: { x1: number; x2: number; y: number; dashed?: boolean }) {
|
|
9
|
+
const d = x2 > x1 ? -16 : 16;
|
|
10
|
+
return <g fill="none" stroke={accent} strokeWidth={3}><path d={`M${x1} ${y} H${x2}`} strokeDasharray={dashed ? '10 8' : undefined}/><path d={`M${x2 + d} ${y - 10} L${x2} ${y} L${x2 + d} ${y + 10}`}/></g>;
|
|
11
|
+
}
|
|
12
|
+
function Frame({ title, description, page, children }: { title: string; description: string; page: number; children: ReactNode }) {
|
|
13
|
+
return <svg xmlns="http://www.w3.org/2000/svg" width="1920" height="1080" viewBox="0 0 1920 1080" fontFamily="IBM Plex Sans, sans-serif" role="img" aria-label={`${title}. ${description}`}>
|
|
14
|
+
<rect width={1920} height={1080} fill="#FFFFFF"/>
|
|
15
|
+
<Text x={112} y={150} size={64} weight={600}>{title}</Text>
|
|
16
|
+
{children}
|
|
17
|
+
<Text x={1760} y={1018} size={26} color={muted}>{page} / 3</Text>
|
|
18
|
+
</svg>;
|
|
19
|
+
}
|
|
20
|
+
function Acceptance() {
|
|
21
|
+
return <Frame page={1} title="Parcel Relay accepts an event after commit" description="Fictional service. Producer submits an event ID to the API. The API saves event and job in one database transaction. Accepted follows commit; commit failure returns an error, so the event was not accepted. Workers claim leased jobs from the database and POST to customer endpoints with the event ID. No separate broker.">
|
|
22
|
+
<Text x={112} y={224} color={muted}>A fictional webhook service • the database holds both events and the job queue</Text>
|
|
23
|
+
<Text x={160} y={365} size={40} weight={600}>Producer</Text>
|
|
24
|
+
<Text x={548} y={365} size={40} weight={600}>API</Text>
|
|
25
|
+
<path d="M250 390 V635 M580 390 V635" fill="none" stroke={line} strokeWidth={2}/>
|
|
26
|
+
<rect x={965} y={294} width={835} height={280} rx={8} fill="#FFFFFF" stroke={line} strokeWidth={2}/>
|
|
27
|
+
<Text x={1005} y={355} size={40} weight={600}>Database</Text>
|
|
28
|
+
<Text x={1005} y={423}>Event + delivery job</Text>
|
|
29
|
+
<Text x={1005} y={479} color={accent} weight={500}>Saved in one transaction</Text>
|
|
30
|
+
<Text x={1005} y={531} size={30} color={muted}>Commit is the acceptance boundary.</Text>
|
|
31
|
+
<Arrow x1={250} x2={580} y={415}/>
|
|
32
|
+
<Text x={280} y={397} size={30}>Submit event ID</Text>
|
|
33
|
+
<Arrow x1={580} x2={965} y={415}/>
|
|
34
|
+
<Text x={690} y={391} size={30}>Save both</Text>
|
|
35
|
+
<Arrow x1={965} x2={580} y={515}/>
|
|
36
|
+
<Text x={680} y={563} size={30}>Commit succeeds</Text>
|
|
37
|
+
<Arrow x1={580} x2={250} y={610}/>
|
|
38
|
+
<Text x={236} y={660} color={accent} weight={500}>Return Accepted</Text>
|
|
39
|
+
<path d="M1120 704 V574 M1110 590 L1120 574 L1130 590" fill="none" stroke={accent} strokeWidth={3}/>
|
|
40
|
+
<Text x={1160} y={644} size={30}>Claim leased job</Text>
|
|
41
|
+
<Text x={112} y={797} size={40} weight={600}>If commit fails</Text>
|
|
42
|
+
<Text x={112} y={855}>API returns an error.</Text>
|
|
43
|
+
<Text x={112} y={907}>The event has not been accepted.</Text>
|
|
44
|
+
<Text x={1020} y={738} size={40} weight={600}>Worker</Text>
|
|
45
|
+
<path d="M1120 756 V844" fill="none" stroke={accent} strokeWidth={3}/>
|
|
46
|
+
<Arrow x1={1120} x2={1550} y={844}/>
|
|
47
|
+
<Text x={1160} y={891} size={30}>POST + event ID</Text>
|
|
48
|
+
<Text x={1555} y={817} size={38} weight={600}>Customer</Text>
|
|
49
|
+
<Text x={1555} y={869} size={30}>endpoint</Text>
|
|
50
|
+
</Frame>;
|
|
51
|
+
}
|
|
52
|
+
function Outcomes() {
|
|
53
|
+
return <Frame page={2} title="The response decides the job’s next state" description="2xx marks delivered. Timeout or 5xx schedules another attempt, up to three attempts total. The third unsuccessful attempt marks failed for human investigation. 4xx is terminal. If a worker crashes, another may claim the unfinished job after lease expiry even if the customer already received it.">
|
|
54
|
+
<Text x={112} y={228} color={muted}>After each POST, the worker records the outcome in the database.</Text>
|
|
55
|
+
<Text x={112} y={346} size={30} color={muted}>OBSERVED RESULT</Text>
|
|
56
|
+
<Text x={660} y={346} size={30} color={muted}>WHAT RELAY DOES</Text>
|
|
57
|
+
{[380, 490, 660, 780].map(y => <line key={y} x1={112} x2={1808} y1={y} y2={y} stroke={line} strokeWidth={2}/>)}
|
|
58
|
+
<Text x={112} y={447} size={40} weight={600}>2xx</Text>
|
|
59
|
+
<Text x={660} y={447} size={38}>Marks the job delivered.</Text>
|
|
60
|
+
<Text x={112} y={555} size={40} weight={600}>Timeout or 5xx</Text>
|
|
61
|
+
<Text x={660} y={552} size={38}>Schedules another attempt; three attempts total.</Text>
|
|
62
|
+
<Text x={660} y={609} size={32} color={muted}>Third unsuccessful attempt: failed, for human investigation.</Text>
|
|
63
|
+
<Text x={112} y={731} size={40} weight={600}>4xx</Text>
|
|
64
|
+
<Text x={660} y={731} size={38}>Terminal in this example.</Text>
|
|
65
|
+
<Text x={112} y={865} size={38} weight={600}>A worker crash can also repeat a request</Text>
|
|
66
|
+
<Text x={112} y={925} size={34}>After lease expiry, another worker can claim the unfinished job,</Text>
|
|
67
|
+
<Text x={112} y={975} size={34}>even if the customer already received the earlier request.</Text>
|
|
68
|
+
</Frame>;
|
|
69
|
+
}
|
|
70
|
+
function Duplicate() {
|
|
71
|
+
return <Frame page={3} title="A timeout can hide a completed update" description="evt-204 reaches the customer and applies its update, but the response is lost. Relay times out and retries the same event ID. The customer recognizes evt-204 and skips the repeated update. This deduplication depends on the customer implementation. Relay cannot enforce it, make customer side effects atomic, or guarantee exactly-once delivery.">
|
|
72
|
+
<Text x={112} y={224} color={muted}>The same event ID connects both requests: evt-204.</Text>
|
|
73
|
+
<Text x={260} y={334} size={40} weight={600}>Relay worker</Text>
|
|
74
|
+
<Text x={1130} y={334} size={40} weight={600}>Customer endpoint</Text>
|
|
75
|
+
<line x1={380} x2={380} y1={367} y2={785} stroke={line} strokeWidth={2}/>
|
|
76
|
+
<line x1={1260} x2={1260} y1={367} y2={785} stroke={line} strokeWidth={2}/>
|
|
77
|
+
<Arrow x1={380} x2={1260} y={432}/>
|
|
78
|
+
<Text x={610} y={406} size={32}>POST evt-204</Text>
|
|
79
|
+
<Text x={1310} y={445} size={34} weight={500}>Applies update</Text>
|
|
80
|
+
<path d="M1260 525 H760" fill="none" stroke={accent} strokeWidth={3} strokeDasharray="10 8"/>
|
|
81
|
+
<path d="M746 511 L774 539 M774 511 L746 539" stroke={accent} strokeWidth={3}/>
|
|
82
|
+
<Text x={835} y={504} size={32}>Response lost</Text>
|
|
83
|
+
<Text x={112} y={561} size={32}>Times out</Text>
|
|
84
|
+
<Text x={112} y={603} size={30} color={muted}>Outcome unknown</Text>
|
|
85
|
+
<Arrow x1={380} x2={1260} y={663}/>
|
|
86
|
+
<Text x={610} y={637} size={32}>Retry POST evt-204</Text>
|
|
87
|
+
<Text x={1310} y={672} size={34} weight={500}>Recognizes evt-204</Text>
|
|
88
|
+
<Text x={1310} y={720} size={34} color={accent} weight={500}>Skips repeated update</Text>
|
|
89
|
+
<Text x={112} y={861} size={40} weight={600}>Customers must handle repeated event IDs.</Text>
|
|
90
|
+
<Text x={112} y={924} size={34}>The skip above depends on customer code; Relay does not enforce it.</Text>
|
|
91
|
+
<Text x={112} y={974} size={34}>Relay cannot make customer side effects atomic or guarantee exactly-once delivery.</Text>
|
|
92
|
+
</Frame>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export const meta = { title: 'Parcel Relay', createdAt: '2026-09-12T14:14:00Z' };
|
|
96
|
+
export const notes = [
|
|
97
|
+
'Audience question: When is an event accepted, and where does the job live? The connector from the database to the worker denotes job ownership, not a separate message broker.',
|
|
98
|
+
'Audience question: What happens after a delivery attempt, or a worker crash? No backoff duration or lease duration is specified by the fictional brief.',
|
|
99
|
+
'Audience question: Why did evt-204 repeat, and who prevents duplicate side effects? The customer deduplication outcome is an example, not a guarantee supplied by Relay.',
|
|
100
|
+
];
|
|
101
|
+
const pages = [Acceptance, Outcomes, Duplicate];
|
|
102
|
+
export default pages;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Turn the article below into a two-page briefing for engineering managers.
|
|
2
|
+
Explain its main argument and a practical way to apply it without turning
|
|
3
|
+
the author's experience into a universal rule.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
This is the complete fictional article, written for this example.
|
|
8
|
+
|
|
9
|
+
“The decision record that survived the meeting”
|
|
10
|
+
By Mira Sol, an engineer at the fictional company Alder
|
|
11
|
+
|
|
12
|
+
Our meeting notes were detailed, but a month later we could not explain why
|
|
13
|
+
we had chosen a daily data export over a live integration. The notes recorded
|
|
14
|
+
who spoke and which tasks followed. They did not record the constraint that
|
|
15
|
+
made the decision sensible: the receiving system accepted only one upload
|
|
16
|
+
per day.
|
|
17
|
+
|
|
18
|
+
We started keeping a short decision record alongside the work. It contains
|
|
19
|
+
the question, the constraints known at the time, the alternatives considered,
|
|
20
|
+
the choice and the condition that would make us revisit it. For the export,
|
|
21
|
+
that condition was the receiving system adding a supported live API.
|
|
22
|
+
|
|
23
|
+
This is not an attempt to predict every future requirement. It gives the next
|
|
24
|
+
engineer enough context to distinguish a deliberate trade-off from an accident.
|
|
25
|
+
When circumstances change, we append a new decision and link the old one
|
|
26
|
+
rather than silently rewriting its rationale.
|
|
27
|
+
|
|
28
|
+
Not every implementation detail deserves a record. We use one when a choice
|
|
29
|
+
crosses team boundaries, is costly to reverse or depends on a constraint likely
|
|
30
|
+
to change. Routine local choices can stay in code review. We still need task
|
|
31
|
+
lists and meeting notes; they answer different questions.
|
|
32
|
+
|
|
33
|
+
We have not measured whether this practice makes delivery faster. Our claim
|
|
34
|
+
is narrower: recording the reason and revisit condition makes our decisions
|
|
35
|
+
easier to explain later.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import { Text } from '../../lib/text.tsx';
|
|
3
|
+
import { fontsReady } from '../../lib/typeface.ts';
|
|
4
|
+
|
|
5
|
+
await fontsReady;
|
|
6
|
+
|
|
7
|
+
const ink = '#182B35';
|
|
8
|
+
const muted = '#4C606B';
|
|
9
|
+
const accent = '#006D77';
|
|
10
|
+
|
|
11
|
+
function Copy({ x = 112, y, width = 1696, size = 38, weight = 400, color = ink, children }: {
|
|
12
|
+
x?: number; y: number; width?: number; size?: number; weight?: number; color?: string; children: string;
|
|
13
|
+
}) {
|
|
14
|
+
return <Text x={x} y={y} width={width} size={size} weight={weight} color={color} maxLines={8} leading={1.22}>{children}</Text>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function Canvas({ page, title, description, children }: { page: number; title: string; description: string; children: ReactNode }) {
|
|
18
|
+
return <svg xmlns="http://www.w3.org/2000/svg" width="1920" height="1080" viewBox="0 0 1920 1080" role="img"
|
|
19
|
+
aria-labelledby={`title-${page} description-${page}`} data-page={page} fontFamily="IBM Plex Sans">
|
|
20
|
+
<title id={`title-${page}`}>{title}</title>
|
|
21
|
+
<desc id={`description-${page}`}>{description}</desc>
|
|
22
|
+
<rect width="1920" height="1080" fill="#FFFFFF" />
|
|
23
|
+
<Copy y={146} size={72} weight={600}>{title}</Copy>
|
|
24
|
+
{children}
|
|
25
|
+
<text x="1808" y="1020" textAnchor="end" fill={muted} fontSize="28">{page} / 2</text>
|
|
26
|
+
</svg>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const Argument = () => <Canvas page={1} title="Keep the reason with the decision"
|
|
30
|
+
description="In Mira Sol’s fictional Alder account, meeting notes preserved speakers and tasks but lost why a daily export was chosen. The receiving system allowed only one upload per day. Alder’s decision record preserved that constraint and a revisit trigger: a supported live API. Sol claims decisions became easier to explain, not measured faster delivery.">
|
|
31
|
+
<Copy y={224} size={32} color={muted}>Mira Sol’s “The decision record that survived the meeting” · A fictional Alder account</Copy>
|
|
32
|
+
<Copy y={354} width={560} size={48} weight={600}>A month later, the “why” was missing.</Copy>
|
|
33
|
+
<Copy y={508} width={550} color={muted}>Meeting notes captured who spoke and what to do next. They lost the constraint behind the export choice.</Copy>
|
|
34
|
+
<Copy x={800} y={350} width={1008} size={30} weight={600} color={accent}>ALDER’S EXPORT DECISION</Copy>
|
|
35
|
+
<line x1="800" x2="1808" y1="388" y2="388" stroke="#A5B4BC" strokeWidth="2" />
|
|
36
|
+
<Copy x={800} y={450} width={260} size={34} weight={600}>Choice</Copy>
|
|
37
|
+
<Copy x={1100} y={450} width={708} size={38}>Daily data export over a live integration</Copy>
|
|
38
|
+
<line x1="800" x2="1808" y1="532" y2="532" stroke="#A5B4BC" strokeWidth="2" />
|
|
39
|
+
<Copy x={800} y={594} width={260} size={34} weight={600}>Reason</Copy>
|
|
40
|
+
<Copy x={1100} y={594} width={708} size={38}>Receiving system accepted only one upload per day</Copy>
|
|
41
|
+
<line x1="800" x2="1808" y1="676" y2="676" stroke="#A5B4BC" strokeWidth="2" />
|
|
42
|
+
<Copy x={800} y={738} width={260} size={34} weight={600}>Revisit when</Copy>
|
|
43
|
+
<Copy x={1100} y={738} width={708} size={38}>Receiving system adds a supported live API</Copy>
|
|
44
|
+
<line x1="800" x2="1808" y1="820" y2="820" stroke="#A5B4BC" strokeWidth="2" />
|
|
45
|
+
<Copy y={922} size={40} weight={500} color={accent}>The claim: decisions became easier to explain later.</Copy>
|
|
46
|
+
<Copy y={975} size={32} color={muted}>Sol reports no measurement of faster delivery; this is one team’s experience.</Copy>
|
|
47
|
+
</Canvas>;
|
|
48
|
+
|
|
49
|
+
const Application = () => <Canvas page={2} title="Try a record on one consequential choice"
|
|
50
|
+
description="Suggested application for engineering managers, adapted from Sol’s fictional account: choose a decision that crosses teams, is costly to reverse, or rests on a changing constraint. Keep a short record beside the work with question, constraints, alternatives, choice, and revisit condition. If Alder gains a supported live API, reassess the export; append and link a new decision rather than overwrite the old rationale. Routine local choices can stay in code review. Keep task lists and meeting notes for their separate purposes.">
|
|
51
|
+
<Copy y={224} size={32} color={muted}>A suggested first trial, adapted from Sol’s practice—not a requirement for every team.</Copy>
|
|
52
|
+
<Copy y={350} width={620} size={44} weight={600}>Choose selectively</Copy>
|
|
53
|
+
<Copy y={420} width={620} size={36}>Use a record when a choice:</Copy>
|
|
54
|
+
<Copy y={490} width={620} size={38}>• Crosses team boundaries</Copy>
|
|
55
|
+
<Copy y={552} width={620} size={38}>• Is costly to reverse</Copy>
|
|
56
|
+
<Copy y={614} width={620} size={38}>• Relies on a constraint likely to change</Copy>
|
|
57
|
+
<Copy y={780} width={610} size={34} color={muted}>Routine local choices can stay in code review. Keep task lists and meeting notes; they answer different questions.</Copy>
|
|
58
|
+
<Copy x={860} y={350} width={948} size={44} weight={600}>Keep a short record beside the work</Copy>
|
|
59
|
+
<Copy x={860} y={423} width={948} size={36}>Question · Known constraints · Alternatives</Copy>
|
|
60
|
+
<Copy x={860} y={477} width={948} size={36}>Choice · Condition for revisiting</Copy>
|
|
61
|
+
<Copy x={860} y={600} width={948} size={44} weight={600} color={accent}>When Alder gains a supported live API…</Copy>
|
|
62
|
+
<Copy x={860} y={723} width={948} size={38}>Reassess whether the daily export still fits. Append a new decision and link the old one; preserve the original rationale.</Copy>
|
|
63
|
+
<Copy x={860} y={911} width={948} size={34} color={muted}>A manager’s review question: can the next engineer explain the trade-off and when to reconsider it?</Copy>
|
|
64
|
+
</Canvas>;
|
|
65
|
+
|
|
66
|
+
export const meta = { title: 'Alder — The reason behind the decision' };
|
|
67
|
+
export const notes = [
|
|
68
|
+
'Source: the complete fictional article in PROMPT.md by Mira Sol at Alder. This page summarizes the stated experience and preserves its explicit evidence limit.',
|
|
69
|
+
'The single-choice trial and manager’s review question are editorial applications, not quotations or measured outcomes. All five record fields and selective-use criteria come from the supplied article.',
|
|
70
|
+
];
|
|
71
|
+
export default [Argument, Application];
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Make a two-page briefing for the operations lead at Birch about processing time
|
|
2
|
+
across five workflows. Use grouped horizontal bars to compare before and after,
|
|
3
|
+
then explain the decision the evidence supports and what remains unknown.
|
|
4
|
+
|
|
5
|
+
Use one common zero-based minute axis, directly label every bar and keep the
|
|
6
|
+
before/after series order consistent. Make the workflow that became slower as
|
|
7
|
+
easy to notice as the improvements.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
Birch is a fictional internal operations team. These are synthetic, hand-authored
|
|
12
|
+
median active processing times per completed case. Waiting time is excluded.
|
|
13
|
+
Each workflow has 80 completed cases in the before period and 80 different
|
|
14
|
+
completed cases in the after period. Lower is better.
|
|
15
|
+
|
|
16
|
+
Workflow | Before median (minutes) | After median (minutes)
|
|
17
|
+
Account setup | 18 | 12
|
|
18
|
+
Address correction | 11 | 10
|
|
19
|
+
Refund review | 25 | 19
|
|
20
|
+
Access removal | 7 | 8
|
|
21
|
+
Supplier onboarding | 32 | 24
|
|
22
|
+
|
|
23
|
+
The before and after periods each lasted two weeks. A revised work queue was
|
|
24
|
+
introduced between them. Cases were not randomized or matched, and case
|
|
25
|
+
complexity and staffing could differ. These are independent groups, not paired
|
|
26
|
+
measurements of the same cases. No raw times, variability estimates or quality
|
|
27
|
+
measurements are supplied.
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
Four workflows have lower observed medians; access removal rises from 7 to 8
|
|
32
|
+
minutes. Supplier onboarding has the largest absolute median reduction, eight
|
|
33
|
+
minutes, but still has the highest after-period median at 24 minutes.
|
|
34
|
+
|
|
35
|
+
Recommend investigating the access-removal increase and checking case mix and
|
|
36
|
+
quality before extending the revised queue. The data do not establish that the
|
|
37
|
+
queue caused any change. Do not multiply median differences by case counts to
|
|
38
|
+
claim total time or money saved, or imply that a lower median means every case
|
|
39
|
+
became faster.
|