From 787fda6971db2d1b7584e93c8afad3ee14cdb099 Mon Sep 17 00:00:00 2001 From: Wessel Verheij Date: Wed, 29 Jul 2026 14:22:54 +0200 Subject: [PATCH] feat(reports): close the report granularity vocabulary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report granularity is monthly or weekly and nothing else, but C7 never said so. The set exists only as a `'monthly' | 'weekly'` docblock repeated across five classes, and the value is stored inside a saved report's definition payload where no column constraint can reach it. This is the third vocabulary in the codebase that says `weekly`. The series cadence of C2-R20 and the digest cadence of C8-R22 are already closed and already required to be distinct from each other; granularity was left out of that pass. Sharing a type between any two of them would be wrong in a way that type-checks: a report is not on a cadence, and widening its buckets has nothing to do with how often a series recurs or how often a digest is sent. C7-R21 closes the set, requires one named type rather than free strings, requires a stored value outside the set to be rejected rather than defaulted, and states that the type MUST NOT be shared with either cadence. Quarterly is deliberately not in the vocabulary. The generator reaches it on its own when a range would exceed the point cap, but the user never picks it and a saved report never holds it — it is a widening outcome, not a choice. The prose says this explicitly, because the existing widening paragraph names quarterly and would otherwise read as if it were selectable. Spec: GOV-R13 Signed-off-by: Wessel Verheij --- 10-functional/features/c-insight/c7-reports.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/10-functional/features/c-insight/c7-reports.md b/10-functional/features/c-insight/c7-reports.md index ac52fb4..215a395 100644 --- a/10-functional/features/c-insight/c7-reports.md +++ b/10-functional/features/c-insight/c7-reports.md @@ -51,6 +51,18 @@ to monthly to quarterly — up to a maximum point count. It never silently truncates the range, because a chart showing half the period the user asked for is worse than a coarser chart showing all of it. +The granularity the user chooses is monthly or weekly, and nothing else. +Quarterly is a widening outcome the generator reaches on its own; it is never a +value the user picks and never one a saved report holds, so it is not part of +the vocabulary. A stored granularity outside the set is a defect rather than a +new case to handle, so the set is named once and the code is held to it. + +The series cadence in [C2](c2-recurring.md) and the digest cadence in +[C8](c8-notifications.md) also say `weekly`. All three are separate +vocabularies that happen to share a word and a value, and none of them may +share a type — a report is not on a cadence, and widening its buckets has +nothing to do with how often a series recurs or how often a digest is sent. + ### Currency modes are explicit In base mode, values convert and merge, and rows with no available rate are @@ -124,6 +136,7 @@ that would accept ambiguous input. An inverted range is rejected. | **C7-R18** | Every write action MUST guard ownership explicitly and MUST return not-found for a report the caller does not own. | | **C7-R19** | A chart segment MUST drill through to the transactions behind it, carrying the report's filters. | | **C7-R20** | Custom ranges MUST be parsed strictly, and an inverted range MUST be rejected. | +| **C7-R21** | The report granularity vocabulary MUST be closed to monthly and weekly; it MUST be expressed as one named type rather than as free strings, a stored value outside the set MUST be rejected rather than defaulted, and the type MUST NOT be shared with the series cadence of C2 or the digest cadence of C8. | ## Related @@ -131,3 +144,5 @@ that would accept ambiguous input. An inverted range is rejected. - [B9 Full-text search](../b-ledger/b9-search.md) — the drill-through target - [B10 Multi-currency](../b-ledger/b10-multi-currency.md) — the currency modes - [C1 Dashboard](c1-dashboard.md) — pinned cards +- [C2 Recurring detection](c2-recurring.md) — the series cadence, a separate vocabulary that also says weekly +- [C8 Notifications](c8-notifications.md) — the digest cadence, the third one