Add real-world specimens and cross-links between sins

Two new levers on a sin page. `wild[]` renders credited, linked
specimens between the explanation and the citations — first the
argument, then proof people actually publish this, then the sources.
`related[]` cross-links neighbouring sins and is resolved at build, so a
typo fails the build instead of shipping a dead link.

Both arrive with the first specimen: a Hungarian primary graphic whose
three slices read 38/22/19 — 79% — drawn as a full circle, so every
share is inflated by about 1.27x. Measured off the image, the 38% slice
occupies 173.6 degrees, or 48.3% of the pie.

Our published pie sin is about too many slices, so it didn't cover this;
"Pie Slices That Don't Sum to the Whole" is a new severity-5 page whose
before/after is two pies differing only in whether the remainder is
present. That sin is an omission, so `compareNote` replaces the standard
"same numbers, both charts" line.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ULE5RRxdQE1ebwefEd2eCM
This commit is contained in:
Claude
2026-08-05 20:45:23 +00:00
parent 444b89660f
commit 673f265398
17 changed files with 453 additions and 15 deletions
+23
View File
@@ -27,6 +27,29 @@ const sins = defineCollection({
// Basenames of Vega-Lite specs in src/charts/ (without .json).
badChart: z.string(),
fixedChart: z.string(),
// Overrides the line under the before/after pair. Default asserts the two
// charts share one dataset; set it when the sin *is* an omission and that
// wording would be wrong.
compareNote: z.string().optional(),
// Real specimens of this sin, published by someone else and found in the
// wild. Zero, one or many. Rendered above the citations. Always credit the
// original and link where it was collected when we can.
wild: z
.array(
z.object({
caption: z.string(),
note: z.string(),
image: z.string().optional(),
alt: z.string().optional(),
credit: z.string(),
sourceUrl: z.string().optional(),
sourceLabel: z.string().optional(),
}),
)
.default([]),
// Slugs of other sins worth reading next — a chart usually commits more
// than one. Validated at build; an unknown slug throws.
related: z.array(z.string()).default([]),
// Optional publish date; unset entries sort last.
date: z.coerce.date().optional(),
draft: z.boolean().default(false),