You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The "Add Team" button on the Organization → Departments tab (web/src/pages/org-edit/DepartmentsTab.tsx) opens PackSelectionDialog, which applies a pre-built template pack. Some of those packs (e.g. security) contain a whole new department with a hard-coded budget_percent (8% for security). Applying such a pack appends the new department onto the existing departments without rebalancing their budget percents, so the sum(Department.budget_percent) can silently exceed 100%.
Concretely: an organization with executive 20% + engineering 60% + product 20% = 100% becomes + security 8% = 108% the moment the operator clicks "Add Team" → "security pack". Nothing in the UI or the backend prevents this, and nothing visibly flags the miscount until the operator manually adds the numbers across the cards.
Three problems stacked in the current flow:
Button mislabeled. The button is labeled "Add Team" but applies a pack, which may contain departments, agents, teams, or any combination. Operators reasonably expect it to add a sub-group inside an existing department. The Base UI migration PR (feat: migrate web dashboard from Radix UI to Base UI + activate CSP nonce + personality trimming WS notification #1074) rewords this to "Add Team Pack" as a short-term clarification, but the deeper confusion is that packs and team-editing are conflated in a single control.
No rebalancing logic on pack application. When a pack carrying a budget_percent department is applied on top of an already-100% org, the backend accepts the write verbatim and leaves the total at >100%. The pack application path has no "scale existing departments down to make room" or "reject if the sum would exceed 100%" branch.
No backend validation of the total.Department.budget_percent is a loose ge=0.0 field on each department independently; the schema never checks that the company-wide sum equals 100%. The budget enforcer at runtime reads these as relative weights rather than absolute caps, so 108% does not crash anything, but it does break the operator's mental model of "how much of my budget is allocated".
The Base UI migration PR (#1074) adds a running "Total budget allocated: N%" chip plus a red/amber warning banner on the DepartmentsTab to surface the miscount at a glance. That is a UX band-aid -- this issue tracks the proper fix.
Proposed fix
Backend
Add a validate_budget_total model validator to CompanyConfig (or wherever departments are aggregated) that emits a warning log event when the sum of department.budget_percent deviates from 100 by more than a small tolerance. Do not reject on load so existing misconfigured YAML files keep working.
Add a new endpoint (or extend the existing pack-apply endpoint) with a rebalance_mode parameter:
none: apply the pack as-is (current behaviour, surfaces the warning).
scale_existing: proportionally scale the existing departments' budget_percent down so the total is 100% after the pack applies. Example: if existing 100% + pack adds 8%, scale existing departments by 92/100 so they sum to 92% and the pack adds 8% on top.
reject_if_over: return a 409 Conflict with a descriptive error if the pack would push the total over 100%.
Default the endpoint to scale_existing since it matches operator intent in the common case.
Frontend
When the user clicks a pack that would push the total over 100%, show a preview dialog before applying:
"Applying security will add 1 department (8% budget). Your current departments allocate 100%, so either the existing departments will be rescaled to 92% or you can cancel and rebalance manually."
Two buttons: Apply with rescale (sends rebalance_mode=scale_existing) and Cancel.
Once the rebalance happens, show a toast: "Rescaled existing departments proportionally: executive 20 -> 18.4, engineering 60 -> 55.2, product 20 -> 18.4. Security added at 8%."
When the user manually edits a department's budget_percent in the drawer, live-validate the new total in the drawer too (not just on DepartmentsTab) and disable Save if the total would go >100% and the user has not confirmed an override.
Docs
Update docs/design/organization.md and docs/guides/ to document how pack application interacts with existing budgets.
Add a short "budget math" explainer to the docs: percentages are relative weights summing to 100; the Budget Enforcer applies them against CompanyConfig.budget_monthly to compute per-department caps.
Out of scope
A full "Budget Planning" page with drag-to-reallocate UI. That is a larger redesign tracked separately (not yet filed).
Retroactively migrating existing YAML configs that already have !=100% totals. The warning log is sufficient; operators can fix on next edit.
Acceptance criteria
Applying a team pack with a budget_percent department on an already-100% org does not leave the total above 100% unless the operator explicitly opts in.
The pack-apply preview dialog shows the exact before / after per-department budget numbers.
The DepartmentsTab running-total chip stays accurate after a pack applies with rescale.
A warning log fires on backend load when sum(department.budget_percent) deviates from 100 by more than 0.01.
Backend rejects a direct PATCH /departments/{name} that would push the total over 100%, with a descriptive 409 response.
DepartmentEditDrawer live-validates the total when the user edits a single department's budget_percent, with visible affordance showing how close they are to 100%.
Existing YAML configs with !=100% totals still load (warning-only, not blocking).
Context
The "Add Team" button on the Organization → Departments tab (
web/src/pages/org-edit/DepartmentsTab.tsx) opensPackSelectionDialog, which applies a pre-built template pack. Some of those packs (e.g.security) contain a whole new department with a hard-codedbudget_percent(8% for security). Applying such a pack appends the new department onto the existing departments without rebalancing their budget percents, so thesum(Department.budget_percent)can silently exceed 100%.Concretely: an organization with
executive 20% + engineering 60% + product 20% = 100%becomes+ security 8% = 108%the moment the operator clicks "Add Team" → "security pack". Nothing in the UI or the backend prevents this, and nothing visibly flags the miscount until the operator manually adds the numbers across the cards.Three problems stacked in the current flow:
budget_percentdepartment is applied on top of an already-100% org, the backend accepts the write verbatim and leaves the total at >100%. The pack application path has no "scale existing departments down to make room" or "reject if the sum would exceed 100%" branch.Department.budget_percentis a loosege=0.0field on each department independently; the schema never checks that the company-wide sum equals 100%. The budget enforcer at runtime reads these as relative weights rather than absolute caps, so 108% does not crash anything, but it does break the operator's mental model of "how much of my budget is allocated".The Base UI migration PR (#1074) adds a running "Total budget allocated: N%" chip plus a red/amber warning banner on the DepartmentsTab to surface the miscount at a glance. That is a UX band-aid -- this issue tracks the proper fix.
Proposed fix
Backend
validate_budget_totalmodel validator toCompanyConfig(or wherever departments are aggregated) that emits a warning log event when the sum ofdepartment.budget_percentdeviates from 100 by more than a small tolerance. Do not reject on load so existing misconfigured YAML files keep working.rebalance_modeparameter:none: apply the pack as-is (current behaviour, surfaces the warning).scale_existing: proportionally scale the existing departments'budget_percentdown so the total is 100% after the pack applies. Example: if existing100%+ pack adds8%, scale existing departments by92/100so they sum to 92% and the pack adds 8% on top.reject_if_over: return a 409 Conflict with a descriptive error if the pack would push the total over 100%.scale_existingsince it matches operator intent in the common case.Frontend
securitywill add 1 department (8% budget). Your current departments allocate 100%, so either the existing departments will be rescaled to 92% or you can cancel and rebalance manually."Apply with rescale(sendsrebalance_mode=scale_existing) andCancel.Docs
docs/design/organization.mdanddocs/guides/to document how pack application interacts with existing budgets.CompanyConfig.budget_monthlyto compute per-department caps.Out of scope
Acceptance criteria
budget_percentdepartment on an already-100% org does not leave the total above 100% unless the operator explicitly opts in.sum(department.budget_percent)deviates from 100 by more than 0.01.PATCH /departments/{name}that would push the total over 100%, with a descriptive 409 response.budget_percent, with visible affordance showing how close they are to 100%.Related
src/synthorg/core/company.py(Department.budget_percent),web/src/pages/org-edit/DepartmentsTab.tsx,web/src/pages/org-edit/PackSelectionDialog.tsx.