feat(recurring): close the two cadence vocabularies and separate them - #5
Merged
Conversation
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 <info@nightworks.io>
lessevv
enabled auto-merge (squash)
July 28, 2026 22:56
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Why
Two different closed sets are both called "cadence", both are carried as free strings, and they overlap on the value
weekly. Nothing in the spec says they are different things, and nothing in the product stops a value outside either set from being written.The series cadence is already described in C2's prose — "snaps to a named cadence — weekly, monthly, quarterly, yearly — or declares the series irregular" — but never required to be closed. The consequence is visible in the schema: in the same migration that creates
recurring_series, thestatecolumn gets a CHECK constraint built from an explicit allowed list, whilecadencegets a barestring(24)with a default. Two closed vocabularies on one table, governed differently, with nothing in the spec to say the second one is closed at all.The digest cadence is referenced in C8 only as "the user's chosen cadence". What may be chosen is
daily,weekly, oroff, which appears nowhere except in the code that reads it.The overlap is the part worth being explicit about. Both sets contain
weekly, so a single shared type is an easy and wrong simplification: it would let a digest preference ofoffbe assigned to a series, and a series cadence ofquarterlybe assigned to a digest. C8-R22 says they must not share a type for that reason.What this enables
Implementation is a follow-up in
beatrax, citing these identifiers: a backed enum per vocabulary, and a CHECK constraint on each column matching howrecurring_series.stateis already governed.It also removes a cause rather than a symptom. The cadence literals appear a few hundred times across Recurring, Forecasting, Calendar, Notifications, DriftAlerts and Position, and the duplicated-literal findings that keep surfacing there are that absence of a type showing through.
Checklist
just integrity— spec integrity cleantyposclean on both changed filesGOV-R10) — C2 ends at R19, C8 at R21MUST, testable, and name the vocabulary explicitlySpec:trailer