Found while implementing #61.
duly_duty has recurring_needs_frequency (a script validation refusing form == "recurring" && isBlank(frequency)). duly_catalog_item has never had an equivalent rule — before #61 this was mostly masked by frequency's unconditional default: true option marker always filling in 'monthly' on insert, but the gap is real: an UPDATE that explicitly blanks a recurring catalog item's frequency (e.g. { frequency: null }) has nothing to refuse it, since applyFieldDefaults only runs on INSERT.
This matters more than a bare schema gap because of applyCatalogHandler (src/actions/catalog.handlers.ts): it copies frequency (among the other cadence fields) from the catalog item onto every new duly_dutyverbatim. A recurring catalog item quietly left with a blank frequency would replicate that blank onto every person who takes the role — each of whom would then individually trip duly_duty's own recurring_needs_frequency on their first save, rather than the org catching the problem once, at the source, on the catalog item itself.
Not fixed as part of #61 — that issue's adjudicated scope was specifically the standing-duty-carries-a-frequency defect (the converse direction), not this pre-existing, independent "recurring requires a frequency" gap on the catalog object. Also worth deciding at the same time: duly_catalog_item has no effective_*-window equivalent either (N/A — it has no effective_from/effective_to fields at all), so this issue is scoped to recurring_needs_frequency only.
Suggested fix
Add a recurring_needs_frequency validation to duly_catalog_item (src/objects/catalog-item.object.ts), mirroring duly_duty's wording, alongside the standing_no_frequency / non_recurring_no_due_timing / standing_no_grace_days rules #61 adds there.
Found while implementing #61.
duly_dutyhasrecurring_needs_frequency(a script validation refusingform == "recurring" && isBlank(frequency)).duly_catalog_itemhas never had an equivalent rule — before #61 this was mostly masked byfrequency's unconditionaldefault: trueoption marker always filling in'monthly'on insert, but the gap is real: an UPDATE that explicitly blanks a recurring catalog item'sfrequency(e.g.{ frequency: null }) has nothing to refuse it, sinceapplyFieldDefaultsonly runs on INSERT.This matters more than a bare schema gap because of
applyCatalogHandler(src/actions/catalog.handlers.ts): it copiesfrequency(among the other cadence fields) from the catalog item onto every newduly_dutyverbatim. Arecurringcatalog item quietly left with a blank frequency would replicate that blank onto every person who takes the role — each of whom would then individually tripduly_duty's ownrecurring_needs_frequencyon their first save, rather than the org catching the problem once, at the source, on the catalog item itself.Not fixed as part of #61 — that issue's adjudicated scope was specifically the standing-duty-carries-a-frequency defect (the converse direction), not this pre-existing, independent "recurring requires a frequency" gap on the catalog object. Also worth deciding at the same time:
duly_catalog_itemhas noeffective_*-window equivalent either (N/A — it has noeffective_from/effective_tofields at all), so this issue is scoped torecurring_needs_frequencyonly.Suggested fix
Add a
recurring_needs_frequencyvalidation toduly_catalog_item(src/objects/catalog-item.object.ts), mirroringduly_duty's wording, alongside thestanding_no_frequency/non_recurring_no_due_timing/standing_no_grace_daysrules #61 adds there.