Repository navigation
feat(api): give every project its own sponsorship budget, keyed by environment - #2580
multipletwigs wants to merge 8 commits into
Conversation
…hema Delete organization-scope policies and their revisions, narrow the scope_type CHECK to global and project, and drop the organization clause from the tenant read policy. Breaking: deploy only after the two-scope API is live.
[Critical impact] The PR appears safe to merge under the documented pre-launch deployment plan. SummaryGives each project its own sponsorship budget and keys global budgets by SDP environment.
Previous decisions remain accepted:
Reviews (5) · Last reviewed commit: "test(api): read 0184 bigint columns as t..." · Reviewed by Greptile |
| CREATE DOMAIN sdp_lua_safe_lamports AS BIGINT | ||
| CHECK (VALUE BETWEEN 0 AND 9007199254740991); | ||
|
|
||
| CREATE TABLE sponsorship_budget_project_policies ( |
There was a problem hiding this comment.
Required archive fields are missing
Both new tables omit the required status column and its CHECK. This violates “Soft delete only” in the API section of docs/contributing/code-standards.md, which requires those fields on new tables.
Add them to sponsorship_budget_project_policies and sponsorship_budget_project_policy_revisions before merging.
File Used: docs/contributing/code-standards.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Not adding these; the soft-delete rule targets resource tables, and neither new table is one. sponsorship_budget_project_policies is one row per project that lives and dies with the project, and its enabled flag is the switch. sponsorship_budget_project_policy_revisions is an append-only log (a trigger denies UPDATE and DELETE), so a row has nothing to be archived from. This matches the existing sponsorship_budget_policies, sponsorship_budget_policy_revisions and sponsorship_reconciliation_state tables, none of which has a status column.
There was a problem hiding this comment.
You’re right; I over-applied the soft-delete convention. These are operational policy and append-only history tables, not API resources, and their lifecycle is already handled by the project relationship and enabled flag. The existing sponsorship policy/revision tables provide direct precedent. I’m withdrawing the finding.
Should I remember this distinction for future reviews: require status = 'archived' on resource tables, but not on operational policy or append-only history tables?
|
@greptileai please re-review: 0184 now marks its locks as blocking, with the reason, and the breaking-migration finding is answered in its thread. |
…vironment Brings the branch onto the current base and applies the restructure: one global policy per SDP environment, one policy per project written by a trigger on project insert, budgets/reservations/Redis keyed by sandbox|production, status reporting projects without a policy, and tests for all of it.
A recreated project policy restarts at version 1; keyed by project, its Redis control entry kept the old row's higher version and forward-only syncs never caught up, so admission would refuse the project as a stale policy. Keyed by the row id, a new row gets a new entry.
8a3e0d6 to
9a0c6de
Compare
SDP has no users, no mainnet sponsorship has run, and the production database is reset before launch, so a brief sponsorship refusal and the loss of pre-launch revision history beat a four-step expand/contract. Not a precedent after launch.
…l int8 parser Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QhoeJoQRZxzdNUMVTZXeoU
|
backoffice solves this |
Stacked on #2579. Gives every project its own sponsorship budget row and keys budgets by SDP environment (
sandbox,production) instead of network names. Migration 0184 drops the organization scope and the NULL project default.sdp:migration-compat: breaking), in one step on purpose. SDP has zero users, mainnet sponsorship has never run, and the prod DB is reset before launch. So we accept a few minutes of refused sponsorship while 0184 runs (feat(api): budget sponsorship per project and run the operator CLI as a job #2579's budget queries fail closed) and the loss of pre-launch org/project revision history, instead of a four-PR expand/contract. Merge only once feat(api): budget sponsorship per project and run the operator CLI as a job #2579 is live in prod; never roll back past it. After launch, renames go expand/contract.projects, at starting limits per environment, in the same transaction as the project. Tenants can't write budgets: RLS allows that insert only from inside the trigger.sdp_api_sponsorship_project_policy_missing); the breaker stays closed.statuslistsprojectsWithoutPolicyand exits 2.Final structure
sponsorship_budget_policiesenvironmentsponsorship_budget_project_policiesproject_id(FK)…_revisions(one per policy table)sponsorship_budget_reservationsHow a sponsorship is checked
max_allowed_lamports) must fit both per-transaction limits.globalandproject:<id>. Over:RATE_LIMITED, released.charged_unknownif unprovable.Verification: typecheck 28/28, biome clean. Migrated a fresh DB and the local dev DB (55/55 projects got rows, override carried, 2 global rows). CLI end to end: preview, apply, stale-version refusal, kill, resume, missing-row detection. Trigger and RLS probed as a non-bypass role. Transfer admission not exercised locally (local API is
self_hosted, which skips budgets); covered by the service suite. Vitest not run locally.