- Migration 025: ADD COLUMN budget_limit BIGINT DEFAULT NULL and
monthly_spend BIGINT NOT NULL DEFAULT 0 to workspaces table
- Models: BudgetLimit *int64 in CreateWorkspacePayload;
MonthlySpend int64 in HeartbeatPayload
- workspace.go: scanWorkspaceRow, workspaceListQuery, Get, Create, and
Update all handle budget_limit/monthly_spend; budget_limit is gated
as a sensitiveUpdateField
- registry.go: heartbeat conditionally writes monthly_spend only when
payload.MonthlySpend > 0 (avoids overwriting with zero)
- a2a_proxy.go: checkWorkspaceBudget() returns 429 when
monthly_spend >= budget_limit (NULL = no limit; fail-open on DB error)
- Tests: 8 new workspace_budget_test.go tests + patched existing tests
for the 20-column scanWorkspaceRow and 10-param CREATE INSERT
Field type: BIGINT (int64), units: USD cents (budget_limit=500 = $5.00/month)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
`CreateWorkspacePayload` was missing a `Secrets` field, so any
`secrets: { KEY: value }` included in a POST /workspaces body was
silently dropped by ShouldBindJSON.
Changes:
- Add `Secrets map[string]string` field to `CreateWorkspacePayload`
- Wrap workspace INSERT in a DB transaction; iterate over secrets,
encrypt each value via `crypto.Encrypt`, and upsert into
`workspace_secrets` within the same tx — rollback both on any failure
- Add `mock.ExpectBegin()`/`mock.ExpectCommit()`/`mock.ExpectRollback()`
to all existing Create tests that were missing transaction expectations
- Add 3 new tests: WithSecrets_Persists, SecretPersistFails_RollsBack,
EmptySecrets_OK
Closes#545
Co-authored-by: Molecule AI Backend Engineer <backend-engineer@agents.moleculesai.app>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>