From 4c3aa4c7e37983147869f8b822cd5d7688a85271 Mon Sep 17 00:00:00 2001 From: Lucas Winther Date: Thu, 27 Aug 2026 16:55:03 +0200 Subject: [PATCH] Call it a cycle, not "every N days" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Every 26 days" is ordinary English for an interval, but it sits directly beneath a start and an end — a duration — and a reader who has just been thinking in durations reads it as another one. Naming the shape of the repetition is what separates them, and "cycle" is the word this genre already uses. A cycle of one unit is named rather than numbered, because nobody says "a 1-week cycle"; a longer one takes the singular unit, since a hyphenated "26-day" is an adjective and not a count. The article follows how the number sounds — an 8-day cycle, an 11-day, an 84-day — which is the kind of wrong that reads as sloppiness rather than as a bug. The manual control becomes "Cycle length" for the same reason: it is asking for the length of the cycle, not for a duration the reader already gave. Co-Authored-By: Claude Opus 5 (1M context) --- src/client/components/CustomForms.tsx | 38 +++++++++++++++++++++++---- test/custom-ui.test.tsx | 35 +++++++++++++++++++----- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/src/client/components/CustomForms.tsx b/src/client/components/CustomForms.tsx index 66a3e10..495b679 100644 --- a/src/client/components/CustomForms.tsx +++ b/src/client/components/CustomForms.tsx @@ -69,11 +69,39 @@ export function strandedNotice(count: number): string | null { } you've already recorded.`; } -/** How often a rule comes round, in the words the form offered. */ +/** Whether a number opens on a vowel sound when read aloud. */ +function takesAn(n: number): boolean { + // Eight, eleven and eighteen, plus every number in the eighties — which is + // still inside the 365 ceiling `interval` is capped at, so nothing larger + // needs considering. + return n === 8 || n === 11 || n === 18 || (n >= 80 && n <= 89); +} + +/** + * How often a rule comes round, in the words the form offered. + * + * "Cycle" rather than "every N days", because this sits directly beneath a + * start and an end — a duration — and a reader who has just been thinking in + * durations reads "every 26 days" as another one. Naming the shape of the + * repetition is what separates the two, and "cycle" is the word this genre + * already uses for it. + * + * A cycle of one unit is named rather than numbered: nobody says "a 1-week + * cycle". A longer one takes the singular unit, because a hyphenated + * "26-day" is an adjective, not a count. + */ export function cadenceLabel(repeat: Repeat | null): string | null { if (repeat === null) return null; - if (repeat.interval === 1) return `every ${repeat.unit.replace(/s$/, "")}`; - return `every ${repeat.interval} ${repeat.unit}`; + if (repeat.interval === 1) { + const named: Record = { + days: "daily", + weeks: "weekly", + months: "monthly", + }; + return `on a ${named[repeat.unit]} cycle`; + } + const article = takesAn(repeat.interval) ? "an" : "a"; + return `on ${article} ${repeat.interval}-${repeat.unit.replace(/s$/, "")} cycle`; } /** @@ -617,7 +645,7 @@ export function EventForm({ {repeatMode === "forever" && !measuring && (