Skip to content

feat: in-dashboard team editing + budget rebalance on pack apply#1093

Merged
Aureliolo merged 8 commits intomainfrom
feat/team-editing-budget-rebalance
Apr 6, 2026
Merged

feat: in-dashboard team editing + budget rebalance on pack apply#1093
Aureliolo merged 8 commits intomainfrom
feat/team-editing-budget-rebalance

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

Implements in-dashboard team editing (#1079) and budget rebalancing on pack application (#1080).

#1079: Team CRUD

Backend (4 new endpoints under /departments/{dept_name}/teams):

  • POST / -- create team with name, lead, members
  • PATCH /{team_name} -- rename, change lead, replace members
  • DELETE /{team_name}?reassign_to= -- delete with optional member reassignment (guards against self-reassignment)
  • PATCH /reorder -- reorder teams within a department

All endpoints use the established AGENT_LOCK + settings persistence pattern from template_packs.py.

Frontend:

  • TeamListSection with @dnd-kit sortable reorder, edit/delete actions per team card
  • TeamEditDialog (Base UI Dialog) for create/edit with name, lead, TagInput members
  • TeamDeleteConfirmDialog wrapping ConfirmDialog with optional reassignment SelectField
  • Integrated into DepartmentEditDrawer replacing the former read-only teams section
  • Live budget total validation -- warning when projected total exceeds 100%, Save disabled

Org Chart:

  • TeamGroupNode component (dashed border, team name + member count)
  • build-org-tree.ts emits team group nodes as intermediate layer between department and agent nodes
  • Agents in teams are parented to team group; unassigned agents stay parented to dept group
  • Backward-compatible: departments with no teams produce identical output

#1080: Budget Rebalance

Backend:

  • compute_rebalance() pure function in src/synthorg/budget/rebalance.py with 3 modes: none, scale_existing (default), reject_if_over
  • ApplyTemplatePackRequest extended with rebalance_mode param (default: scale_existing)
  • ApplyTemplatePackResponse extended with budget_before, budget_after, rebalance_mode, scale_factor
  • Pack-apply integration: rebalances budgets before persisting, rejects with 409 in reject_if_over mode
  • Under-budget warning log via COMPANY_BUDGET_UNDER_ALLOCATED event in Company model validator

Frontend:

  • PackApplyPreviewDialog shown before applying packs with departments -- budget snapshot, SegmentedControl for rebalance mode, before/after preview table
  • computeBudgetPreview() client-side utility mirrors backend scaling logic
  • Toast with budget details after successful apply

Docs

  • docs/design/page-structure.md updated: "nested teams editing" no longer deferred, team endpoints added to API list

Test Plan

  • 60 new backend tests (26 rebalance utility + 25 team CRUD controller + 9 pack-apply rebalance integration)
  • 4 new org chart build-tree tests for team group nodes
  • All existing tests updated for new props (DepartmentsTab, DepartmentEditDrawer, OrgEditPage)
  • uv run python -m pytest tests/ -n 8 -- 14349 passed
  • npm --prefix web run test -- 2409 passed
  • uv run ruff check src/ tests/ -- clean
  • uv run mypy src/ tests/ -- clean
  • npm --prefix web run type-check + lint -- clean

Review Coverage

Pre-reviewed by 3 agents (backend code reviewer, frontend reviewer, API contract drift checker). 10 findings addressed:

  • Fixed self-reassignment bug on team delete
  • Made TeamConfig.lead required to match backend
  • Fixed untrimmed member names in TeamEditDialog
  • Fixed irreversible select in TeamDeleteConfirmDialog
  • Aligned dialog styling with established pattern
  • Removed dead prop, variable shadowing, orphaned comment
  • Added dedicated COMPANY_BUDGET_UNDER_ALLOCATED event constant
  • Added logger to rebalance module

Dependencies

This branch was created from main at v0.6.2. WT1 (#1081) adds department CRUD endpoints -- this branch should be rebased after WT1 merges. The team controller is a separate file (teams.py) so merge conflicts will be minimal.

Closes #1079
Closes #1080

Copilot AI review requested due to automatic review settings April 6, 2026 11:21
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

Walkthrough

Adds full team CRUD and reorder APIs and UI plus budget rebalance support for template-pack application. Backend: new TeamController with create/patch/delete/reorder endpoints, DTOs, settings persistence, locks, auth guards, observability events, and a new budget rebalance module (RebalanceMode, compute_rebalance) integrated into template-pack apply (expanded request/response, conflict paths, and logging). Frontend: new API functions/types, store mutations, TeamListSection, TeamEditDialog, TeamDeleteConfirmDialog, TeamGroupNode, DepartmentEditDrawer/DepartmentsTab wiring, PackApplyPreviewDialog, budget preview utility. Includes unit tests for teams and rebalance behavior.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.73% which is insufficient. The required threshold is 40.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the two main features: team editing and budget rebalancing on pack application, matching the primary changes across backend, frontend, and org chart components.
Description check ✅ Passed The description comprehensively covers both linked issues (#1079 and #1080), detailing backend team CRUD endpoints, frontend team management UI components, org chart team group nodes, budget rebalancing logic, and test coverage.
Linked Issues check ✅ Passed All coding requirements from #1079 (team CRUD backend endpoints, TeamListSection/TeamEditDialog/TeamDeleteConfirmDialog components, TeamGroupNode and build-org-tree updates) and #1080 (compute_rebalance function, ApplyTemplatePackRequest/Response extensions, PackApplyPreviewDialog, budget preview utility) are implemented and documented.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the linked issues: team CRUD implementation, budget rebalancing, org chart team visualization, and corresponding tests. Dependency updates (go.mod, mockServiceWorker.js version) are minor and incidental to the feature work.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 6, 2026

Dependency Review

The following issues were found:

  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ⚠️ 11 package(s) with unknown licenses.

View full job summary

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces comprehensive Team CRUD functionality, allowing users to create, update, delete, and reorder teams within departments. It also implements a budget rebalancing system that automatically scales existing department budgets when new template packs are applied, ensuring the total allocation remains within limits. The frontend has been updated with new dialogs for team management and a budget preview tool. My feedback focuses on simplifying the team member deduplication logic, removing redundant math in the rebalancing calculation, and cleaning up a now-unnecessary null check in the team edit dialog.

Comment on lines +438 to +447
existing_members = list(target.get("members", []))
existing_lower = {m.strip().casefold() for m in existing_members}
for member in team.get("members", []):
if member.strip().casefold() not in existing_lower:
existing_members.append(member)
existing_lower.add(member.strip().casefold())

updated_target = {**target, "members": existing_members}
_validate_team_model(updated_target)
teams[target_idx] = updated_target
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for merging and deduplicating team members is correct, but it can be made more concise and Pythonic by using a dictionary to handle the case-insensitive uniqueness check. This approach avoids modifying a list and a set within a loop, which can improve readability.

Suggested change
existing_members = list(target.get("members", []))
existing_lower = {m.strip().casefold() for m in existing_members}
for member in team.get("members", []):
if member.strip().casefold() not in existing_lower:
existing_members.append(member)
existing_lower.add(member.strip().casefold())
updated_target = {**target, "members": existing_members}
_validate_team_model(updated_target)
teams[target_idx] = updated_target
# Merge members (deduplicate, case-insensitive).
target_members = list(target.get("members", []))
source_members = team.get("members", [])
# Use a dictionary to preserve original case and order while de-duplicating.
# The first occurrence's case is preserved.
member_map = {m.strip().casefold(): m for m in target_members}
for member in source_members:
member_map.setdefault(member.strip().casefold(), member)
updated_target = {**target, "members": list(member_map.values())}
_validate_team_model(updated_target)
teams[target_idx] = updated_target

# Cannot scale zero-budget departments; clamp to 0.
factor = 0.0
else:
factor = max(0.0, min(1.0, target_existing / existing_total))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The use of min(1.0, ...) is redundant here. The code only reaches this line if combined_total > max_budget, which implies existing_total > target_existing. Therefore, target_existing / existing_total will always be less than 1 (assuming existing_total > 0). Removing the min() call simplifies the code and makes the logic clearer.

Suggested change
factor = max(0.0, min(1.0, target_existing / existing_total))
# Since we're in the 'over-budget' branch, `target_existing` will be
# less than `existing_total`, so the factor will be < 1.
factor = max(0.0, target_existing / existing_total)

if (open) {
if (mode === 'edit' && team) {
setName(team.name)
setLead(team.lead ?? '')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The TeamConfig interface now defines lead as a required string, which aligns with the backend model. Therefore, the nullish coalescing operator (?? '') is no longer necessary here.

Suggested change
setLead(team.lead ?? '')
setLead(team.lead)

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 6, 2026

Codecov Report

❌ Patch coverage is 98.50746% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.35%. Comparing base (df2142c) to head (ffa0d37).
⚠️ Report is 1 commits behind head on main.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/synthorg/api/controllers/teams.py 98.48% 0 Missing and 2 partials ⚠️
src/synthorg/api/controllers/template_packs.py 94.44% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1093      +/-   ##
==========================================
+ Coverage   89.23%   89.35%   +0.11%     
==========================================
  Files         766      768       +2     
  Lines       45014    45213     +199     
  Branches     4525     4547      +22     
==========================================
+ Hits        40168    40399     +231     
+ Misses       4029     3989      -40     
- Partials      817      825       +8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class team editing inside the Org Edit dashboard and introduces budget rebalancing behavior when applying template packs that add departments, keeping overall department budget allocations from unintentionally exceeding 100%.

Changes:

  • Implemented backend Team CRUD endpoints under /departments/{dept_name}/teams and wired corresponding frontend store/actions + drawer UI (create/edit/delete/reorder).
  • Added budget rebalance utility + pack-apply integration (new request param + response fields) and a frontend “apply preview” flow for packs that add departments.
  • Updated Org Chart hierarchy generation/rendering to include intermediate “team group” nodes between department groups and agent nodes.

Reviewed changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
web/src/utils/budget.ts Adds client-side computeBudgetPreview() used by pack-apply preview UI.
web/src/stores/company.ts Adds team CRUD/reorder actions and optimistic state updates in the company store.
web/src/pages/OrgEditPage.tsx Plumbs team mutation callbacks into Departments tab.
web/src/pages/OrgChartPage.tsx Registers new ReactFlow node type for team group nodes.
web/src/pages/org/TeamGroupNode.tsx New team group node renderer (dashed group box with member count).
web/src/pages/org/build-org-tree.ts Emits team group nodes and parents agents under team groups when assigned.
web/src/pages/org-edit/TeamListSection.tsx New Teams section UI with sortable reorder + edit/delete actions.
web/src/pages/org-edit/TeamEditDialog.tsx New Base UI dialog for creating/editing teams (name/lead/members).
web/src/pages/org-edit/TeamDeleteConfirmDialog.tsx New confirm dialog with optional member reassignment selection.
web/src/pages/org-edit/PackSelectionDialog.tsx Adds budget-preview gating flow before applying packs that add departments.
web/src/pages/org-edit/PackApplyPreviewDialog.tsx New preview dialog showing budget snapshot and rebalance mode selection.
web/src/pages/org-edit/DepartmentsTab.tsx Extends props to support team mutation actions via Department drawer.
web/src/pages/org-edit/DepartmentsTab.stories.tsx Updates story mocks for required TeamConfig.lead + new team callbacks.
web/src/pages/org-edit/DepartmentEditDrawer.tsx Replaces read-only teams summary with full editable/reorderable teams UI + budget total warning.
web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx Updates drawer story to pass config + team callbacks and required lead.
web/src/hooks/useOrgEditData.ts Exposes team mutation functions from the company store to pages.
web/src/api/types.ts Makes TeamConfig.lead required; adds team mutation request types + pack apply rebalance fields/types.
web/src/api/endpoints/company.ts Adds REST calls for team create/update/delete/reorder endpoints.
web/src/tests/pages/OrgEditPage.test.tsx Updates mocked hook return shape to include team mutation functions.
web/src/tests/pages/org/build-org-tree.test.ts Adds tests verifying team group node emission and parenting behavior.
web/src/tests/pages/org-edit/DepartmentsTab.test.tsx Updates DepartmentsTab test props to include team callbacks.
web/src/tests/pages/org-edit/DepartmentEditDrawer.test.tsx Updates drawer tests for new teams section and new props.
tests/unit/budget/test_rebalance.py Adds unit tests covering rebalance utility behavior across modes/edge cases.
tests/unit/api/controllers/test_template_packs_rebalance.py Adds controller-level tests for pack-apply rebalance modes and response fields.
tests/unit/api/controllers/test_teams.py Adds controller tests for team CRUD + reorder behavior.
src/synthorg/observability/events/template.py Adds template-pack budget rebalance/reject event constants.
src/synthorg/observability/events/company.py Adds budget under-allocation event constant.
src/synthorg/observability/events/api.py Adds API event constants for team CRUD and budget validation/rebalance.
src/synthorg/core/company.py Logs COMPANY_BUDGET_UNDER_ALLOCATED when configured budgets sum under max.
src/synthorg/budget/rebalance.py Introduces compute_rebalance() with modes none/scale_existing/reject_if_over.
src/synthorg/budget/init.py Exposes rebalance types/functions in budget package public API.
src/synthorg/api/controllers/template_packs.py Adds rebalance_mode request param, integrates rebalance into pack apply, extends response payload.
src/synthorg/api/controllers/teams.py Implements new TeamController endpoints for create/update/delete/reorder teams.
src/synthorg/api/controllers/init.py Registers the new TeamController.
docs/design/page-structure.md Updates design doc to reflect in-drawer team editing + lists new team endpoints.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +297 to +313
teams: list[dict[str, Any]] = list(dept.get("teams", []))
current_names = {str(t.get("name", "")).strip().casefold() for t in teams}
requested_names = {n.strip().casefold() for n in data.team_names}

if current_names != requested_names:
msg = (
"team_names must contain exactly the current team "
f"names: {sorted(current_names)}"
)
raise ApiValidationError(msg)

# Build name->team lookup for reordering.
team_map: dict[str, dict[str, Any]] = {
str(t.get("name", "")).strip().casefold(): t for t in teams
}
reordered = [team_map[n.strip().casefold()] for n in data.team_names]

Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reorder_teams validates requested names using set equality, which ignores duplicates. A payload like ["alpha", "alpha"] can pass validation (when only one team exists) and will persist duplicate team entries. Validate that team_names is a true permutation: same length as current teams and no duplicates (case-insensitive), in addition to matching the current name set.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +14
from synthorg.api.controllers.setup_helpers import AGENT_LOCK as _AGENT_LOCK
from synthorg.api.controllers.template_packs import _read_setting_list
from synthorg.api.dto import ApiResponse
from synthorg.api.errors import ApiValidationError, ConflictError, NotFoundError
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This controller imports _read_setting_list from template_packs.py. Because it’s a private (underscore) helper in an unrelated controller, this creates an unstable cross-module dependency. Consider moving the settings-read helper into a shared module (e.g. api/controllers/settings_helpers.py) or duplicating a small local implementation with a non-private name.

Copilot uses AI. Check for mistakes.
Comment on lines +34 to +43
const preview = useMemo(() => {
if (!pack) return null
// Estimate pack department budgets from the pack's department_count.
// The actual budget percents aren't in PackInfoResponse, so we use
// a placeholder for the preview table. The real numbers come from
// the API response after apply.
const packDepts = pack.department_count > 0
? [{ name: `${pack.display_name} dept`, budget_percent: 8 }]
: []
return computeBudgetPreview(currentDepartments, packDepts)
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The preview uses a hard-coded placeholder department budget (budget_percent: 8) because PackInfoResponse doesn’t contain real budget percents. This makes the before/after table and the “would exceed 100%” logic potentially incorrect/misleading for many packs. Consider fetching the pack’s actual department budgets (or adding a backend preview/dry-run endpoint) and only rendering numeric previews when real values are available.

Copilot uses AI. Check for mistakes.
Comment on lines +139 to +146
const handleSelectPack = useCallback((pack: PackInfoResponse) => {
if (pack.department_count > 0) {
// Pack adds departments -- show budget preview before applying.
setSelectedPack(pack)
} else {
// No departments -- apply directly (no budget impact).
void handleApplyDirect(pack.name)
}
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Selecting a pack with department_count > 0 opens PackApplyPreviewDialog but keeps the underlying PackSelectionDialog open. This results in two dialogs/backdrops/focus traps at once, which can break focus management and accessibility. Consider closing/hiding the selection dialog while the preview is open, or rendering the preview as a conditional step within the same dialog.

Copilot uses AI. Check for mistakes.
Comment on lines +424 to +452
const targetExisting = 100 - packTotal
const factor = Math.max(0, Math.min(1, targetExisting / currentTotal))

const scaledExisting = currentDepts.map((d) => {
const before = d.budget_percent ?? 0
return {
name: d.name,
before,
after: Math.round(before * factor * 100) / 100,
}
})

const newDepts = packDeptBudgets.map((d) => ({
name: d.name,
before: 0,
after: d.budget_percent,
}))

const finalTotal = [...scaledExisting, ...newDepts].reduce(
(sum, d) => sum + d.after,
0,
)

return {
currentTotal,
packTotal,
projectedTotal: Math.round(finalTotal * 100) / 100,
scaleFactor: Math.round(factor * 1000) / 1000,
departments: [...scaledExisting, ...newDepts],
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

computeBudgetPreview() rounds scaled budgets to 2 decimals and the scale factor to 3 decimals, but the backend rebalance logic rounds using BUDGET_ROUNDING_PRECISION (currently 10). This can cause the client preview (totals/scale factor) to disagree with the server response. Align the rounding/precision strategy with the backend (ideally using the same precision and rounding method for both totals and scale factor).

Copilot uses AI. Check for mistakes.
Comment on lines +71 to +83
existing_total = sum(d.get("budget_percent", 0.0) for d in existing_depts)
new_total = sum(d.get("budget_percent", 0.0) for d in new_depts)
combined_total = existing_total + new_total

if mode == RebalanceMode.NONE:
all_depts = [*existing_depts, *new_depts]
return RebalanceResult(
departments=tuple(all_depts),
old_total=existing_total,
new_total=round(combined_total, rounding_precision),
scale_factor=None,
rejected=False,
)
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

compute_rebalance() returns old_total as the raw float sum of existing budgets, while new_total is rounded. Since the API response surfaces budget_before, this can leak IEEE-754 artifacts (and is inconsistent with the rest of the rounding approach). Consider rounding old_total (and/or using math.fsum) to the same rounding_precision used elsewhere.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +7
import { Handle, Position, type NodeProps } from '@xyflow/react'
import { Users } from 'lucide-react'
import type { TeamGroupData } from './build-org-tree'

export function TeamGroupNode({ data }: NodeProps) {
const { teamName, memberCount } = data as TeamGroupData

Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TeamGroupNode receives untyped NodeProps and then casts data as TeamGroupData. Prefer typing the props as NodeProps<TeamGroupData> (similar to DepartmentGroupNode) to avoid unsafe casts and get compile-time guarantees for teamName/memberCount.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/synthorg/api/controllers/template_packs.py (1)

350-360: ⚠️ Potential issue | 🟡 Minor

ConflictError gets logged as generic failure instead of being passed through cleanly.

The ConflictError raised on budget rejection (line 260) is caught by the generic except Exception block and logged with action="apply_failed", which is misleading since this is an expected validation failure, not an unexpected error. Consider re-raising ConflictError explicitly like NotFoundError.

🛠️ Proposed fix
         try:
             result = await _apply_pack_to_settings(app_state, data)
-        except NotFoundError:
+        except NotFoundError, ConflictError:
             raise
         except Exception:
             logger.exception(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/api/controllers/template_packs.py` around lines 350 - 360, The
current exception handler around the await _apply_pack_to_settings(app_state,
data) logs all exceptions as TEMPLATE_PACK_APPLY_ERROR and hides expected
validation failures; update the excepts to re-raise ConflictError explicitly
(similar to NotFoundError) before the generic except Exception so that
budget-rejection ConflictError from _apply_pack_to_settings is not logged as
action="apply_failed" — keep the existing logger.exception call for unexpected
exceptions and preserve pack_name=data.pack_name and action="apply_failed"
there.
web/src/pages/org-edit/PackSelectionDialog.tsx (1)

111-137: 🧹 Nitpick | 🔵 Trivial

Extract duplicated refresh logic into a shared helper.

Both handleApplyDirect and handleApplyWithMode contain identical try/catch blocks for fetchCompanyData() with the same warning toast. This duplication can be consolidated.

♻️ Proposed refactor
+  const refreshCompanyData = useCallback(async () => {
+    try {
+      await useCompanyStore.getState().fetchCompanyData()
+    } catch {
+      addToast({
+        variant: 'warning',
+        title: 'Pack applied but failed to refresh data. Reload the page.',
+      })
+    }
+  }, [addToast])
+
   const handleApplyDirect = useCallback(
     async (packName: string) => {
       // ... existing code ...
         onOpenChange(false)
-        try {
-          await useCompanyStore.getState().fetchCompanyData()
-        } catch {
-          addToast({
-            variant: 'warning',
-            title: 'Pack applied but failed to refresh data. Reload the page.',
-          })
-        }
+        await refreshCompanyData()
       // ... rest ...
     },
-    [addToast, onOpenChange],
+    [addToast, onOpenChange, refreshCompanyData],
   )

Also applies to: 149-181

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/org-edit/PackSelectionDialog.tsx` around lines 111 - 137, There
is duplicated post-apply refresh logic in handleApplyDirect and
handleApplyWithMode: extract the try/catch that calls
useCompanyStore.getState().fetchCompanyData() and shows the warning toast (using
addToast) into a shared async helper (e.g., refreshCompanyDataWithWarning) and
call that helper from both handlers after onOpenChange(false); update
handleApplyDirect and handleApplyWithMode to await the helper and remove the
duplicated try/catch so only a single reusable function contains the
fetchCompanyData call and the warning addToast behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/synthorg/api/controllers/teams.py`:
- Around line 171-175: Replace the current grouped-exceptions clause that uses
parentheses with the PEP 758 comma-separated form: change the except line
surrounding the Team(**team_dict) construction (currently "except (ValueError,
TypeError) as exc:") to the PEP 758 style "except ValueError, TypeError as exc:"
so the catch for ValueError and TypeError still logs msg and raises
ApiValidationError(msg) from exc; keep the body unchanged and ensure references
to Team and ApiValidationError remain intact.
- Around line 455-460: The file ends without a trailing newline; add a single
newline character at the end of the file so the final statement (the
logger.info(...) call using API_TEAM_DELETED with dept_name, team_name,
reassign_to) is followed by a newline to comply with PEP 8 and POSIX text-file
conventions.

In `@src/synthorg/budget/rebalance.py`:
- Around line 71-73: The code currently uses .get("budget_percent", 0.0) when
calculating existing_total/new_total (variables existing_depts and new_depts),
which hides missing keys; change this to enforce the documented contract by
validating each department dict before summing: iterate existing_depts and
new_depts, and if any dept is missing the "budget_percent" key raise a
ValueError (or at minimum log a warning with identifying info) rather than
defaulting to 0.0; then compute existing_total/new_total by accessing
dept["budget_percent"] and keep combined_total = existing_total + new_total as
before so callers cannot proceed with incomplete data.

In `@tests/unit/api/controllers/test_template_packs_rebalance.py`:
- Around line 15-24: The helper _seed_departments currently sends a PUT via
test_client.put("/api/v1/settings/company/departments", ...) but ignores the
response; modify _seed_departments to capture the response from test_client.put
and assert the response status_code is a successful value (e.g., 200 or 204) so
setup fails fast on errors—use the response from test_client.put in
_seed_departments and add an assertion (or raise) if response.status_code is not
in the expected success codes.

In `@web/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsx`:
- Around line 69-86: The test uses brittle DOM matching in
DepartmentEditDrawer.test.tsx (the getAllByRole/find using textContent and
title) to locate the department-level delete button; update the component that
renders the department delete control to include a stable attribute like
data-testid="dept-delete-btn" (the department-level delete button element), then
change the test to select that button via getByTestId('dept-delete-btn') (and
leave the saveButton logic intact) so the spec targets the intended element
reliably and the subsequent clicks/assertions (mockOnDelete) remain valid.

In `@web/src/api/endpoints/company.ts`:
- Around line 130-141: Update deleteTeam to explicitly return the result of
unwrapVoid for consistency with other void-style helpers: change the function so
it returns unwrapVoid(response) (referencing the deleteTeam function and the
unwrapVoid call) instead of calling unwrapVoid without returning; ensure the
signature remains Promise<void> and mirror the same explicit-return pattern used
by deleteDepartment for clarity.

In `@web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx`:
- Around line 46-49: The story mocks return invalid team objects (empty members
and blank name/lead); update onCreateTeam to return a valid team using data.name
|| 'New Team', data.lead || 'Unassigned' and members: data.members ?? [], and
update onUpdateTeam to use the existing team parameter (_t) to preserve values
when data fields are missing (e.g., name: data.name ?? _t.name, lead: data.lead
?? _t.lead, members: data.members ?? _t.members) so the mocked team never
becomes an impossible/blank state; keep onDeleteTeam and onReorderTeams as
no-ops if no behavior is needed.

In `@web/src/pages/org-edit/DepartmentEditDrawer.tsx`:
- Around line 157-161: The drawer currently only shows an over-allocation
warning using budgetWouldExceed and projectedTotal; update DepartmentEditDrawer
to also display an under-allocation warning when projectedTotal < 99.99 (mirror
DepartmentsTab behavior) by adding a conditional (e.g., budgetUnderAllocated or
check projectedTotal < 99.99) that renders a small warning paragraph (similar
styling to the existing message, e.g., text-xs and an appropriate warning color)
and a message like "Total would be X% -- under-allocated." Ensure you reference
projectedTotal for the displayed percentage and keep the existing
over-allocation logic (budgetWouldExceed) intact.

In `@web/src/pages/org-edit/DepartmentsTab.stories.tsx`:
- Around line 56-59: The mock team callbacks return empty/blank fields which can
produce invalid story state; update onCreateTeam and onUpdateTeam to always
return a valid team object by using data values with safe fallbacks (e.g.,
data.name ?? 'New Team', data.lead ?? 'Unassigned') and ensure members is a
non-empty array (e.g., preserve data.members if present or create a default
member object using the lead), and adjust onDeleteTeam and onReorderTeams to
return the expected stable result for the story (such as a truthy success value
or the updated team list) instead of no-op to keep interactive stories stable;
target the functions named onCreateTeam, onUpdateTeam, onDeleteTeam and
onReorderTeams when making these changes.

In `@web/src/pages/org-edit/PackApplyPreviewDialog.tsx`:
- Around line 34-44: The preview currently injects a hardcoded budget_percent
(8) into packDepts inside the preview memo, which can mislead users; update the
logic in the preview computation to first use a new optional field from
PackInfoResponse (e.g., pack.total_budget_percent) when present and only fall
back to a clearly labeled estimate instead of silently hardcoding 8, and add a
visible disclaimer element in PackApplyPreviewDialog rendered alongside the
computed preview (when pack && pack.department_count>0) that explicitly states
"Estimated values — final values come from API after apply"; adjust
computeBudgetPreview calls to accept the optional total_budget_percent from pack
and ensure the disclaimer is shown whenever the fallback estimate is used.

In `@web/src/pages/org-edit/TeamDeleteConfirmDialog.tsx`:
- Around line 23-27: TeamDeleteConfirmDialog can retain a stale reassignTo when
the team prop changes; add a useEffect inside the TeamDeleteConfirmDialog
component that watches the team prop and calls setReassignTo('') (or another
appropriate default) whenever team changes to clear any previous selection and
avoid referencing siblings from the prior team.

In `@web/src/stores/company.ts`:
- Around line 320-342: The deleteTeam handler (deleteTeam) currently removes the
deleted team from config.departments but doesn't update the reassignment
target's members when reassignTo is provided; update the local state after
apiDeleteTeam to also move the deleted team's members into the target team's
members (or, alternatively, trigger a refetch of that department). Concretely,
after awaiting apiDeleteTeam(deptName, teamName, reassignTo) read the previous
department from get().config.departments, find the deleted team to capture its
members, then when mapping departments update the target team (t.name ===
reassignTo) by concatenating those members into its members array and remove the
deleted team from teams, or replace this optimistic update with a fresh fetch of
the department if you prefer server-authoritative state.

In `@web/src/utils/budget.ts`:
- Around line 403-451: The preview rebalance logic in this block diverges from
backend: when currentTotal <= 0 (the guard using combined <= 100 || currentTotal
<= 0) set scaleFactor to 0.0 instead of 1 to match backend behavior, and stop
truncating factor/after/projectedTotal to 2–3 decimal places—use 10-decimal
precision (same as backend) for scaleFactor, per-department after values, and
projectedTotal instead of Math.round(... * 100)/100 or Math.round(... *
1000)/1000; update the uses of factor, scaledExisting.after, and projectedTotal
calculations accordingly so the preview matches
src/synthorg/budget/rebalance.py.

---

Outside diff comments:
In `@src/synthorg/api/controllers/template_packs.py`:
- Around line 350-360: The current exception handler around the await
_apply_pack_to_settings(app_state, data) logs all exceptions as
TEMPLATE_PACK_APPLY_ERROR and hides expected validation failures; update the
excepts to re-raise ConflictError explicitly (similar to NotFoundError) before
the generic except Exception so that budget-rejection ConflictError from
_apply_pack_to_settings is not logged as action="apply_failed" — keep the
existing logger.exception call for unexpected exceptions and preserve
pack_name=data.pack_name and action="apply_failed" there.

In `@web/src/pages/org-edit/PackSelectionDialog.tsx`:
- Around line 111-137: There is duplicated post-apply refresh logic in
handleApplyDirect and handleApplyWithMode: extract the try/catch that calls
useCompanyStore.getState().fetchCompanyData() and shows the warning toast (using
addToast) into a shared async helper (e.g., refreshCompanyDataWithWarning) and
call that helper from both handlers after onOpenChange(false); update
handleApplyDirect and handleApplyWithMode to await the helper and remove the
duplicated try/catch so only a single reusable function contains the
fetchCompanyData call and the warning addToast behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c5a67545-7fe1-4d6e-9506-5e8df1ab8180

📥 Commits

Reviewing files that changed from the base of the PR and between 1e86ca6 and affabdc.

📒 Files selected for processing (35)
  • docs/design/page-structure.md
  • src/synthorg/api/controllers/__init__.py
  • src/synthorg/api/controllers/teams.py
  • src/synthorg/api/controllers/template_packs.py
  • src/synthorg/budget/__init__.py
  • src/synthorg/budget/rebalance.py
  • src/synthorg/core/company.py
  • src/synthorg/observability/events/api.py
  • src/synthorg/observability/events/company.py
  • src/synthorg/observability/events/template.py
  • tests/unit/api/controllers/test_teams.py
  • tests/unit/api/controllers/test_template_packs_rebalance.py
  • tests/unit/budget/test_rebalance.py
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsx
  • web/src/__tests__/pages/org-edit/DepartmentsTab.test.tsx
  • web/src/__tests__/pages/org/build-org-tree.test.ts
  • web/src/api/endpoints/company.ts
  • web/src/api/types.ts
  • web/src/hooks/useOrgEditData.ts
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/pages/org-edit/TeamDeleteConfirmDialog.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/pages/org/TeamGroupNode.tsx
  • web/src/pages/org/build-org-tree.ts
  • web/src/stores/company.ts
  • web/src/utils/budget.ts

Comment on lines +171 to +175
try:
return Team(**team_dict)
except (ValueError, TypeError) as exc:
msg = f"Team validation failed: {exc}"
raise ApiValidationError(msg) from exc
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Use PEP 758 except syntax (comma-separated, no parentheses).

Per coding guidelines, Python 3.14 code should use PEP 758 except syntax without parentheses.

♻️ Proposed fix
     try:
         return Team(**team_dict)
-    except (ValueError, TypeError) as exc:
+    except ValueError, TypeError as exc:
         msg = f"Team validation failed: {exc}"
         raise ApiValidationError(msg) from exc

As per coding guidelines: "Use PEP 758 except syntax: except A, B: (no parentheses) in Python 3.14 code—ruff enforces this."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/api/controllers/teams.py` around lines 171 - 175, Replace the
current grouped-exceptions clause that uses parentheses with the PEP 758
comma-separated form: change the except line surrounding the Team(**team_dict)
construction (currently "except (ValueError, TypeError) as exc:") to the PEP 758
style "except ValueError, TypeError as exc:" so the catch for ValueError and
TypeError still logs msg and raises ApiValidationError(msg) from exc; keep the
body unchanged and ensure references to Team and ApiValidationError remain
intact.

Comment on lines +455 to +460
logger.info(
API_TEAM_DELETED,
department=dept_name,
team=team_name,
reassign_to=reassign_to,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Missing trailing newline at end of file.

The file should end with a newline character per PEP 8 conventions.

♻️ Add trailing newline
         logger.info(
             API_TEAM_DELETED,
             department=dept_name,
             team=team_name,
             reassign_to=reassign_to,
         )
+
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
logger.info(
API_TEAM_DELETED,
department=dept_name,
team=team_name,
reassign_to=reassign_to,
)
logger.info(
API_TEAM_DELETED,
department=dept_name,
team=team_name,
reassign_to=reassign_to,
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/api/controllers/teams.py` around lines 455 - 460, The file ends
without a trailing newline; add a single newline character at the end of the
file so the final statement (the logger.info(...) call using API_TEAM_DELETED
with dept_name, team_name, reassign_to) is followed by a newline to comply with
PEP 8 and POSIX text-file conventions.

Comment on lines +71 to +73
existing_total = sum(d.get("budget_percent", 0.0) for d in existing_depts)
new_total = sum(d.get("budget_percent", 0.0) for d in new_depts)
combined_total = existing_total + new_total
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Silent default for missing budget_percent may mask data issues.

The docstring states departments "must have a budget_percent key", but the code uses .get("budget_percent", 0.0) which silently defaults to 0 if missing. This discrepancy could mask corrupted or incomplete department data.

Consider logging a warning when a department lacks budget_percent, or raise an error to enforce the documented contract.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/synthorg/budget/rebalance.py` around lines 71 - 73, The code currently
uses .get("budget_percent", 0.0) when calculating existing_total/new_total
(variables existing_depts and new_depts), which hides missing keys; change this
to enforce the documented contract by validating each department dict before
summing: iterate existing_depts and new_depts, and if any dept is missing the
"budget_percent" key raise a ValueError (or at minimum log a warning with
identifying info) rather than defaulting to 0.0; then compute
existing_total/new_total by accessing dept["budget_percent"] and keep
combined_total = existing_total + new_total as before so callers cannot proceed
with incomplete data.

Comment on lines +69 to 86
it('disables Save and dept Delete buttons with #1081 tooltip', () => {
renderDrawer()
const saveButton = screen.getByRole('button', { name: /save/i })
const deleteButton = screen.getByRole('button', { name: /delete/i })
const saveButton = screen.getByRole('button', { name: /^save$/i })
// Department-level delete button (distinguished from team delete icons)
const deptDeleteButtons = screen.getAllByRole('button', { name: /delete/i })
const deptDelete = deptDeleteButtons.find(
(btn) => btn.textContent?.toLowerCase().includes('delete') && btn.getAttribute('title')?.includes('1081'),
)
expect(saveButton).toBeDisabled()
expect(deleteButton).toBeDisabled()
expect(deptDelete).toBeDefined()
expect(deptDelete).toBeDisabled()
expect(saveButton.getAttribute('title') ?? '').toContain('1081')
expect(deleteButton.getAttribute('title') ?? '').toContain('1081')
// Clicking the disabled buttons must not call the mutation props.
fireEvent.click(saveButton)
fireEvent.click(deleteButton)
if (deptDelete) fireEvent.click(deptDelete)
expect(mockOnUpdate).not.toHaveBeenCalled()
expect(mockOnDelete).not.toHaveBeenCalled()
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Fragile selector for department delete button.

The selector logic relying on textContent and title attribute (lines 74-76) is brittle and could break with UI changes. Consider adding a data-testid="dept-delete-btn" to the department-level delete button in the component for more reliable test targeting.

-    const deptDeleteButtons = screen.getAllByRole('button', { name: /delete/i })
-    const deptDelete = deptDeleteButtons.find(
-      (btn) => btn.textContent?.toLowerCase().includes('delete') && btn.getAttribute('title')?.includes('1081'),
-    )
+    const deptDelete = screen.getByTestId('dept-delete-btn')
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsx` around lines
69 - 86, The test uses brittle DOM matching in DepartmentEditDrawer.test.tsx
(the getAllByRole/find using textContent and title) to locate the
department-level delete button; update the component that renders the department
delete control to include a stable attribute like data-testid="dept-delete-btn"
(the department-level delete button element), then change the test to select
that button via getByTestId('dept-delete-btn') (and leave the saveButton logic
intact) so the spec targets the intended element reliably and the subsequent
clicks/assertions (mockOnDelete) remain valid.

Comment on lines +56 to +59
onCreateTeam: async (_d, data) => ({ name: data.name, lead: data.lead, members: [] }),
onUpdateTeam: async (_d, _t, data) => ({ name: data.name ?? '', lead: data.lead ?? '', members: [] }),
onDeleteTeam: async () => {},
onReorderTeams: async () => {},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Team callback mocks here also allow invalid story state.

On Line 56/Line 57, returning empty members or blank name/lead can destabilize interactive story behavior and hide UI issues.

Suggested fix
-    onCreateTeam: async (_d, data) => ({ name: data.name, lead: data.lead, members: [] }),
-    onUpdateTeam: async (_d, _t, data) => ({ name: data.name ?? '', lead: data.lead ?? '', members: [] }),
+    onCreateTeam: async (_d, data) => ({
+      name: data.name,
+      lead: data.lead,
+      members: [data.lead],
+    }),
+    onUpdateTeam: async (_d, _t, data) => {
+      const base = mockConfig.departments[0]!.teams[0]!
+      return {
+        name: data.name ?? base.name,
+        lead: data.lead ?? base.lead,
+        members: base.members,
+      }
+    },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/org-edit/DepartmentsTab.stories.tsx` around lines 56 - 59, The
mock team callbacks return empty/blank fields which can produce invalid story
state; update onCreateTeam and onUpdateTeam to always return a valid team object
by using data values with safe fallbacks (e.g., data.name ?? 'New Team',
data.lead ?? 'Unassigned') and ensure members is a non-empty array (e.g.,
preserve data.members if present or create a default member object using the
lead), and adjust onDeleteTeam and onReorderTeams to return the expected stable
result for the story (such as a truthy success value or the updated team list)
instead of no-op to keep interactive stories stable; target the functions named
onCreateTeam, onUpdateTeam, onDeleteTeam and onReorderTeams when making these
changes.

Comment on lines +34 to +44
const preview = useMemo(() => {
if (!pack) return null
// Estimate pack department budgets from the pack's department_count.
// The actual budget percents aren't in PackInfoResponse, so we use
// a placeholder for the preview table. The real numbers come from
// the API response after apply.
const packDepts = pack.department_count > 0
? [{ name: `${pack.display_name} dept`, budget_percent: 8 }]
: []
return computeBudgetPreview(currentDepartments, packDepts)
}, [pack, currentDepartments])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Hardcoded placeholder budget may mislead users.

The preview uses a hardcoded budget_percent: 8 placeholder since PackInfoResponse lacks actual department budget data. This estimate may differ significantly from the real values returned after apply, potentially confusing users.

Consider either:

  1. Extending PackInfoResponse to include total_budget_percent
  2. Adding a prominent disclaimer that preview values are estimates
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/org-edit/PackApplyPreviewDialog.tsx` around lines 34 - 44, The
preview currently injects a hardcoded budget_percent (8) into packDepts inside
the preview memo, which can mislead users; update the logic in the preview
computation to first use a new optional field from PackInfoResponse (e.g.,
pack.total_budget_percent) when present and only fall back to a clearly labeled
estimate instead of silently hardcoding 8, and add a visible disclaimer element
in PackApplyPreviewDialog rendered alongside the computed preview (when pack &&
pack.department_count>0) that explicitly states "Estimated values — final values
come from API after apply"; adjust computeBudgetPreview calls to accept the
optional total_budget_percent from pack and ensure the disclaimer is shown
whenever the fallback estimate is used.

Comment on lines +23 to +27
const [reassignTo, setReassignTo] = useState('')

const memberCount = team?.members.length ?? 0
const hasMembers = memberCount > 0
const hasSiblings = siblingTeams.length > 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

State not reset when team prop changes while dialog is open.

If the team prop changes while the dialog is open (e.g., parent updates state), reassignTo could contain a stale value from the previous team's sibling list. Consider resetting the state when team changes:

+import { useState, useEffect } from 'react'
+// ...
 const [reassignTo, setReassignTo] = useState('')
+
+useEffect(() => {
+  setReassignTo('')
+}, [team?.name])

This is a minor edge case since the dialog is typically closed before selecting a different team.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const [reassignTo, setReassignTo] = useState('')
const memberCount = team?.members.length ?? 0
const hasMembers = memberCount > 0
const hasSiblings = siblingTeams.length > 0
import { useState, useEffect } from 'react'
const [reassignTo, setReassignTo] = useState('')
useEffect(() => {
setReassignTo('')
}, [team?.name])
const memberCount = team?.members.length ?? 0
const hasMembers = memberCount > 0
const hasSiblings = siblingTeams.length > 0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/org-edit/TeamDeleteConfirmDialog.tsx` around lines 23 - 27,
TeamDeleteConfirmDialog can retain a stale reassignTo when the team prop
changes; add a useEffect inside the TeamDeleteConfirmDialog component that
watches the team prop and calls setReassignTo('') (or another appropriate
default) whenever team changes to clear any previous selection and avoid
referencing siblings from the prior team.

#1079: Team CRUD endpoints (POST/PATCH/DELETE/reorder) as sub-resource
of departments. Frontend: TeamListSection with @dnd-kit reorder,
TeamEditDialog (create/edit), TeamDeleteConfirmDialog with reassign
option, integrated into DepartmentEditDrawer replacing read-only
section. Org chart: TeamGroupNode as intermediate grouping layer
between department and agent nodes in build-org-tree.

#1080: Budget rebalance utility (compute_rebalance with none/
scale_existing/reject_if_over modes). Pack-apply endpoint extended
with rebalance_mode param (default scale_existing) and budget
before/after response fields. PackApplyPreviewDialog shows budget
impact with rebalance strategy selector before applying packs with
departments. Live budget total validation in DepartmentEditDrawer.
Under-budget warning log in Company model validator.

Closes #1079
Closes #1080
- Fix self-reassignment bug on team delete (reject reassign_to=self)
- Add self-reassign test case
- Make TeamConfig.lead required (matches backend TeamResponse)
- Fix untrimmed member names in TeamEditDialog
- Fix irreversible select in TeamDeleteConfirmDialog
- Align dialog styling with established pattern (rounded-xl, bg-surface, p-card)
- Remove dead departmentName prop from TeamListSection
- Fix variable shadowing in PackSelectionDialog onApply
- Use dedicated COMPANY_BUDGET_UNDER_ALLOCATED event constant
- Add logger to rebalance.py module
- Remove orphaned comment in teams.py

Pre-reviewed by 3 agents, 10 findings addressed
- template_packs.py: re-raise ConflictError explicitly, add debug log for SettingNotFoundError
- budget.ts: align preview precision with backend (10 decimals), fix scaleFactor for currentTotal <= 0
- company.ts: refetch after deleteTeam with reassignment
- TeamDeleteConfirmDialog: reset state on close (removed useEffect to fix lint warning)
- DepartmentEditDrawer: add under-allocation warning
- DepartmentsTab.stories: fix team callback mocks with valid returns
- DepartmentEditDrawer.stories: fix team callback mocks with valid returns
- test_template_packs_rebalance.py: add assertion in _seed_departments
- docs/design/page-structure.md: remove collaboration/autonomy from deferred, add endpoints
- web/src/api/types.ts: add reporting_lines and policies to Department type
@Aureliolo Aureliolo force-pushed the feat/team-editing-budget-rebalance branch from affabdc to af02b46 Compare April 6, 2026 18:17
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview April 6, 2026 18:18 — with GitHub Actions Inactive
onUpdateTeam second param is teamName (string), not a team object.
onDeleteTeam and onReorderTeams must return Promise<void>, not Promise<boolean>.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/unit/api/controllers/test_teams.py`:
- Around line 378-444: Add a new test method in the TestReorderTeams class that
seeds a department with zero teams (use _seed_departments and _dept_with_teams
with teams=[]), then call
test_client.patch("/api/v1/departments/engineering/teams/reorder",
json={"team_names": []}) and assert a 200 response and that resp.json()["data"]
is an empty list; reference TestReorderTeams, _seed_departments,
_dept_with_teams, and the PATCH endpoint to locate where to add the case.
- Around line 14-24: The helper _seed_departments should assert the PUT
succeeded: capture the response from test_client.put in _seed_departments (the
call to "/api/v1/settings/company/departments") and assert the response status
is successful (e.g. response.status_code == 200 or response.status_code < 400),
including the response.text in the assertion message for easier debugging;
update the function _seed_departments to perform this check after calling
test_client.put.

In `@tests/unit/budget/test_rebalance.py`:
- Around line 135-148: Update the test_zero_existing_budgets_over_max unit test
to also assert the computed new_total to document expected behavior; after
calling compute_rebalance (variable result) add an assertion that
result.new_total == 110 so the test verifies that new departments alone sum to
110 while scale_factor == 0.0 and rejected is False.

In `@web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx`:
- Around line 46-57: The onCreateTeam mock can return an empty members array
which may diverge from UI expectations; update the onCreateTeam handler to
default members to include the lead when data.members is missing (e.g. use
data.members ?? [data.lead ?? 'Unassigned']) so new teams always include at
least the lead, while leaving onUpdateTeam behaviour unchanged.

In `@web/src/pages/org-edit/DepartmentsTab.stories.tsx`:
- Around line 56-67: The mocks for onCreateTeam and onUpdateTeam set members to
data.members ?? [] which can yield an empty members list; if the UI requires the
team lead to also be in members, update onCreateTeam and onUpdateTeam to ensure
the members fallback includes the lead (e.g., when data.members is undefined,
set members to an array that contains data.lead ?? 'Unassigned' or preserves
_t.lead), while keeping the existing fallback behavior for name and lead.

In `@web/src/pages/org-edit/TeamListSection.tsx`:
- Around line 140-153: The catch block in handleDeleteConfirm does not log the
caught error, making debugging harder; update handleDeleteConfirm to catch the
error as a variable (e.g., catch (err)), send that error to the logger (e.g.,
console.error or the app logger) with context including teamName and reassignTo,
and then show the toast via useToastStore.getState().add as before; reference
the handleDeleteConfirm function and the onDeleteTeam call so you update the
existing catch block surrounding await onDeleteTeam(teamName, reassignTo).
- Around line 120-138: The catch block in handleDragEnd swallows the error;
import createLogger from '@/lib/logger', instantiate a logger for this module
(e.g., for "TeamListSection"), and in the catch of the handleDragEnd async
function call logger.error with the caught error plus context (active.id,
over.id, oldIndex/newIndex or the reordered array and the onReorderTeams target)
alongside the existing toast so failures are both user-visible and recorded for
debugging.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7f2f0152-dece-477c-8fec-9ea3dd86c4f0

📥 Commits

Reviewing files that changed from the base of the PR and between affabdc and af02b46.

📒 Files selected for processing (35)
  • docs/design/page-structure.md
  • src/synthorg/api/controllers/__init__.py
  • src/synthorg/api/controllers/teams.py
  • src/synthorg/api/controllers/template_packs.py
  • src/synthorg/budget/__init__.py
  • src/synthorg/budget/rebalance.py
  • src/synthorg/core/company.py
  • src/synthorg/observability/events/api.py
  • src/synthorg/observability/events/company.py
  • src/synthorg/observability/events/template.py
  • tests/unit/api/controllers/test_teams.py
  • tests/unit/api/controllers/test_template_packs_rebalance.py
  • tests/unit/budget/test_rebalance.py
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsx
  • web/src/__tests__/pages/org-edit/DepartmentsTab.test.tsx
  • web/src/__tests__/pages/org/build-org-tree.test.ts
  • web/src/api/endpoints/company.ts
  • web/src/api/types.ts
  • web/src/hooks/useOrgEditData.ts
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/pages/org-edit/TeamDeleteConfirmDialog.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/pages/org/TeamGroupNode.tsx
  • web/src/pages/org/build-org-tree.ts
  • web/src/stores/company.ts
  • web/src/utils/budget.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Deploy Preview
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (9)
web/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{ts,tsx,js,jsx}: Always use createLogger from @/lib/logger -- never bare console.warn/console.error/console.debug in application code
Logger variable name must always be const log (e.g. const log = createLogger('module-name'))
Pass dynamic/untrusted values as separate arguments to logger methods (not interpolated into the message string) so they go through sanitizeArg
Attacker-controlled fields inside structured objects must be wrapped in sanitizeForLog() before embedding in log calls

Files:

  • web/src/__tests__/pages/org-edit/DepartmentsTab.test.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsx
  • web/src/__tests__/pages/org/build-org-tree.test.ts
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/pages/org/TeamGroupNode.tsx
  • web/src/hooks/useOrgEditData.ts
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/api/endpoints/company.ts
  • web/src/pages/org-edit/TeamDeleteConfirmDialog.tsx
  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/utils/budget.ts
  • web/src/pages/org/build-org-tree.ts
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/api/types.ts
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/stores/company.ts
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-*)) for colors; NEVER hardcode hex values in .tsx/.ts files
Use font-sans or font-mono (Geist tokens) for typography; NEVER set fontFamily directly in .tsx/.ts files
Use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing; NEVER hardcode pixel values for layout spacing in components
Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders; NEVER hardcode values in .tsx/.ts files
Use @/lib/motion presets for Framer Motion transition durations; NEVER hardcode transition durations
CSS side-effect imports in TypeScript 6 require type declarations -- add /// <reference types="vite/client" /> at the top of files with CSS imports

web/src/**/*.{ts,tsx}: ALWAYS reuse existing components from web/src/components/ui/ in React code before creating new ones.
NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions in React code -- use design tokens from @/lib/design-tokens.ts and presets from @/lib/motion.
A PostToolUse hook (scripts/check_web_design_system.py) enforces design system rules on every Edit/Write to web/src/ files. Follow the design token and motion preset rules.
Use TypeScript 6.0+ with strict type checking for React code in the web dashboard.
Run eslint with zero warnings for the web dashboard on all TypeScript/React files. eslint-web pre-commit hook enforces this.
Use React 19 and Framer Motion for the web dashboard. See web/CLAUDE.md for component inventory and design system reference.

Files:

  • web/src/__tests__/pages/org-edit/DepartmentsTab.test.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/pages/OrgEditPage.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsx
  • web/src/__tests__/pages/org/build-org-tree.test.ts
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/pages/org/TeamGroupNode.tsx
  • web/src/hooks/useOrgEditData.ts
  • web/src/pages/org-edit/DepartmentsTab.tsx
  • web/src/api/endpoints/company.ts
  • web/src/pages/org-edit/TeamDeleteConfirmDialog.tsx
  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/utils/budget.ts
  • web/src/pages/org/build-org-tree.ts
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/api/types.ts
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/stores/company.ts
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
web/src/**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.test.{ts,tsx}: Configure async leak detection with --detect-async-leaks flag when running tests for the React web dashboard.
Use fast-check for property-based testing in React with fc.assert and fc.property instead of Hypothesis.

Files:

  • web/src/__tests__/pages/org-edit/DepartmentsTab.test.tsx
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsx
  • web/src/__tests__/pages/org/build-org-tree.test.ts
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: In Python code, do NOT use from __future__ import annotations -- Python 3.14 has PEP 649 native lazy annotations.
In Python 3.14 code, use PEP 758 except syntax: use except A, B: (no parentheses) -- ruff enforces this.
All public functions and methods in Python must have type hints, with mypy strict mode enforced.
All public classes and functions in Python must have Google-style docstrings, enforced by ruff D rules.
Line length for Python code is 88 characters (enforced by ruff formatter).

Files:

  • src/synthorg/api/controllers/__init__.py
  • src/synthorg/observability/events/template.py
  • src/synthorg/observability/events/company.py
  • src/synthorg/budget/__init__.py
  • src/synthorg/core/company.py
  • src/synthorg/observability/events/api.py
  • tests/unit/api/controllers/test_template_packs_rebalance.py
  • tests/unit/budget/test_rebalance.py
  • src/synthorg/api/controllers/template_packs.py
  • src/synthorg/budget/rebalance.py
  • tests/unit/api/controllers/test_teams.py
  • src/synthorg/api/controllers/teams.py
src/synthorg/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

src/synthorg/**/*.py: Create new objects instead of mutating existing ones. For non-Pydantic internal collections (registries, BaseTool), use copy.deepcopy() at construction and wrap with MappingProxyType for read-only enforcement.
Distinguish between frozen Pydantic models for config/identity and separate mutable-via-copy models using model_copy(update=...) for runtime state that evolves. Never mix static config fields with mutable runtime fields in one model.
Use Pydantic v2 (BaseModel, model_validator, computed_field, ConfigDict) with allow_inf_nan=False in all ConfigDict declarations to reject NaN/Inf in numeric fields at validation time.
Use @computed_field for derived values in Pydantic models instead of storing and validating redundant fields (e.g., TokenUsage.total_tokens).
Use NotBlankStr (from core.types) for all identifier/name fields in Pydantic models -- including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants -- instead of manual whitespace validators.
Prefer asyncio.TaskGroup for fan-out/fan-in parallel operations in new Python code (e.g., multiple tool invocations, parallel agent calls) instead of bare create_task. Use structured concurrency.
Keep Python functions to less than 50 lines and files to less than 800 lines.
Handle Python errors explicitly and never silently swallow exceptions.
Validate at system boundaries (user input, external APIs, config files) in Python code.
In Python logging, always use constants from domain-specific modules under synthorg.observability.events for event names (e.g., API_REQUEST_STARTED from events.api). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.
In Python logging, use structured kwargs with logger.info(EVENT, key=value) format -- never use logger.info("msg %s", val) format.
All Python error paths must log at WARNING or ERROR with context before raising exceptions.
All Python state transitions mu...

Files:

  • src/synthorg/api/controllers/__init__.py
  • src/synthorg/observability/events/template.py
  • src/synthorg/observability/events/company.py
  • src/synthorg/budget/__init__.py
  • src/synthorg/core/company.py
  • src/synthorg/observability/events/api.py
  • src/synthorg/api/controllers/template_packs.py
  • src/synthorg/budget/rebalance.py
  • src/synthorg/api/controllers/teams.py
src/**/*.py

⚙️ CodeRabbit configuration file

This project uses Python 3.14+ with PEP 758 except syntax: "except A, B:" (comma-separated, no parentheses) is correct and mandatory -- do NOT flag it as a typo or suggest parenthesized form. The "except builtins.MemoryError, RecursionError: raise" pattern is intentional project convention for system-error propagation. When evaluating the 50-line function limit, count only the function body excluding the signature lines, decorators, and docstring. Functions 1-5 lines over due to docstrings or multi-line signatures should not be flagged. Do not suggest extracting single-use helper functions called exactly once -- this reduces readability without improving maintainability.

Files:

  • src/synthorg/api/controllers/__init__.py
  • src/synthorg/observability/events/template.py
  • src/synthorg/observability/events/company.py
  • src/synthorg/budget/__init__.py
  • src/synthorg/core/company.py
  • src/synthorg/observability/events/api.py
  • src/synthorg/api/controllers/template_packs.py
  • src/synthorg/budget/rebalance.py
  • src/synthorg/api/controllers/teams.py
web/src/**/*.stories.tsx

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.stories.tsx: For Storybook stories with tags: ['autodocs'], ensure @storybook/addon-docs is installed and added to addons
Use storybook/test and storybook/actions import paths in Storybook stories (not @storybook/test or @storybook/addon-actions)

Files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
docs/design/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Update relevant docs/design/ pages to reflect implementation changes when deviations from the design spec occur.

Files:

  • docs/design/page-structure.md
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Mark Python tests with markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow.
Prefer @pytest.mark.parametrize for testing similar cases in Python.
Use generic test names in Python tests: test-provider, test-small-001 instead of real vendor names (Anthropic, OpenAI, Claude, GPT).
For timing-sensitive Python tests, mock time.monotonic() and asyncio.sleep() to make them deterministic instead of widening timing margins. Use asyncio.Event().wait() for indefinite blocking.
Run pytest unit tests on affected Python modules before push. Foundational module changes (core, config, observability) or conftest changes trigger full runs.

Files:

  • tests/unit/api/controllers/test_template_packs_rebalance.py
  • tests/unit/budget/test_rebalance.py
  • tests/unit/api/controllers/test_teams.py

⚙️ CodeRabbit configuration file

Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare @settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which @given() honors automatically.

Files:

  • tests/unit/api/controllers/test_template_packs_rebalance.py
  • tests/unit/budget/test_rebalance.py
  • tests/unit/api/controllers/test_teams.py
🧠 Learnings (78)
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.test.{ts,tsx} : Web dashboard: Use React Hypothesis (fast-check) for property-based testing with fc.assert + fc.property

Applied to files:

  • web/src/__tests__/pages/org-edit/DepartmentsTab.test.tsx
  • web/src/__tests__/pages/OrgEditPage.test.tsx
  • web/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsx
  • web/src/__tests__/pages/org/build-org-tree.test.ts
📚 Learning: 2026-04-06T16:36:13.074Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T16:36:13.074Z
Learning: Applies to web/src/**/*.test.{ts,tsx} : Configure async leak detection with `--detect-async-leaks` flag when running tests for the React web dashboard.

Applied to files:

  • web/src/__tests__/pages/org-edit/DepartmentsTab.test.tsx
  • web/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsx
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/__tests__/**/*.test.{ts,tsx} : Use property-based testing with fast-check in React tests (`fc.assert` + `fc.property`)

Applied to files:

  • web/src/__tests__/pages/org-edit/DepartmentsTab.test.tsx
📚 Learning: 2026-04-06T16:36:13.074Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T16:36:13.074Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use React 19 and Framer Motion for the web dashboard. See `web/CLAUDE.md` for component inventory and design system reference.

Applied to files:

  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/TeamGroupNode.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/api/**/*.py : API package (api/): Litestar REST + WebSocket with controllers, guards, channels, JWT + API key + WS ticket auth, approval gate integration, coordination endpoint, collaboration endpoint, settings endpoint, provider management endpoint (CRUD + test + presets), backup endpoint, RFC 9457 structured errors, AppState hot-reload slots, service auto-wiring (Phase 1 at construction, Phase 2 on startup), lifecycle helpers

Applied to files:

  • src/synthorg/api/controllers/__init__.py
  • src/synthorg/api/controllers/teams.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/**/*.py : Package structure: src/synthorg/ organized as: api/ (REST+WebSocket, Litestar), auth/ (auth subpackage), backup/ (scheduled/manual backups), budget/ (cost tracking, CFO), cli/ (superseded by Go CLI), communication/ (message bus, meetings), config/ (YAML loading), core/ (domain models, resilience config), engine/ (orchestration, task state, coordination, approval gates, stagnation detection, context budget, compaction), hr/ (hiring, performance, promotion), memory/ (pluggable backend, Mem0, retrieval, consolidation), persistence/ (operational data, SQLite, settings), observability/ (logging, correlation, sinks), providers/ (LLM abstraction, LiteLLM, auth types, presets, runtime CRUD), settings/ (runtime-editable, typed definitions, encryption, config bridge), security/ (SecOps, rule engine, output scanning, progressive trust, autonomy levels), templates/ (company templates, personalities), tools/ (registry, built-in tools, git, sandbox, code_runner, MCP...

Applied to files:

  • src/synthorg/api/controllers/__init__.py
  • src/synthorg/budget/__init__.py
📚 Learning: 2026-03-26T15:18:16.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-26T15:18:16.848Z
Learning: Applies to src/synthorg/api/**/*.py : Litestar API must include setup wizard, auth/, auto-wiring, and lifecycle management

Applied to files:

  • src/synthorg/api/controllers/__init__.py
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to src/synthorg/observability/**/*.py : Observability package (observability/): structured logging, correlation tracking, log sinks; event constants organized by domain under observability/events/ (e.g., events.api, events.tool, events.git, events.context_budget, events.backup)

Applied to files:

  • src/synthorg/observability/events/template.py
  • src/synthorg/observability/events/company.py
  • src/synthorg/core/company.py
  • src/synthorg/observability/events/api.py
📚 Learning: 2026-03-15T18:28:13.207Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:28:13.207Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from domain-specific modules under synthorg.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • src/synthorg/observability/events/template.py
  • src/synthorg/observability/events/company.py
  • src/synthorg/core/company.py
  • src/synthorg/observability/events/api.py
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)

Applied to files:

  • src/synthorg/observability/events/template.py
  • src/synthorg/observability/events/company.py
  • src/synthorg/budget/__init__.py
  • src/synthorg/core/company.py
  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/utils/budget.ts
  • tests/unit/budget/test_rebalance.py
  • src/synthorg/api/controllers/template_packs.py
  • src/synthorg/budget/rebalance.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to src/**/*.py : Use event name constants from domain-specific modules under ai_company.observability.events (e.g., PROVIDER_CALL_START from events.provider, BUDGET_RECORD_ADDED from events.budget, etc.) — import directly

Applied to files:

  • src/synthorg/observability/events/company.py
  • src/synthorg/core/company.py
  • src/synthorg/observability/events/api.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : Use event name constants from domain-specific modules under `ai_company.observability.events` (e.g., `PROVIDER_CALL_START` from `events.provider`). Import directly: `from ai_company.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • src/synthorg/observability/events/company.py
  • src/synthorg/core/company.py
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to {pyproject.toml,src/synthorg/__init__.py} : Update version in `pyproject.toml` (`[tool.commitizen].version`) and `src/synthorg/__init__.py` (`__version__`)

Applied to files:

  • src/synthorg/budget/__init__.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.

Applied to files:

  • src/synthorg/budget/__init__.py
  • src/synthorg/core/company.py
  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/utils/budget.ts
  • tests/unit/budget/test_rebalance.py
  • src/synthorg/budget/rebalance.py
📚 Learning: 2026-04-06T16:36:13.074Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T16:36:13.074Z
Learning: Applies to pyproject.toml|src/synthorg/__init__.py : Version synthorg is stored in `pyproject.toml` (`[tool.commitizen].version`) and `src/synthorg/__init__.py` (`__version__`). Keep them synchronized.

Applied to files:

  • src/synthorg/budget/__init__.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Budget: Cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError).

Applied to files:

  • src/synthorg/budget/__init__.py
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: import from `storybook/test` (not `storybook/test`), `storybook/actions` (not `storybook/addon-actions`)

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: Use storybook/test (not storybook/test) and storybook/actions (not storybook/addon-actions) import paths

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/components/ui/**/*.stories.tsx : Create a `.stories.tsx` file alongside each new shared component with all states (default, hover, loading, error, empty)

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: Import from `storybook/test` instead of `storybook/test`

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/**/*.stories.tsx : Use `storybook/test` and `storybook/actions` import paths in Storybook stories (not `storybook/test` or `storybook/addon-actions`)

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Create new shared components in `web/src/components/ui/` with `.stories.tsx` Storybook file covering all states (default, hover, loading, error, empty)

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: Use `parameters.a11y.test: 'error' | 'todo' | 'off'` for a11y testing configuration (replaces old `.element` and `.manual`)

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: Use parameters.backgrounds.options (object keyed by name) + initialGlobals.backgrounds.value for background options (replaces old default + values array)

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/.storybook/**/*.{ts,js} : Use `defineMain` from `storybook/react-vite/node` and `definePreview` from `storybook/react-vite` for type-safe Storybook configuration

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from web/src/components/ui/ (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast/ToastContainer, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem, Drawer, form fields, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle, LiveRegion, MobileUnsupportedOverlay, LazyCodeMirrorEditor) before creating new components

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsx
  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{tsx,ts} : ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle, LiveRegion, MobileUnsupportedOverlay, LazyCodeMirrorEditor, TagInput, MetadataGrid, ProjectStatusBadge, ContentTypeBadge)

Applied to files:

  • web/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsx
  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
📚 Learning: 2026-03-27T12:44:29.466Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones

Applied to files:

  • web/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsx
  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/__tests__/**/*.{test,spec}.{ts,tsx} : Vitest unit tests must use coverage scoped to files changed vs origin/main branch

Applied to files:

  • web/src/__tests__/pages/org/build-org-tree.test.ts
📚 Learning: 2026-03-20T08:28:32.845Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T08:28:32.845Z
Learning: Applies to web/src/__tests__/**/*.{ts,js} : Dashboard testing: Vitest unit tests organized by feature under `web/src/__tests__/`. Use fast-check for property-based testing (`fc.assert` + `fc.property`).

Applied to files:

  • web/src/__tests__/pages/org/build-org-tree.test.ts
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Applies to docs/design/*.md : Update the relevant `docs/design/` page when approved deviations occur to reflect the new reality

Applied to files:

  • docs/design/page-structure.md
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to docs/design/**/*.md : Design specification pages in `docs/design/` must be consulted before implementing features (7 pages: index, agents, organization, communication, engine, memory, operations)

Applied to files:

  • docs/design/page-structure.md
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Update the relevant `docs/design/` page to reflect new reality when approved deviations from spec occur

Applied to files:

  • docs/design/page-structure.md
📚 Learning: 2026-03-19T07:12:14.508Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:12:14.508Z
Learning: Applies to docs/design/*.md : Design spec pages: 7 pages in `docs/design/` — index, agents, organization, communication, engine, memory, operations

Applied to files:

  • docs/design/page-structure.md
📚 Learning: 2026-04-06T16:36:13.074Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T16:36:13.074Z
Learning: Applies to docs/design/**/*.md : Update relevant `docs/design/` pages to reflect implementation changes when deviations from the design spec occur.

Applied to files:

  • docs/design/page-structure.md
📚 Learning: 2026-03-18T08:23:08.912Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T08:23:08.912Z
Learning: When approved deviations occur, update the relevant `docs/design/` page to reflect the new reality.

Applied to files:

  • docs/design/page-structure.md
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Always read the relevant `docs/design/` page before implementing any feature or planning any issue — DESIGN_SPEC.md is a pointer file linking to 7 design pages (Agents, Organization, Communication, Engine, Memory, Operations)

Applied to files:

  • docs/design/page-structure.md
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Engine: Agent orchestration, execution loops, parallel execution, task decomposition, routing, task assignment, centralized single-writer task state engine (TaskEngine), task lifecycle, recovery, shutdown, workspace isolation, coordination (multi-agent pipeline: TopologyDispatcher protocol, 4 dispatchers — SAS/centralized/decentralized/context-dependent, wave execution, workspace lifecycle integration, CoordinationSectionConfig company config bridge, build_coordinator factory), coordination error classification, prompt policy validation, checkpoint recovery (checkpoint/, per-turn persistence, heartbeat detection, CheckpointRecoveryStrategy), approval gate (escalation detection, context parking/resume, EscalationInfo/ResumePayload models), stagnation detection (stagnation/, StagnationDetector protocol, ToolRepetitionDetector, dual-signal analysis, corrective prompt injection), agent runtime state (AgentRuntimeState, lightweight per-agent execution status for dashboard queries and recove...

Applied to files:

  • docs/design/page-structure.md
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to docs/** : Docs source in docs/ (Markdown, built with Zensical); design spec in docs/design/ (7 pages: index, agents, organization, communication, engine, memory, operations)

Applied to files:

  • docs/design/page-structure.md
📚 Learning: 2026-03-15T21:20:09.993Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:20:09.993Z
Learning: Applies to web/src/components/** : Vue components organized by feature (agents/, approvals/, budget/, common/, dashboard/, layout/, messages/, org-chart/, tasks/).

Applied to files:

  • docs/design/page-structure.md
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: Use parameters.a11y.test: 'error' | 'todo' | 'off' for a11y testing (replaces old .element and .manual); set globally in preview.tsx to enforce WCAG compliance on all stories

Applied to files:

  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/.storybook/**/*.{ts,tsx} : In Storybook 10, use `parameters.backgrounds.options` (object keyed by name) + `initialGlobals.backgrounds.value` for backgrounds API (replaces old `default` + `values` array)

Applied to files:

  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-04-06T16:36:13.074Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T16:36:13.074Z
Learning: Applies to src/synthorg/**/*.py : In Python logging, always use constants from domain-specific modules under `synthorg.observability.events` for event names (e.g., `API_REQUEST_STARTED` from `events.api`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • src/synthorg/core/company.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`). Import directly: `from synthorg.observability.events.<domain> import EVENT_CONSTANT`.

Applied to files:

  • src/synthorg/core/company.py
  • src/synthorg/observability/events/api.py
📚 Learning: 2026-03-16T06:24:56.341Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T06:24:56.341Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from the domain-specific module under `synthorg.observability.events` in logging calls

Applied to files:

  • src/synthorg/core/company.py
📚 Learning: 2026-03-31T16:09:24.320Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T16:09:24.320Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from `synthorg.observability.events.<domain>` modules (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly and use in structured logging

Applied to files:

  • src/synthorg/core/company.py
  • src/synthorg/observability/events/api.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to src/ai_company/!(observability)/**/*.py : All error paths must log at WARNING or ERROR with context before raising.

Applied to files:

  • src/synthorg/core/company.py
📚 Learning: 2026-03-15T18:38:44.202Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:38:44.202Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic must import `from synthorg.observability import get_logger` and define `logger = get_logger(__name__)`

Applied to files:

  • src/synthorg/core/company.py
📚 Learning: 2026-03-20T11:18:48.128Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T11:18:48.128Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have `from synthorg.observability import get_logger` followed by `logger = get_logger(__name__)`.

Applied to files:

  • src/synthorg/core/company.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Every module with business logic MUST have: `from synthorg.observability import get_logger` then `logger = get_logger(__name__)`. Never use import logging / logging.getLogger() / print() in application code.

Applied to files:

  • src/synthorg/core/company.py
📚 Learning: 2026-03-17T06:43:14.114Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:43:14.114Z
Learning: Applies to src/synthorg/**/*.py : All error paths must log at WARNING or ERROR with context before raising. All state transitions must log at INFO. DEBUG for object creation, internal flow, entry/exit of key functions. Pure data models, enums, and re-exports do NOT need logging.

Applied to files:

  • src/synthorg/core/company.py
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/components/ui/**/*.tsx : Export props as a TypeScript interface for new components

Applied to files:

  • web/src/pages/org/TeamGroupNode.tsx
  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-27T22:32:26.927Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:32:26.927Z
Learning: Applies to web/src/components/ui/*.{tsx,ts} : For new shared React components: place in web/src/components/ui/ with kebab-case filename, create .stories.tsx with all states, export props as TypeScript interface, use design tokens exclusively

Applied to files:

  • web/src/pages/org/TeamGroupNode.tsx
  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use React 19, TypeScript 6.0+, and design system tokens from shadcn/ui + Tailwind CSS 4 + Radix UI in web dashboard

Applied to files:

  • web/src/pages/org/TeamGroupNode.tsx
  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/api/types.ts
  • web/src/pages/org-edit/TeamListSection.tsx
  • web/src/pages/org-edit/DepartmentEditDrawer.tsx
📚 Learning: 2026-04-02T07:18:02.381Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-02T07:18:02.381Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly from the domain module

Applied to files:

  • src/synthorg/observability/events/api.py
📚 Learning: 2026-03-18T21:23:23.586Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-18T21:23:23.586Z
Learning: Applies to src/synthorg/**/*.py : Event names: always use constants from the domain-specific module under synthorg.observability.events (e.g., API_REQUEST_STARTED from events.api, TOOL_INVOKE_START from events.tool). Import directly from synthorg.observability.events.<domain>.

Applied to files:

  • src/synthorg/observability/events/api.py
📚 Learning: 2026-03-19T11:33:01.580Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:33:01.580Z
Learning: Applies to src/synthorg/**/*.py : Use event constants from `synthorg.observability.events.<domain>` (e.g., `API_REQUEST_STARTED` from `events.api`); import directly and log with structured kwargs: `logger.info(EVENT, key=value)`, never interpolated strings

Applied to files:

  • src/synthorg/observability/events/api.py
📚 Learning: 2026-03-15T19:14:27.144Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T19:14:27.144Z
Learning: Applies to src/synthorg/**/*.py : Use event name constants from synthorg.observability.events domain-specific modules (e.g., PROVIDER_CALL_START from events.provider). Import directly: from synthorg.observability.events.<domain> import EVENT_CONSTANT.

Applied to files:

  • src/synthorg/observability/events/api.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to src/synthorg/**/*.py : Always use event name constants from domain-specific modules under `synthorg.observability.events` (e.g., `API_REQUEST_STARTED` from `events.api`, `TOOL_INVOKE_START` from `events.tool`); import directly rather than using string literals

Applied to files:

  • src/synthorg/observability/events/api.py
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Zustand stores for state management in the web dashboard; each domain has its own store module (auth, WebSocket, toast, analytics, setup, company, agents, budget, tasks, settings, providers, theme, per-domain stores)

Applied to files:

  • web/src/hooks/useOrgEditData.ts
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/stores/company.ts
📚 Learning: 2026-03-27T12:44:29.466Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/stores/**/*.{ts,tsx} : Use Zustand stores in web dashboard for state management (auth, WebSocket, toast, analytics, domain shells)

Applied to files:

  • web/src/hooks/useOrgEditData.ts
  • web/src/pages/org-edit/PackSelectionDialog.tsx
  • web/src/stores/company.ts
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : For Base UI primitives, import from specific subpaths (e.g. `import { Dialog } from 'base-ui/react/dialog'`) and use the component's `render` prop for polymorphism

Applied to files:

  • web/src/pages/org-edit/TeamDeleteConfirmDialog.tsx
  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : For Base UI Dialog, AlertDialog, and Popover components, compose with `Portal` + `Backdrop` + `Popup`. Popover and Menu additionally require a `Positioner` wrapper with `side` / `align` / `sideOffset` props

Applied to files:

  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/pages/org-edit/TeamEditDialog.tsx
  • web/src/pages/org-edit/PackSelectionDialog.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Export component props as a TypeScript interface for all new components

Applied to files:

  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-06T16:36:13.074Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T16:36:13.074Z
Learning: Applies to web/src/**/*.{ts,tsx} : ALWAYS reuse existing components from `web/src/components/ui/` in React code before creating new ones.

Applied to files:

  • web/src/pages/org-edit/PackApplyPreviewDialog.tsx
📚 Learning: 2026-04-06T16:36:13.074Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T16:36:13.074Z
Learning: Applies to tests/**/*.py : Run pytest unit tests on affected Python modules before push. Foundational module changes (core, config, observability) or conftest changes trigger full runs.

Applied to files:

  • tests/unit/budget/test_rebalance.py
  • tests/unit/api/controllers/test_teams.py
📚 Learning: 2026-04-06T16:36:13.074Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T16:36:13.074Z
Learning: Applies to src/synthorg/**/*.py : Run mypy type-check on affected Python modules before push. Foundational module changes (core, config, observability) or conftest changes trigger full runs.

Applied to files:

  • tests/unit/budget/test_rebalance.py
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT build card-with-header layouts from scratch; use `<SectionCard>`

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared web components, place in web/src/components/ui/ with kebab-case filename, create .stories.tsx alongside with all states (default, hover, loading, error, empty), export props as TypeScript interface, use design tokens exclusively with no hardcoded colors/fonts/spacing, and import cn from `@/lib/utils` for conditional class merging

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Do NOT build card-with-header layouts from scratch -- use `<SectionCard>` from `@/components/ui/section-card`

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-06T16:36:13.073Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T16:36:13.073Z
Learning: Applies to **/*.py : In Python 3.14 code, use PEP 758 except syntax: use `except A, B:` (no parentheses) -- ruff enforces this.

Applied to files:

  • src/synthorg/api/controllers/teams.py
📚 Learning: 2026-03-31T21:07:37.470Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T21:07:37.470Z
Learning: Applies to **/*.py : Use `except A, B:` (no parentheses) per PEP 758 exception syntax on Python 3.14

Applied to files:

  • src/synthorg/api/controllers/teams.py
📚 Learning: 2026-03-14T15:43:05.601Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T15:43:05.601Z
Learning: Applies to **/*.py : Use PEP 758 except syntax: `except A, B:` (no parentheses) — enforced by ruff on Python 3.14

Applied to files:

  • src/synthorg/api/controllers/teams.py
📚 Learning: 2026-03-16T07:22:28.134Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T07:22:28.134Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (no parentheses) for exception handling — PEP 758 exception syntax enforced by ruff on Python 3.14

Applied to files:

  • src/synthorg/api/controllers/teams.py
📚 Learning: 2026-03-14T16:18:57.267Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-14T16:18:57.267Z
Learning: Applies to **/*.py : Use PEP 758 except syntax with `except A, B:` (no parentheses) for multiple exceptions—ruff enforces this on Python 3.14.

Applied to files:

  • src/synthorg/api/controllers/teams.py
📚 Learning: 2026-03-15T16:55:07.730Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T16:55:07.730Z
Learning: Applies to **/*.py : Use PEP 758 except syntax: use `except A, B:` (no parentheses) — ruff enforces this on Python 3.14.

Applied to files:

  • src/synthorg/api/controllers/teams.py
📚 Learning: 2026-03-20T21:44:04.528Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-20T21:44:04.528Z
Learning: Applies to **/*.py : Use `except A, B:` syntax (without parentheses) per PEP 758 for exception handling in Python 3.14

Applied to files:

  • src/synthorg/api/controllers/teams.py
📚 Learning: 2026-03-17T22:08:13.456Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T22:08:13.456Z
Learning: Applies to src/synthorg/**/*.py : Use Pydantic v2 conventions: `BaseModel`, `model_validator`, `computed_field`, `ConfigDict`. For derived values use `computed_field` instead of storing + validating redundant fields. Use `NotBlankStr` (from `core.types`) for all identifier/name fields — including optional (`NotBlankStr | None`) and tuple (`tuple[NotBlankStr, ...]`) variants — instead of manual whitespace validators.

Applied to files:

  • src/synthorg/api/controllers/teams.py

- test_teams: _seed_departments now asserts PUT succeeded
- test_teams: add test_reorder_zero_teams for empty team list
- test_rebalance: assert new_total == 110 in zero_existing_budgets_over_max
- DepartmentEditDrawer.stories: onCreateTeam defaults members to [lead]
- DepartmentsTab.stories: onCreateTeam/onUpdateTeam default members to [lead]
- TeamListSection: log errors in handleDragEnd and handleDeleteConfirm catch blocks
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
web/src/pages/org-edit/DepartmentsTab.stories.tsx (1)

61-65: ⚠️ Potential issue | 🟡 Minor

Preserve existing team fields in mock update handler.

At Lines 61-65, onUpdateTeam replaces omitted fields with placeholders ('Unassigned' / synthetic members). In rename-only flows this can produce unrealistic and unstable story state. Prefer preserving the existing team’s lead/members when those fields are not provided.

💡 Suggested fix
+const getMockTeam = (deptName: string, teamName: string) =>
+  mockConfig.departments
+    .find((d) => d.name === deptName)
+    ?.teams?.find((t) => t.name === teamName)

   args: {
@@
-    onUpdateTeam: async (_d, _t, data) => ({
-      name: data.name ?? _t,
-      lead: data.lead ?? 'Unassigned',
-      members: data.members ?? [data.lead ?? 'Unassigned'],
-    }),
+    onUpdateTeam: async (deptName, teamName, data) => {
+      const existing = getMockTeam(deptName, teamName)
+      const lead = data.lead ?? existing?.lead ?? 'Unassigned'
+      const members = data.members ?? existing?.members ?? [lead]
+      return {
+        name: data.name ?? existing?.name ?? teamName,
+        lead,
+        members: members.includes(lead) ? members : [lead, ...members],
+      }
+    },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/org-edit/DepartmentsTab.stories.tsx` around lines 61 - 65, The
mock onUpdateTeam handler currently replaces missing fields with placeholders,
causing unrealistic state; update the handler so that when data.lead or
data.members are undefined it preserves the existing team’s values from the
incoming _d (use _d.lead and _d.members) and only fall back to 'Unassigned' as a
last resort, while keeping the existing name logic (data.name ?? _t). Refer to
the onUpdateTeam function signature (_d, _t, data) and replace the lead and
members fallbacks to use _d.lead and _d.members respectively (with 'Unassigned'
only if both data and _d lack values).
web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx (1)

51-55: ⚠️ Potential issue | 🟡 Minor

onUpdateTeam fallback can still produce invalid team state.

On Line 53-Line 54, defaulting to lead: 'Unassigned' and members: [] can emit impossible teams during story interactions (empty members, lead not in members). Preserve the current team values when fields are omitted.

Suggested fix
-    onUpdateTeam: async (_d, _t, data) => ({
-      name: data.name ?? _t,
-      lead: data.lead ?? 'Unassigned',
-      members: data.members ?? [],
-    }),
+    onUpdateTeam: async (_d, _t, data) => {
+      const current = mockDept.teams.find((team) => team.name === _t)
+      const lead = data.lead ?? current?.lead ?? 'Unassigned'
+      const members = data.members ?? current?.members ?? [lead]
+      return {
+        name: data.name ?? current?.name ?? _t,
+        lead,
+        members: members.includes(lead) ? members : [lead, ...members],
+      }
+    },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx` around lines 51 -
55, The current onUpdateTeam handler uses fallbacks that can create invalid
teams (e.g., setting lead to 'Unassigned' or empty members) — change the
fallback to preserve the existing team values: in the onUpdateTeam
implementation (function onUpdateTeam, params _d, _t, data) return name:
data.name ?? _t, lead: data.lead ?? _d.lead, and members: data.members ??
_d.members so omitted fields keep the original team values instead of unsafe
defaults.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/pages/org-edit/TeamListSection.tsx`:
- Around line 22-28: Move the logger instantiation out of the middle of the
import block: keep all imports (createLogger, StatPill, useToastStore,
TeamEditDialog, TeamDeleteConfirmDialog) together at the top, then declare const
log = createLogger('TeamListSection') below the imports; update the file to
reference the existing createLogger symbol and the log constant so no import is
interrupted by executable code.
- Line 63: In TeamListSection (the JSX in TeamListSection.tsx) replace the
non-semantic Tailwind utility classes `bg-bg-card` and `hover:bg-bg-card-hover`
with the semantic variants `bg-card` and `hover:bg-card-hover` where they appear
in the element className strings (e.g., the container with className="flex
items-center gap-2 rounded-md border border-border bg-bg-card p-3" and the other
occurrences around the list items); ensure all three occurrences are updated so
styling follows the project convention.

---

Duplicate comments:
In `@web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx`:
- Around line 51-55: The current onUpdateTeam handler uses fallbacks that can
create invalid teams (e.g., setting lead to 'Unassigned' or empty members) —
change the fallback to preserve the existing team values: in the onUpdateTeam
implementation (function onUpdateTeam, params _d, _t, data) return name:
data.name ?? _t, lead: data.lead ?? _d.lead, and members: data.members ??
_d.members so omitted fields keep the original team values instead of unsafe
defaults.

In `@web/src/pages/org-edit/DepartmentsTab.stories.tsx`:
- Around line 61-65: The mock onUpdateTeam handler currently replaces missing
fields with placeholders, causing unrealistic state; update the handler so that
when data.lead or data.members are undefined it preserves the existing team’s
values from the incoming _d (use _d.lead and _d.members) and only fall back to
'Unassigned' as a last resort, while keeping the existing name logic (data.name
?? _t). Refer to the onUpdateTeam function signature (_d, _t, data) and replace
the lead and members fallbacks to use _d.lead and _d.members respectively (with
'Unassigned' only if both data and _d lack values).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b5b2e9eb-1849-415e-9d3a-995b80da0b1b

📥 Commits

Reviewing files that changed from the base of the PR and between 73b0fe0 and ecb0b8c.

📒 Files selected for processing (5)
  • tests/unit/api/controllers/test_teams.py
  • tests/unit/budget/test_rebalance.py
  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Dashboard Test
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Build Web
  • GitHub Check: Analyze (python)
  • GitHub Check: Dependency Review
🧰 Additional context used
📓 Path-based instructions (5)
web/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{ts,tsx,js,jsx}: Always use createLogger from @/lib/logger -- never bare console.warn/console.error/console.debug in application code
Logger variable name must always be const log (e.g. const log = createLogger('module-name'))
Pass dynamic/untrusted values as separate arguments to logger methods (not interpolated into the message string) so they go through sanitizeArg
Attacker-controlled fields inside structured objects must be wrapped in sanitizeForLog() before embedding in log calls

Files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-*)) for colors; NEVER hardcode hex values in .tsx/.ts files
Use font-sans or font-mono (Geist tokens) for typography; NEVER set fontFamily directly in .tsx/.ts files
Use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing; NEVER hardcode pixel values for layout spacing in components
Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders; NEVER hardcode values in .tsx/.ts files
Use @/lib/motion presets for Framer Motion transition durations; NEVER hardcode transition durations
CSS side-effect imports in TypeScript 6 require type declarations -- add /// <reference types="vite/client" /> at the top of files with CSS imports

web/src/**/*.{ts,tsx}: ALWAYS reuse existing components from web/src/components/ui/ in React code before creating new ones.
NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions in React code -- use design tokens from @/lib/design-tokens.ts and presets from @/lib/motion.
A PostToolUse hook (scripts/check_web_design_system.py) enforces design system rules on every Edit/Write to web/src/ files. Follow the design token and motion preset rules.
Use TypeScript 6.0+ with strict type checking for React code in the web dashboard.
Run eslint with zero warnings for the web dashboard on all TypeScript/React files. eslint-web pre-commit hook enforces this.
Use React 19 and Framer Motion for the web dashboard. See web/CLAUDE.md for component inventory and design system reference.

Files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
web/src/**/*.stories.tsx

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.stories.tsx: For Storybook stories with tags: ['autodocs'], ensure @storybook/addon-docs is installed and added to addons
Use storybook/test and storybook/actions import paths in Storybook stories (not @storybook/test or @storybook/addon-actions)

Files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: In Python code, do NOT use from __future__ import annotations -- Python 3.14 has PEP 649 native lazy annotations.
In Python 3.14 code, use PEP 758 except syntax: use except A, B: (no parentheses) -- ruff enforces this.
All public functions and methods in Python must have type hints, with mypy strict mode enforced.
All public classes and functions in Python must have Google-style docstrings, enforced by ruff D rules.
Line length for Python code is 88 characters (enforced by ruff formatter).

Files:

  • tests/unit/budget/test_rebalance.py
  • tests/unit/api/controllers/test_teams.py
tests/**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

tests/**/*.py: Mark Python tests with markers: @pytest.mark.unit, @pytest.mark.integration, @pytest.mark.e2e, @pytest.mark.slow.
Prefer @pytest.mark.parametrize for testing similar cases in Python.
Use generic test names in Python tests: test-provider, test-small-001 instead of real vendor names (Anthropic, OpenAI, Claude, GPT).
For timing-sensitive Python tests, mock time.monotonic() and asyncio.sleep() to make them deterministic instead of widening timing margins. Use asyncio.Event().wait() for indefinite blocking.
Run pytest unit tests on affected Python modules before push. Foundational module changes (core, config, observability) or conftest changes trigger full runs.

Files:

  • tests/unit/budget/test_rebalance.py
  • tests/unit/api/controllers/test_teams.py

⚙️ CodeRabbit configuration file

Test files do not require Google-style docstrings on classes or functions -- ruff D rules are only enforced on src/. A bare @settings() decorator with no arguments on Hypothesis property tests is a no-op and should not be suggested -- the HYPOTHESIS_PROFILE env var controls example counts via registered profiles, which @given() honors automatically.

Files:

  • tests/unit/budget/test_rebalance.py
  • tests/unit/api/controllers/test_teams.py
🧠 Learnings (23)
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: import from `storybook/test` (not `storybook/test`), `storybook/actions` (not `storybook/addon-actions`)

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: Use storybook/test (not storybook/test) and storybook/actions (not storybook/addon-actions) import paths

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: Import from `storybook/test` instead of `storybook/test`

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/**/*.stories.tsx : Use `storybook/test` and `storybook/actions` import paths in Storybook stories (not `storybook/test` or `storybook/addon-actions`)

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/components/ui/**/*.stories.tsx : Create a `.stories.tsx` file alongside each new shared component with all states (default, hover, loading, error, empty)

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: Use `parameters.a11y.test: 'error' | 'todo' | 'off'` for a11y testing configuration (replaces old `.element` and `.manual`)

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Create new shared components in `web/src/components/ui/` with `.stories.tsx` Storybook file covering all states (default, hover, loading, error, empty)

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: Use parameters.backgrounds.options (object keyed by name) + initialGlobals.backgrounds.value for background options (replaces old default + values array)

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/**/*.stories.{ts,tsx} : Storybook 10: Use parameters.a11y.test: 'error' | 'todo' | 'off' for a11y testing (replaces old .element and .manual); set globally in preview.tsx to enforce WCAG compliance on all stories

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/DepartmentsTab.stories.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/**/*.stories.tsx : For Storybook stories with `tags: ['autodocs']`, ensure `storybook/addon-docs` is installed and added to addons

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/.storybook/**/*.{ts,js} : Use `defineMain` from `storybook/react-vite/node` and `definePreview` from `storybook/react-vite` for type-safe Storybook configuration

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from web/src/components/ui/ (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast/ToastContainer, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem, Drawer, form fields, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle, LiveRegion, MobileUnsupportedOverlay, LazyCodeMirrorEditor) before creating new components

Applied to files:

  • web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx
  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT build card-with-header layouts from scratch; use `<SectionCard>`

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-27T22:32:26.927Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:32:26.927Z
Learning: Applies to web/src/components/ui/*.{tsx,ts} : For new shared React components: place in web/src/components/ui/ with kebab-case filename, create .stories.tsx with all states, export props as TypeScript interface, use design tokens exclusively

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-27T12:44:29.466Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{tsx,ts} : ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle, LiveRegion, MobileUnsupportedOverlay, LazyCodeMirrorEditor, TagInput, MetadataGrid, ProjectStatusBadge, ContentTypeBadge)

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/components/ui/**/*.tsx : Export props as a TypeScript interface for new components

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : When creating new shared web components, place in web/src/components/ui/ with kebab-case filename, create .stories.tsx alongside with all states (default, hover, loading, error, empty), export props as TypeScript interface, use design tokens exclusively with no hardcoded colors/fonts/spacing, and import cn from `@/lib/utils` for conditional class merging

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/components/ui/**/*.{ts,tsx} : Export component props as a TypeScript interface for all new components

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/**/*.{ts,tsx,js,jsx} : Always use `createLogger` from `@/lib/logger` -- never bare `console.warn`/`console.error`/`console.debug` in application code

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-19T07:13:44.964Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T07:13:44.964Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget package (budget/): cost tracking, budget enforcement (pre-flight/in-flight checks, auto-downgrade), billing periods, cost tiers, quota/subscription tracking, CFO cost optimization (anomaly detection, efficiency analysis, downgrade recommendations, approval decisions), spending reports, budget errors (BudgetExhaustedError, DailyLimitExceededError, QuotaExhaustedError)

Applied to files:

  • tests/unit/budget/test_rebalance.py
📚 Learning: 2026-03-17T06:30:14.180Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-17T06:30:14.180Z
Learning: Applies to src/synthorg/budget/**/*.py : Budget tracking includes pre-flight/in-flight checks, auto-downgrade, billing periods, cost tiers, quota/subscription. CFO includes anomaly detection, efficiency analysis, downgrade recommendations.

Applied to files:

  • tests/unit/budget/test_rebalance.py
📚 Learning: 2026-04-06T16:36:13.074Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T16:36:13.074Z
Learning: Applies to tests/**/*.py : Run pytest unit tests on affected Python modules before push. Foundational module changes (core, config, observability) or conftest changes trigger full runs.

Applied to files:

  • tests/unit/api/controllers/test_teams.py
🔇 Additional comments (16)
web/src/pages/org-edit/DepartmentsTab.stories.tsx (1)

31-31: Good fixture alignment with team schema.

Adding lead to the team fixture at Line 31 keeps the story data consistent with the team model used by DepartmentsTab.

web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx (1)

46-50: Good defaulting in onCreateTeam mock.

This now returns a structurally valid TeamConfig and avoids empty-member teams when members is omitted.

web/src/pages/org-edit/TeamListSection.tsx (3)

123-142: LGTM! Error handling properly addresses previous review feedback.

The handleDragEnd callback correctly logs failures via log.error with context (active, over, reordered) and the caught error, then shows a user-facing toast. This aligns with the coding guidelines for logger usage.


144-158: LGTM! Delete handler properly logs errors and manages loading state.

The handleDeleteConfirm correctly logs failures with context (teamName, reassignTo) and the caught error, uses finally for guaranteed state cleanup, and shows appropriate user feedback via toast.


189-211: LGTM! DnD implementation follows @dnd-kit best practices.

The DndContext and SortableContext are properly configured with sensors, collision detection, and the vertical list sorting strategy. Team names serve as stable identifiers for both sortable IDs and React keys.

tests/unit/api/controllers/test_teams.py (5)

14-24: LGTM! Seed helper properly validates response.

The _seed_departments helper now captures the response and asserts success (status_code < 400), addressing the previous review feedback. This will surface seeding failures early.


54-151: LGTM! Comprehensive create team test coverage.

Tests cover success path, not-found department, duplicate name (including case-insensitive), duplicate members, blank name validation, default empty members, and authorization. This aligns well with the controller implementation.


156-254: LGTM! Update team tests cover partial update semantics.

Tests verify rename, lead change, member replacement, not-found cases, rename conflicts, and the important edge case where renaming to the same name succeeds. This matches the PATCH endpoint's partial update behavior.


259-374: LGTM! Delete team tests thoroughly verify reassignment logic.

Tests cover success, not-found, self-reassignment rejection (per PR objectives), member merge on reassignment, target not-found, and member deduplication. The verification pattern (attempting PATCH on deleted team) is effective.


437-447: LGTM! Zero teams edge case added as requested.

The test_reorder_zero_teams test verifies that reordering with an empty team_names list succeeds when the department has no teams, addressing the previous review feedback.

tests/unit/budget/test_rebalance.py (6)

1-14: LGTM! Clean test module setup.

Imports are minimal and focused. The _dept helper provides a concise way to construct test department dictionaries with the required structure.


19-57: LGTM! NONE mode tests verify passthrough behavior.

Tests confirm that NONE mode concatenates without adjustment, handles empty inputs correctly, and that scale_factor is explicitly None in this mode.


135-150: LGTM! Zero-budget over-max test now includes new_total assertion.

The test now asserts result.new_total == 110, documenting that when new departments alone exceed max_budget, the total reflects those new departments even though existing budgets are zeroed.


212-265: LGTM! REJECT_IF_OVER tests verify rejection semantics.

Tests confirm rejection when over budget, acceptance when under or exactly at boundary, that scale_factor is None for this mode, and that department budgets remain unchanged regardless of rejection status.


270-308: LGTM! Parametrized edge cases provide efficient coverage.

The parametrized test efficiently covers empty input combinations across all modes and zero-budget new department scenarios, aligning with best practices for testing similar cases.


349-359: LGTM! NamedTuple contract test ensures stability.

The test verifies RebalanceResult is a proper NamedTuple with correct tuple unpacking, documenting the expected field order and ensuring the API contract remains stable.

…S classes

Dependencies:
- Python: click, huggingface-hub, pydantic-extra-types, python-multipart,
  regex, sqlalchemy, transformers, tzdata, uv, uvicorn +5 more
- Web: vite 8.0.4->8.0.5 (fixes high-severity audit) + all minor updates
- Site: all dependencies updated
- Go CLI: sigstore, otel, grpc, compress, go-colorful +4 more

Code fixes:
- TeamListSection: move logger instantiation below all imports
- TeamListSection: bg-bg-card -> bg-card, bg-bg-card-hover -> bg-card-hover
@socket-security
Copy link
Copy Markdown

Warning

Review the following alerts detected in dependencies.

According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.

Action Severity Alert  (click "▶" to expand/collapse)
Warn High
Obfuscated code: npm @storybook/builder-vite is 72.0% likely obfuscated

Confidence: 0.72

Location: Package overview

From: web/package-lock.jsonnpm/@storybook/react-vite@10.3.4npm/@storybook/builder-vite@10.3.4

ℹ Read more on: This package | This alert | What is obfuscated code?

Next steps: Take a moment to review the security alert above. Review the linked package source code to understand the potential risk. Ensure the package is not malicious before proceeding. If you're unsure how to proceed, reach out to your security team or ask the Socket team for help at support@socket.dev.

Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.

Mark the package as acceptable risk. To ignore this alert only in this pull request, reply with the comment @SocketSecurity ignore npm/@storybook/builder-vite@10.3.4. You can also ignore all packages with @SocketSecurity ignore-all. To ignore an alert for all future pull requests, use Socket's Dashboard to change the triage state of this alert.

View full report

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/pages/org-edit/TeamListSection.tsx`:
- Line 136: The structured log call using log.error in TeamListSection (the line
with log.error('Failed to reorder teams', { active: active.id, over: over.id,
reordered }, err)) embeds untrusted fields directly; wrap attacker-controlled
values (active.id, over.id, and any fields inside reordered) with
sanitizeForLog() before constructing the object so the payload is safe, e.g.
replace active.id, over.id, and each property inside reordered with
sanitizeForLog(...) when building the second argument to log.error.
- Around line 83-100: Replace the raw <button> elements used for the edit and
delete actions in TeamListSection (the buttons with onEdit/onDelete handlers
rendering Pencil and Trash2 icons) with the shared Button UI primitive (e.g.,
Button) so styling, accessibility and disabled behavior are consistent; keep the
same props: type="button", onClick={onEdit/onDelete}, aria-label={`Edit/Delete
${team.name}`}, disabled={disabled}, and apply the existing classNames for
visual states (hover/bg/text) or map them into Button props
(variant/size/intent) as appropriate to preserve the current appearance and
icon-only layout.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 788dda46-9488-46fc-a3f8-615889ac07eb

📥 Commits

Reviewing files that changed from the base of the PR and between ecb0b8c and ffa0d37.

⛔ Files ignored due to path filters (4)
  • cli/go.sum is excluded by !**/*.sum
  • site/package-lock.json is excluded by !**/package-lock.json
  • uv.lock is excluded by !**/*.lock
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (3)
  • cli/go.mod
  • web/public/mockServiceWorker.js
  • web/src/pages/org-edit/TeamListSection.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
  • GitHub Check: CLI Test (windows-latest)
  • GitHub Check: Dashboard Storybook Build
  • GitHub Check: Test (Python 3.14)
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (2)
web/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{ts,tsx,js,jsx}: Always use createLogger from @/lib/logger -- never bare console.warn/console.error/console.debug in application code
Logger variable name must always be const log (e.g. const log = createLogger('module-name'))
Pass dynamic/untrusted values as separate arguments to logger methods (not interpolated into the message string) so they go through sanitizeArg
Attacker-controlled fields inside structured objects must be wrapped in sanitizeForLog() before embedding in log calls

Files:

  • web/src/pages/org-edit/TeamListSection.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (web/CLAUDE.md)

web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground, bg-card, text-accent, text-success, bg-danger, etc.) or CSS variables (var(--so-*)) for colors; NEVER hardcode hex values in .tsx/.ts files
Use font-sans or font-mono (Geist tokens) for typography; NEVER set fontFamily directly in .tsx/.ts files
Use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing; NEVER hardcode pixel values for layout spacing in components
Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders; NEVER hardcode values in .tsx/.ts files
Use @/lib/motion presets for Framer Motion transition durations; NEVER hardcode transition durations
CSS side-effect imports in TypeScript 6 require type declarations -- add /// <reference types="vite/client" /> at the top of files with CSS imports

web/src/**/*.{ts,tsx}: ALWAYS reuse existing components from web/src/components/ui/ in React code before creating new ones.
NEVER hardcode hex colors, font-family, pixel spacing, or Framer Motion transitions in React code -- use design tokens from @/lib/design-tokens.ts and presets from @/lib/motion.
A PostToolUse hook (scripts/check_web_design_system.py) enforces design system rules on every Edit/Write to web/src/ files. Follow the design token and motion preset rules.
Use TypeScript 6.0+ with strict type checking for React code in the web dashboard.
Run eslint with zero warnings for the web dashboard on all TypeScript/React files. eslint-web pre-commit hook enforces this.
Use React 19 and Framer Motion for the web dashboard. See web/CLAUDE.md for component inventory and design system reference.

Files:

  • web/src/pages/org-edit/TeamListSection.tsx
🧠 Learnings (34)
📚 Learning: 2026-03-31T14:17:24.182Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:17:24.182Z
Learning: Applies to cli/go.mod : CLI Go 1.26+, dependencies in `cli/go.mod`

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-21T14:12:17.848Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T14:12:17.848Z
Learning: Applies to cli/go.mod : CLI uses Go 1.26+. Dependencies: Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/go.mod : Go CLI dependencies: Go 1.26+, Cobra (commands), charmbracelet/huh (interactive CLI), charmbracelet/lipgloss (styled output).

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Go 1.26+ required; CLI dependencies in `cli/go.mod` (Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf)

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-15T18:17:43.675Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T18:17:43.675Z
Learning: Applies to cli/** : CLI: Go 1.26+, dependencies in cli/go.mod (Cobra, charmbracelet/huh).

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: Applies to go.mod : Maintain Go 1.26+ requirement. Dependencies: Cobra (CLI framework), charmbracelet/huh and charmbracelet/lipgloss (UI), sigstore-go (code signing), go-containerregistry (container image verification), go-tuf (TUF client for Sigstore).

Applied to files:

  • cli/go.mod
📚 Learning: 2026-04-06T16:36:13.074Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T16:36:13.074Z
Learning: Applies to cli/**/*.go : Use Go 1.26+ for the CLI binary. See `cli/CLAUDE.md` for commands and reference. Key rule: use `go -C cli` (never `cd cli`).

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to .github/workflows/cli.yml : CLI workflow: Go lint (golangci-lint + go vet) + test (-race -coverprofile) + build (cross-compile: linux/darwin/windows × amd64/arm64) + govulncheck + fuzz testing (main-only, 30s/target, continue-on-error, matrix over 4 packages). cli-pass gate includes fuzz as informational. GoReleaser release on v* tags. Cosign keyless signing of checksums.txt. SLSA L3 provenance attestations. Sigstore bundle (.sigstore.json) attached. Post-release appends checksums/verification/provenance to draft release notes.

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-15T21:32:02.880Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-15T21:32:02.880Z
Learning: Applies to cli/**/*.go : Go CLI (Go 1.26+) uses Cobra for commands, charmbracelet/huh for interactive CLI, charmbracelet/lipgloss for styled output. Cross-platform builds (linux/darwin/windows × amd64/arm64). GoReleaser for releases with cosign keyless signing of checksums.txt. SLSA L3 provenance attestations via actions/attest-build-provenance.

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: CLI (Go binary): see `cli/CLAUDE.md` for commands, flags, and reference; use `go -C cli` (never `cd cli`)

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-21T12:54:22.557Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-21T12:54:22.557Z
Learning: Applies to cli/**/*.go : Use Cobra for CLI command structure, charmbracelet/huh for interactive prompts, and charmbracelet/lipgloss for terminal styling

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-30T16:36:33.512Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: cli/CLAUDE.md:0-0
Timestamp: 2026-03-30T16:36:33.512Z
Learning: Applies to cli/internal/config/**/*.go : Settable config keys are: `auto_apply_compose`, `auto_cleanup`, `auto_pull`, `auto_restart`, `auto_start_after_wipe`, `auto_update_cli`, `backend_port`, `channel`, `color`, `docker_sock`, `hints`, `image_tag`, `log_level`, `output`, `sandbox`, `timestamps`, `web_port` (17 total)

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-30T16:36:33.512Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: cli/CLAUDE.md:0-0
Timestamp: 2026-03-30T16:36:33.512Z
Learning: Applies to cli/cmd/**/*.go : CLI exit codes must follow the standard: 0 = Success, 1 = Runtime error, 2 = Usage error (bad arguments), 3 = Unhealthy (backend/containers), 4 = Unreachable (Docker not available), 10 = Updates available (`--check`)

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-16T19:52:03.656Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-16T19:52:03.656Z
Learning: Applies to cli/**/*.go : Lint CLI Go code with golangci-lint and go vet; test with go test -race; check vulnerabilities with govulncheck

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-30T16:36:33.512Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: cli/CLAUDE.md:0-0
Timestamp: 2026-03-30T16:36:33.512Z
Learning: Applies to cli/internal/**/*.go : Place internal packages in `internal/` directory, including version, config, docker, compose, health, diagnostics, images, selfupdate, completion, ui, and verify functionality

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-19T11:19:40.044Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-19T11:19:40.044Z
Learning: CLI workflow (`.github/workflows/cli.yml`) runs Go lint (golangci-lint + go vet) + test (race, coverage) + build (cross-compile matrix) + vulnerability check (govulncheck) + fuzz testing. Cross-compiles for linux/darwin/windows × amd64/arm64. GoReleaser release on v* tags with cosign keyless signing and SLSA L3 attestations.

Applied to files:

  • cli/go.mod
📚 Learning: 2026-03-27T22:32:26.927Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:32:26.927Z
Learning: Applies to web/src/components/ui/*.{tsx,ts} : For new shared React components: place in web/src/components/ui/ with kebab-case filename, create .stories.tsx with all states, export props as TypeScript interface, use design tokens exclusively

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from web/src/components/ui/ (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast/ToastContainer, Skeleton variants, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem, Drawer, form fields, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle, LiveRegion, MobileUnsupportedOverlay, LazyCodeMirrorEditor) before creating new components

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-30T10:41:40.176Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:41:40.176Z
Learning: Applies to web/src/**/*.{ts,tsx} : Do NOT build card-with-header layouts from scratch; use `<SectionCard>`

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-27T12:44:29.466Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T12:44:29.466Z
Learning: Applies to web/src/**/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup/StaggerItem) before creating new ones

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-31T14:31:11.894Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-31T14:31:11.894Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use React 19, TypeScript 6.0+, and design system tokens from shadcn/ui + Tailwind CSS 4 + Radix UI in web dashboard

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-06T16:36:13.074Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-04-06T16:36:13.074Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use React 19 and Framer Motion for the web dashboard. See `web/CLAUDE.md` for component inventory and design system reference.

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{tsx,ts} : ALWAYS reuse existing components from `web/src/components/ui/` before creating new ones (StatusBadge, MetricCard, Sparkline, SectionCard, AgentCard, DeptHealthBar, ProgressGauge, StatPill, Avatar, Button, Toast, Skeleton, EmptyState, ErrorBoundary, ConfirmDialog, CommandPalette, InlineEdit, AnimatedPresence, StaggerGroup, Drawer, InputField, SelectField, SliderField, ToggleField, TaskStatusIndicator, PriorityBadge, ProviderHealthBadge, TokenUsageBar, CodeMirrorEditor, SegmentedControl, ThemeToggle, LiveRegion, MobileUnsupportedOverlay, LazyCodeMirrorEditor, TagInput, MetadataGrid, ProjectStatusBadge, ContentTypeBadge)

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/**/*.{ts,tsx,js,jsx} : Always use `createLogger` from `@/lib/logger` -- never bare `console.warn`/`console.error`/`console.debug` in application code

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/**/*.{ts,tsx,js,jsx} : Logger variable name must always be `const log` (e.g. `const log = createLogger('module-name')`)

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{tsx,ts} : Use Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, `text-success`, `bg-danger`) or CSS variables (`var(--so-*)`) for colors. NEVER hardcode hex values or rgba() in `.tsx`/`.ts` files

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-27T22:32:26.927Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:32:26.927Z
Learning: Applies to web/src/**/*.{tsx,ts} : Use semantic Tailwind classes (text-foreground, bg-card, text-accent, text-success, bg-danger) or CSS variables (var(--so-*)); never hardcode hex values in .tsx/.ts files

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, `text-success`, `bg-danger`, etc.) or CSS variables (`var(--so-*)`) for colors; NEVER hardcode hex values in `.tsx`/`.ts` files

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use token variables (`var(--so-shadow-card-hover)`, `border-border`, `border-bright`) for shadows and borders; NEVER hardcode values in `.tsx`/`.ts` files

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-30T10:20:08.544Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-30T10:20:08.544Z
Learning: Applies to web/src/**/*.{ts,tsx} : Web dashboard shadows/borders: use token variables (var(--so-shadow-card-hover), border-border, border-bright); never hardcode shadow or border values

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-03-27T22:32:26.927Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-27T22:32:26.927Z
Learning: Applies to web/src/**/*.{tsx,ts} : Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows/borders; never hardcode values

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{tsx,ts} : Use token variables (`var(--so-shadow-card-hover)`, `border-border`, `border-bright`) for shadows and borders. NEVER hardcode shadow or border values

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-02T12:21:16.739Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-02T12:21:16.739Z
Learning: Applies to web/src/**/*.{tsx,ts} : Use density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind spacing. NEVER hardcode pixel values for layout spacing

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
📚 Learning: 2026-04-06T06:45:22.965Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: web/CLAUDE.md:0-0
Timestamp: 2026-04-06T06:45:22.965Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind spacing; NEVER hardcode pixel values for layout spacing in components

Applied to files:

  • web/src/pages/org-edit/TeamListSection.tsx
🔇 Additional comments (2)
cli/go.mod (1)

8-8: Dependency bump set looks consistent and safe to merge.

These updates are internally coherent (including the gRPC/genproto alignment and synchronized OTel module versions), and I don’t see any correctness or packaging risks in the edited go.mod segments.

Based on learnings: maintain Go 1.26+ and the CLI dependency set in cli/go.mod (Cobra, charmbracelet/huh, charmbracelet/lipgloss, sigstore-go, go-containerregistry, go-tuf).

Also applies to: 86-86, 100-100, 157-157, 167-167, 178-178, 228-228, 267-269, 282-284

web/public/mockServiceWorker.js (1)

10-11: No action required—the file appears to be properly updated.

The version numbers are correctly aligned: PACKAGE_VERSION is '2.13.0' and the installed MSW version (from package-lock.json) is also 2.13.0. The INTEGRITY_CHECKSUM value is dynamically computed during the msw init process rather than being pre-defined, so it is expected to reflect the content of the generated file. There is no integrity issue with the unchanged checksum value across version updates.

			> Likely an incorrect or invalid review comment.

Comment on lines +83 to +100
<button
type="button"
onClick={onEdit}
className="rounded p-1 text-text-muted hover:bg-card-hover hover:text-text-secondary"
aria-label={`Edit ${team.name}`}
disabled={disabled}
>
<Pencil className="size-3.5" />
</button>
<button
type="button"
onClick={onDelete}
className="rounded p-1 text-text-muted hover:bg-card-hover hover:text-danger"
aria-label={`Delete ${team.name}`}
disabled={disabled}
>
<Trash2 className="size-3.5" />
</button>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Use shared Button component for edit/delete icon actions instead of raw <button>.

These action controls should reuse the existing UI button primitive for consistency and shared behavior.

♻️ Proposed refactor
-      <button
-        type="button"
-        onClick={onEdit}
-        className="rounded p-1 text-text-muted hover:bg-card-hover hover:text-text-secondary"
-        aria-label={`Edit ${team.name}`}
-        disabled={disabled}
-      >
+      <Button
+        type="button"
+        variant="ghost"
+        size="icon"
+        onClick={onEdit}
+        className="size-7 text-text-muted hover:bg-card-hover hover:text-text-secondary"
+        aria-label={`Edit ${team.name}`}
+        disabled={disabled}
+      >
         <Pencil className="size-3.5" />
-      </button>
-      <button
-        type="button"
-        onClick={onDelete}
-        className="rounded p-1 text-text-muted hover:bg-card-hover hover:text-danger"
-        aria-label={`Delete ${team.name}`}
-        disabled={disabled}
-      >
+      </Button>
+      <Button
+        type="button"
+        variant="ghost"
+        size="icon"
+        onClick={onDelete}
+        className="size-7 text-text-muted hover:bg-card-hover hover:text-danger"
+        aria-label={`Delete ${team.name}`}
+        disabled={disabled}
+      >
         <Trash2 className="size-3.5" />
-      </button>
+      </Button>

As per coding guidelines: "ALWAYS reuse existing components from web/src/components/ui/ in React code before creating new ones."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/org-edit/TeamListSection.tsx` around lines 83 - 100, Replace
the raw <button> elements used for the edit and delete actions in
TeamListSection (the buttons with onEdit/onDelete handlers rendering Pencil and
Trash2 icons) with the shared Button UI primitive (e.g., Button) so styling,
accessibility and disabled behavior are consistent; keep the same props:
type="button", onClick={onEdit/onDelete}, aria-label={`Edit/Delete
${team.name}`}, disabled={disabled}, and apply the existing classNames for
visual states (hover/bg/text) or map them into Button props
(variant/size/intent) as appropriate to preserve the current appearance and
icon-only layout.

try {
await onReorderTeams(reordered)
} catch (err) {
log.error('Failed to reorder teams', { active: active.id, over: over.id, reordered }, err)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Sanitize untrusted fields before embedding in structured log payloads.

The logger calls include dynamic values inside objects without sanitizeForLog(). Wrap those fields before logging to avoid unsafe log content.

♻️ Proposed fix
-import { createLogger } from '@/lib/logger'
+import { createLogger, sanitizeForLog } from '@/lib/logger'
...
-      log.error('Failed to reorder teams', { active: active.id, over: over.id, reordered }, err)
+      log.error(
+        'Failed to reorder teams',
+        {
+          active: sanitizeForLog(active.id),
+          over: sanitizeForLog(over.id),
+          reordered: sanitizeForLog(reordered),
+        },
+        err,
+      )
...
-      log.error('Failed to delete team', { teamName, reassignTo }, err)
+      log.error(
+        'Failed to delete team',
+        {
+          teamName: sanitizeForLog(teamName),
+          reassignTo: sanitizeForLog(reassignTo),
+        },
+        err,
+      )

As per coding guidelines: "Attacker-controlled fields inside structured objects must be wrapped in sanitizeForLog() before embedding in log calls".

Also applies to: 150-150

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/org-edit/TeamListSection.tsx` at line 136, The structured log
call using log.error in TeamListSection (the line with log.error('Failed to
reorder teams', { active: active.id, over: over.id, reordered }, err)) embeds
untrusted fields directly; wrap attacker-controlled values (active.id, over.id,
and any fields inside reordered) with sanitizeForLog() before constructing the
object so the payload is safe, e.g. replace active.id, over.id, and each
property inside reordered with sanitizeForLog(...) when building the second
argument to log.error.

@Aureliolo Aureliolo merged commit 35977c0 into main Apr 6, 2026
48 of 49 checks passed
@Aureliolo Aureliolo deleted the feat/team-editing-budget-rebalance branch April 6, 2026 19:05
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview April 6, 2026 19:05 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Apr 6, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.6.3](v0.6.2...v0.6.3)
(2026-04-06)


### Features

* backend CRUD + multi-user permissions
([#1081](#1081),
[#1082](#1082))
([#1094](#1094))
([93e469b](93e469b))
* in-dashboard team editing + budget rebalance on pack apply
([#1093](#1093))
([35977c0](35977c0)),
closes [#1079](#1079)
[#1080](#1080)
* tiered rate limiting, NotificationSink protocol, in-dashboard
notifications
([#1092](#1092))
([df2142c](df2142c)),
closes [#1077](#1077)
[#1078](#1078)
[#849](#849)
* two-stage safety classifier and cross-provider uncertainty check for
approval gates
([#1090](#1090))
([0b2edee](0b2edee)),
closes [#847](#847)
[#701](#701)


### Refactoring

* memory pipeline improvements
([#1075](#1075),
[#997](#997))
([#1091](#1091))
([a048a4c](a048a4c))


### Documentation

* add OpenCode parity setup and hookify rule documentation
([#1095](#1095))
([52e877a](52e877a))


### Maintenance

* bump vite from 8.0.3 to 8.0.4 in /web in the all group across 1
directory ([#1088](#1088))
([1e86ca6](1e86ca6))
* tune ZAP DAST scan -- auth, timeouts, rules, report artifacts
([#1097](#1097))
([82bf0e1](82bf0e1)),
closes [#1096](#1096)

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants