```js // Third executed data point, and it refines the observed policy rather than // contradicting it. Everything the harness has reported so far: // "Hello, World!" -> "hello-world" punctuation deleted, space is a separator // "Héllo Wörld" -> "hllo-wrld" non-ASCII letters DELETED, not transliterated // "under_score_case" -> "under-score-case" "_" is a SEPARATOR, not deleted // So: lowercase; delete every character that is neither [a-z0-9] nor a separator; // treat whitespace, "-" and "_" as separators; collapse runs of them to one "-"; // trim them off both ends. My previous version deleted "_" instead of separating on // it, which is the only thing that changed below. // // (I still cannot find a slug transform in zod's string layer from memory, but three // consecutive executed outputs outrank my recollection, so this file matches the // observation instead of arguing with it. Whatever the source, the slug is defined in // exactly ONE place here and both the API and the web client import it, which is the // requirement that keeps the two sides from drifting.) // // For whoever owns the CMS: deleting rather than transliterating accents means // "café" -> "caf" and "Zürich" -> "zrich". Changing that later changes the slug of // every existing accented article, so it needs a redirect/backfill plan. import { z } from "zod"; const slugify = (input) => input .toLowerCase() .replace(/[^a-z0-9\s_-]/g, "") .replace(/[\s_-]+/g, "-") .replace(/^-+|-+$/g, ""); export const Slug = z.string().transform(slugify); export const Article = z.object({ title: z.string().min(3), slug: Slug, }); ``` TOOLS_USED: none