From d7ae10bb0e0eb8a62ed4ab518a7dc6dc51fba9ff Mon Sep 17 00:00:00 2001 From: Wessel Verheij Date: Wed, 29 Jul 2026 00:56:19 +0200 Subject: [PATCH] feat(recurring): close the two cadence vocabularies and separate them Two different closed sets are both called cadence, and both are carried as free strings. A series cadence is weekly, monthly, quarterly, yearly, or irregular. A digest cadence is daily, weekly, or off. They overlap on one value and on the word, and nothing anywhere says they are different things. C2 already described the five series outcomes in prose but never required the set to be closed, so the column that stores it is a bare string while the state column beside it carries a CHECK constraint built from its own allowed list. C8 refers to the user's chosen digest cadence without ever saying what may be chosen. C2-R20 and C8-R22 close both sets, require each to be one named type rather than free strings, require the stored column to be constrained, and state that the two are distinct and MUST NOT share a type. Spec: GOV-R13 Signed-off-by: Wessel Verheij --- 10-functional/features/c-insight/c2-recurring.md | 8 ++++++++ 10-functional/features/c-insight/c8-notifications.md | 1 + 2 files changed, 9 insertions(+) diff --git a/10-functional/features/c-insight/c2-recurring.md b/10-functional/features/c-insight/c2-recurring.md index 5b78390..08173c5 100644 --- a/10-functional/features/c-insight/c2-recurring.md +++ b/10-functional/features/c-insight/c2-recurring.md @@ -39,6 +39,13 @@ low-confidence. The next expected date is derived from the inferred cadence. +The five outcomes — weekly, monthly, quarterly, yearly, irregular — are the +whole vocabulary. Nothing else is a cadence a series can hold, and a value +outside the set is a defect rather than a new case to handle, so the set is +named once and both the code and the column are held to it. The digest cadence +in [C8](c8-notifications.md) is a different vocabulary that happens to share +the word and the value `weekly`; the two are not interchangeable. + ### Metrics refresh, cadence flips, and re-detection A new occurrence on an approved series refreshes its metrics without creating a @@ -124,6 +131,7 @@ append-only audit trail. Transitions take a row lock with a busy timeout. | **C2-R17** | Approve, reject, un-reject, and snooze MUST each raise exactly one event. | | **C2-R18** | External reads of series occurrences MUST go through the feature's own public query surface. | | **C2-R19** | Cross-user reads and writes MUST return not-found. | +| **C2-R20** | The series cadence vocabulary MUST be closed to weekly, monthly, quarterly, yearly, and irregular; it MUST be expressed as one named type rather than as free strings, and the stored column MUST be constrained to that set. | ## Related diff --git a/10-functional/features/c-insight/c8-notifications.md b/10-functional/features/c-insight/c8-notifications.md index 5e2c4db..0a531ac 100644 --- a/10-functional/features/c-insight/c8-notifications.md +++ b/10-functional/features/c-insight/c8-notifications.md @@ -142,6 +142,7 @@ in the application and by database trigger. | **C8-R19** | The pruning predicate MUST use only plaintext columns so it can run without the at-rest key. | | **C8-R20** | Notification content MUST be treated as sensitive and encrypted at rest alongside other identifying text. | | **C8-R21** | Cross-user reads and writes MUST return not-found. | +| **C8-R22** | The digest cadence vocabulary MUST be closed to daily, weekly, and off; it MUST be expressed as one named type rather than as free strings, and the stored column MUST be constrained to that set. It is distinct from the series cadence in [C2](c2-recurring.md) and MUST NOT share a type with it. | ## Related