The question
Should the database refuse a scoped_budgets.scope_type or a budgets.reset_alignment value that is outside its vocabulary?
Where things stand
#1417 gives both vocabularies one home, ScopeType and ResetAlignment in src/gateway/models/budgets.py, with the columns they name. The request models are typed with them, so the API refuses an unknown value with a 422. Nothing in the schema does. Both columns are plain strings with no CHECK constraint, unlike ck_budgets_single_period_source on the same budgets table.
So a row written by anything other than the API (a migration, a restore, a direct SQL write, the hosted overlay) can hold a value this build does not know. The code is written to survive that: the response models keep str | None so such a row still reads back, the period roll logs and leaves the window in place, and scope resolution treats an unknown scope type as owned by nobody, which refuses instead of leaking. It fails closed everywhere it was checked.
Why this needs a decision and not just a migration
The unconstrained column is deliberate. The ScopedBudget docstring says scope_type "is a plain string rather than a database enum so a new scope needs no enum migration". A CHECK constraint built from SCOPE_TYPES brings that cost back in a different form: adding a scope would again need a migration, to widen the constraint, and the migration would have to land before any row uses the new value, in this repository and in the overlay that shares the table.
Two coherent answers:
- Keep it as it is. The API boundary is the guard, the code tolerates what gets past it, and adding a scope stays a code-only change. Record that in the column's comment so the next reader does not file this again.
- Add the constraints. The schema then states what the models module now states in Python, and a bad write fails loudly at the write and not quietly at the next read. The cost is one migration per vocabulary change, coordinated with the overlay.
reset_alignment and scope_type do not have to get the same answer. The alignment vocabulary has been three values since it was introduced and a bad one is harder to tolerate (no window can be derived from it), so it is the stronger candidate.
Relevant issues
Follow-up from #1202, raised in review of #1417. Related: #1416, which moves the reservation status names beside their column and meets the same question for budget_reservations.status.
The question
Should the database refuse a
scoped_budgets.scope_typeor abudgets.reset_alignmentvalue that is outside its vocabulary?Where things stand
#1417 gives both vocabularies one home,
ScopeTypeandResetAlignmentinsrc/gateway/models/budgets.py, with the columns they name. The request models are typed with them, so the API refuses an unknown value with a 422. Nothing in the schema does. Both columns are plain strings with no CHECK constraint, unlikeck_budgets_single_period_sourceon the samebudgetstable.So a row written by anything other than the API (a migration, a restore, a direct SQL write, the hosted overlay) can hold a value this build does not know. The code is written to survive that: the response models keep
str | Noneso such a row still reads back, the period roll logs and leaves the window in place, and scope resolution treats an unknown scope type as owned by nobody, which refuses instead of leaking. It fails closed everywhere it was checked.Why this needs a decision and not just a migration
The unconstrained column is deliberate. The
ScopedBudgetdocstring saysscope_type"is a plain string rather than a database enum so a new scope needs no enum migration". A CHECK constraint built fromSCOPE_TYPESbrings that cost back in a different form: adding a scope would again need a migration, to widen the constraint, and the migration would have to land before any row uses the new value, in this repository and in the overlay that shares the table.Two coherent answers:
reset_alignmentandscope_typedo not have to get the same answer. The alignment vocabulary has been three values since it was introduced and a bad one is harder to tolerate (no window can be derived from it), so it is the stronger candidate.Relevant issues
Follow-up from #1202, raised in review of #1417. Related: #1416, which moves the reservation status names beside their column and meets the same question for
budget_reservations.status.