Skip to content

refactor(budgets): move the reservation status names beside the column they name - #1453

Merged
peteski22 merged 5 commits into
mainfrom
refactor/reservation-status-vocabulary
Sep 21, 2026
Merged

peteski22 merged 5 commits into
mainfrom
refactor/reservation-status-vocabulary

Conversation

@peteski22

Copy link
Copy Markdown
Contributor

Description

Nothing changes for someone using Otari. This is an internal tidy-up in the budgets domain, and a follow-up from #1202.

A budget reservation has a status: active, settled, released or expired. The names for those four values lived in a service module, while the database column that stores them lives in the models module. The column's own comment pointed at the service for its meaning. #1202 settled that a closed vocabulary lives in the models module, beside the column it names. The scope types and reset alignments already moved there. The reservation statuses were the last budgets vocabulary outside that rule.

The statuses now take the same shape as the other two: a Literal, a tuple derived from it, and named constants that mypy proves are members. Every importer reads them from the models module, and the ledger does not re-export them. Two small follow-ons use the new type: the function that ends a reservation accepts only a status from the vocabulary, and the column's default reads the named constant where it spelled the string.

The statuses are not part of the published API, so nothing moves on the wire. The stored values and the table definition are unchanged.

How to test it locally

There is no visible behavior to look at. The automated checks carry the proof:

  • uv run pytest tests/unit/test_budget_vocabularies.py: the new test asserts that the named constants cover the Literal.
  • uv run pytest tests/integration/test_budget_reservation_ledger.py: the ledger lifecycle, unchanged, against the moved names.
  • uv run pytest tests/integration/test_schema_metadata_parity.py: the column default still matches the migrated schema.
  • make lint && make typecheck
  • make openapi-check && make postman-check, and the web/src/client/schema.ts drift check: all three show no diff.

Full unit and integration suites ran green locally, apart from test_mcp_dependency_ceiling, which needs network access and is known local noise.

PR Type

  • New Feature
  • Bug Fix
  • Refactor
  • Documentation
  • Infrastructure / CI

Relevant issues

Closes #1416. Follow-up from #1202.

Checklist

  • I understand the code I am submitting.
  • I have added or updated tests that cover my change (tests/unit, tests/integration).
  • I ran the Definition of Done checks locally (make lint, make typecheck, make test).
  • Documentation was updated where necessary.
  • If the API contract changed, I regenerated the OpenAPI spec (uv run python scripts/generate_openapi.py).
  • If this changes a rule in ARCHITECTURE.md or scripts/check_architecture.py, the description names the rule and says why.

AI Usage

  • No AI was used.
  • AI was used for drafting/refactoring.
  • This is fully AI-generated.

AI Model/Tool used:

Claude, via Claude Code.

Any additional AI details you'd like to share:

This was shared work. I wrote the issue, which sets the shape of the change: where the names live, the Literal form they take, and that the ledger does not re-export them. I also set the rules the work ran under: one small commit per step, and any comment the change touches is rewritten to the project's comment standard. Claude carried out the move under that direction, ran the checks, and drafted this description. I reviewed the commits and every touched comment before anything was pushed. Claude raised two optional follow-ons during that review, typing the status parameter and using the constant for the column default, and I decided to include both.

NOTE:
When responding to reviewer questions, please respond yourself rather than copy/pasting reviewer comments into an AI and pasting back its answer. We want to discuss with you, not your AI :)

  • I am an AI Agent filling out this form (check box if true)

…column

A closed vocabulary lives in the domain's models module, beside the string
column it names. ScopeType and ResetAlignment are there. The reservation
statuses were the last budgets vocabulary defined in a service module, so
the BudgetReservation.status column depended on a service for its meaning.

The statuses take the same shape as the other two: a Literal, a tuple
derived from it, and each constant annotated with the Literal so mypy
proves membership. A unit test asserts that the constants cover the Literal.

The ledger imports the names and still re-exports them, so its importers
keep working until the next commit repoints them.
…dule

The settlement path and the ledger tests read the status names through the
ledger module. They now import them from models/budgets.py, where the names
are defined, and the ledger no longer re-exports them. One import path per
name keeps a second definition from growing back.
…dgets.py

The budgets entry said the statuses still sat in the ledger module and would
move. They moved, so the entry lists all three vocabularies in one place.
try_terminate took the new status as a bare string, so a misspelled status
reached the database unchecked. The parameter is now ReservationStatus, and
mypy rejects a value outside the vocabulary at every call site.
…tant

The status column spelled "active" twice beside the constant that names it.
Both defaults now read RESERVATION_ACTIVE, so the column cannot drift from
the vocabulary. The stored value and the DDL are unchanged.
@peteski22
peteski22 deployed to integration-tests September 21, 2026 18:02 — with GitHub Actions Active
@peteski22
peteski22 deployed to integration-tests September 21, 2026 18:02 — with GitHub Actions Active
@peteski22
peteski22 deployed to integration-tests September 21, 2026 18:02 — with GitHub Actions Active
@peteski22
peteski22 deployed to integration-tests September 21, 2026 18:02 — with GitHub Actions Active
@coderabbitai

coderabbitai Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 56 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Repository: mozilla-ai/otari/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: c02a7f6f-1352-4ae8-ac94-cd7ac64a8731

📥 Commits

Reviewing files that changed from the base of the PR and between 1948278 and cfc8e27.

📒 Files selected for processing (6)
  • docs/domains.md
  • src/gateway/models/budgets.py
  • src/gateway/services/budgets/_ledger.py
  • src/gateway/services/budgets/_reservations.py
  • tests/integration/test_budget_reservation_ledger.py
  • tests/unit/test_budget_vocabularies.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@peteski22
peteski22 marked this pull request as ready for review September 21, 2026 18:54
@peteski22
peteski22 requested a review from tbille September 21, 2026 18:54
@peteski22
peteski22 merged commit 1f19ccd into main Sep 21, 2026
17 checks passed
@peteski22
peteski22 deleted the refactor/reservation-status-vocabulary branch September 21, 2026 19:03

This branch was successfully deployed

1 active deployment
integration-tests — cfc8e27f Deployed Sep 21, 2026 by peteski22 via test-integration (1/4) #2383
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Move the reservation status names beside the column they name

2 participants