Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions docs/challenge-explainers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Challenge explainer direction

The 13 current challenges already have diagrams. A signature explainer should
make one consequential transition legible, not add a decorative second figure.
Public statements and harness contracts remain the source of truth. These
artworks are conceptual examples, never a claim about an official run or score.

## Visual grammar

- Ink `#282334` grounds mechanisms; paper `#f2f0ed` carries reading text.
- Orange `#ff784f` means an incoming operation or interruption. Lilac
`#b9a1ff` marks resident state, mint `#a9e8cb` preserved state, cyan
`#53d9ff` transit, and lime `#b7ff45` a confirmed outcome. These meanings
are reinforced with words and shape, not color alone.
- Bricolage Grotesque is for the one-line concept headline, Inter for the
explanation, and JetBrains Mono for keys, sequence numbers, bytes, and ticks.
- One large mechanism occupies the figure. No graph-paper wallpaper, generic
three-card sequence, looping sparkle, fake terminal output, or fictitious
performance result.

```text
concept headline operation / current state

[ one large mechanism ]

one sentence explaining the current consequence Replay
```

Keep the artwork left-aligned with the statement. Start its finite sequence
when it enters view, settle on a meaningful frame, and allow replay. A reduced-
motion preference shows the final explanatory state without motion. The full
sequence must be understandable through a static accessible description. If a
later explainer needs ongoing motion, add pause/stop controls rather than an
uncontrolled loop. A pre-rendered SVG/video is welcome when it conveys the
concept more clearly; it must still have a lightweight poster and transcript.

## One visual question per challenge

| Challenge | Moment to explain |
| --- | --- |
| Cache Pressure, easy | One read changes recency, so a different key is evicted. |
| Cache Pressure, medium | A cold scan displaces a hot set under pure LRU; admission protects reuse. |
| Chunk Store | A short read touches one independently framed block. |
| Deadline Scheduler | A running non-preemptive job delays a newly arrived urgent job. |
| Durable Work Queue | A lease expires and the same job becomes available again. |
| Fair Gate | Tokens refill; an expensive request waits for the exact retry tick. |
| Journal Recovery | A crash leaves a durable prefix and an incomplete tail. |
| Lossy Link | A lost ACK triggers retry; duplicate data is acknowledged, not delivered twice. |
| LSM Compactor | An L0 choice closes over L1 overlaps before tombstone-aware merging. |
| Stream Windows | A watermark finalizes one window while the next remains open. |
| Compaction | Moving one live cell extends the contiguous free suffix. |
| Victim Selection | Choosing a block trades reclaimed space for live-page migration. |
| Wear Levelling | Spreading erases trades extra migration for lower wear concentration. |

## Review gate

For each artwork, compare every shown state with the public contract/example;
check the before/after story at phone width and desktop width; inspect a real
screenshot rather than relying on a build; test keyboard replay and reduced
motion; and keep the surrounding editorial to one observation, one invariant,
and one learner decision. The first pilots are Cache Pressure and Lossy Link.
10 changes: 9 additions & 1 deletion src/app/(workspace)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
import { Bricolage_Grotesque } from "next/font/google";

const editorial = Bricolage_Grotesque({
subsets: ["latin"],
display: "swap",
variable: "--font-editorial",
});

/**
* Chrome for the arena: none.
*
Expand All @@ -14,7 +22,7 @@ export default function WorkspaceLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
<div id="main" className="h-screen overflow-hidden">
<div id="main" className={`${editorial.variable} h-screen overflow-hidden`}>
{children}
</div>
);
Expand Down
10 changes: 8 additions & 2 deletions src/components/blocks/block-renderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ function renderCore(block: CoreBlock, context: BlockContext) {

case "figure":
return (
<Figure illustration={block.illustration} caption={block.caption} label={block.label} />
<Figure illustration={block.illustration} caption={block.caption} label={block.label} presentation={block.presentation} />
);

case "example":
Expand Down Expand Up @@ -143,10 +143,12 @@ function Figure({
illustration,
caption,
label,
presentation,
}: {
illustration: IllustrationSpec;
caption?: string;
label?: string;
presentation?: "immersive";
}) {
const drawing = getIllustration(illustration.id);
if (!drawing) {
Expand All @@ -155,7 +157,11 @@ function Figure({

return (
<figure className="space-y-2.5">
<div className="rounded-16 border border-[#d2e3eb] bg-[#f1f8fb] p-4 sm:p-5">
<div className={cn(
presentation === "immersive"
? "overflow-hidden rounded-[18px]"
: "rounded-16 border border-[#d2e3eb] bg-[#f1f8fb] p-4 sm:p-5",
)}>
{createElement(drawing, illustration.props ?? {})}
</div>
{(label || caption) && (
Expand Down
4 changes: 4 additions & 0 deletions src/lib/arena/statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ const conceptFigureProps = {
"systems.frames": frameProps,
"systems.ranges": rangeProps,
"systems.exchange": exchangeProps,
"systems.cache-story": z.object({ mode: z.enum(["lru", "scan"]) }).strict(),
"systems.lost-ack-story": z.object({}).strict(),
} as const;

const figureBlock = z.object({
Expand All @@ -81,11 +83,13 @@ const figureBlock = z.object({
"systems.flow", "systems.state", "systems.timeline",
"systems.snapshot", "systems.budget", "systems.frames",
"systems.ranges", "systems.exchange",
"systems.cache-story", "systems.lost-ack-story",
]),
props: z.record(z.string(), z.unknown()).optional(),
}).strict(),
caption: z.string().trim().min(1).max(1_000).optional(),
label: z.string().trim().min(1).max(40).optional(),
presentation: z.literal("immersive").optional(),
}).strict().superRefine((figure, ctx) => {
const schema = conceptFigureProps[figure.illustration.id as keyof typeof conceptFigureProps];
if (schema && !schema.safeParse(figure.illustration.props).success) {
Expand Down
2 changes: 2 additions & 0 deletions src/lib/blocks/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ export type CoreBlock =
readonly caption?: string;
/** Short label such as "FIG 1.2". */
readonly label?: string;
/** Lets an authored illustration own its full artwork surface. */
readonly presentation?: "immersive";
}
| {
readonly kind: "example";
Expand Down
3 changes: 3 additions & 0 deletions src/packs/systems/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { registerIllustration } from "../registry";
import { BudgetDiagram, ExchangeDiagram, FrameDiagram, RangeDiagram, SnapshotDiagram } from "./concept-illustrations";
import { FlowDiagram, StateDiagram, TimelineDiagram } from "./illustrations";
import { CacheStory, LostAckStory } from "./story-illustrations";

registerIllustration("systems.flow", FlowDiagram);
registerIllustration("systems.state", StateDiagram);
Expand All @@ -12,3 +13,5 @@ registerIllustration("systems.budget", BudgetDiagram);
registerIllustration("systems.frames", FrameDiagram);
registerIllustration("systems.ranges", RangeDiagram);
registerIllustration("systems.exchange", ExchangeDiagram);
registerIllustration("systems.cache-story", CacheStory);
registerIllustration("systems.lost-ack-story", LostAckStory);
Loading
Loading