feat: in-dashboard team editing + budget rebalance on pack apply#1093
feat: in-dashboard team editing + budget rebalance on pack apply#1093
Conversation
WalkthroughAdds full team CRUD and reorder APIs and UI plus budget rebalance support for template-pack application. Backend: new 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
Dependency ReviewThe following issues were found:
|
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
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.
| 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)) |
There was a problem hiding this comment.
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.
| 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 ?? '') |
Codecov Report❌ Patch coverage is
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. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
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}/teamsand 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.
| 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] | ||
|
|
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
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.
| 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) |
There was a problem hiding this comment.
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.
| 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) | ||
| } |
There was a problem hiding this comment.
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.
web/src/utils/budget.ts
Outdated
| 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], |
There was a problem hiding this comment.
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).
| 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, | ||
| ) |
There was a problem hiding this comment.
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.
| 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 | ||
|
|
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
ConflictErrorgets logged as generic failure instead of being passed through cleanly.The
ConflictErrorraised on budget rejection (line 260) is caught by the genericexcept Exceptionblock and logged withaction="apply_failed", which is misleading since this is an expected validation failure, not an unexpected error. Consider re-raisingConflictErrorexplicitly likeNotFoundError.🛠️ 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 | 🔵 TrivialExtract duplicated refresh logic into a shared helper.
Both
handleApplyDirectandhandleApplyWithModecontain identical try/catch blocks forfetchCompanyData()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
📒 Files selected for processing (35)
docs/design/page-structure.mdsrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/teams.pysrc/synthorg/api/controllers/template_packs.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/rebalance.pysrc/synthorg/core/company.pysrc/synthorg/observability/events/api.pysrc/synthorg/observability/events/company.pysrc/synthorg/observability/events/template.pytests/unit/api/controllers/test_teams.pytests/unit/api/controllers/test_template_packs_rebalance.pytests/unit/budget/test_rebalance.pyweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsxweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/api/endpoints/company.tsweb/src/api/types.tsweb/src/hooks/useOrgEditData.tsweb/src/pages/OrgChartPage.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/PackApplyPreviewDialog.tsxweb/src/pages/org-edit/PackSelectionDialog.tsxweb/src/pages/org-edit/TeamDeleteConfirmDialog.tsxweb/src/pages/org-edit/TeamEditDialog.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/src/pages/org/TeamGroupNode.tsxweb/src/pages/org/build-org-tree.tsweb/src/stores/company.tsweb/src/utils/budget.ts
| try: | ||
| return Team(**team_dict) | ||
| except (ValueError, TypeError) as exc: | ||
| msg = f"Team validation failed: {exc}" | ||
| raise ApiValidationError(msg) from exc |
There was a problem hiding this comment.
🧹 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 excAs 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.
| logger.info( | ||
| API_TEAM_DELETED, | ||
| department=dept_name, | ||
| team=team_name, | ||
| reassign_to=reassign_to, | ||
| ) |
There was a problem hiding this comment.
🧹 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.
| 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.
| 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 |
There was a problem hiding this comment.
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.
| 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() | ||
| }) |
There was a problem hiding this comment.
🧹 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.
| 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 () => {}, |
There was a problem hiding this comment.
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.
| 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]) |
There was a problem hiding this comment.
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:
- Extending
PackInfoResponseto includetotal_budget_percent - 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.
| const [reassignTo, setReassignTo] = useState('') | ||
|
|
||
| const memberCount = team?.members.length ?? 0 | ||
| const hasMembers = memberCount > 0 | ||
| const hasSiblings = siblingTeams.length > 0 |
There was a problem hiding this comment.
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.
| 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
affabdc to
af02b46
Compare
onUpdateTeam second param is teamName (string), not a team object. onDeleteTeam and onReorderTeams must return Promise<void>, not Promise<boolean>.
There was a problem hiding this comment.
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
📒 Files selected for processing (35)
docs/design/page-structure.mdsrc/synthorg/api/controllers/__init__.pysrc/synthorg/api/controllers/teams.pysrc/synthorg/api/controllers/template_packs.pysrc/synthorg/budget/__init__.pysrc/synthorg/budget/rebalance.pysrc/synthorg/core/company.pysrc/synthorg/observability/events/api.pysrc/synthorg/observability/events/company.pysrc/synthorg/observability/events/template.pytests/unit/api/controllers/test_teams.pytests/unit/api/controllers/test_template_packs_rebalance.pytests/unit/budget/test_rebalance.pyweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsxweb/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/api/endpoints/company.tsweb/src/api/types.tsweb/src/hooks/useOrgEditData.tsweb/src/pages/OrgChartPage.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentEditDrawer.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/pages/org-edit/PackApplyPreviewDialog.tsxweb/src/pages/org-edit/PackSelectionDialog.tsxweb/src/pages/org-edit/TeamDeleteConfirmDialog.tsxweb/src/pages/org-edit/TeamEditDialog.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/src/pages/org/TeamGroupNode.tsxweb/src/pages/org/build-org-tree.tsweb/src/stores/company.tsweb/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 usecreateLoggerfrom@/lib/logger-- never bareconsole.warn/console.error/console.debugin application code
Logger variable name must always beconst 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 throughsanitizeArg
Attacker-controlled fields inside structured objects must be wrapped insanitizeForLog()before embedding in log calls
Files:
web/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/pages/OrgChartPage.tsxweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/org/TeamGroupNode.tsxweb/src/hooks/useOrgEditData.tsweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/api/endpoints/company.tsweb/src/pages/org-edit/TeamDeleteConfirmDialog.tsxweb/src/pages/org-edit/PackApplyPreviewDialog.tsxweb/src/pages/org-edit/TeamEditDialog.tsxweb/src/utils/budget.tsweb/src/pages/org/build-org-tree.tsweb/src/pages/org-edit/PackSelectionDialog.tsxweb/src/api/types.tsweb/src/pages/org-edit/TeamListSection.tsxweb/src/stores/company.tsweb/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/.tsfiles
Usefont-sansorfont-mono(Geist tokens) for typography; NEVER setfontFamilydirectly in.tsx/.tsfiles
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/.tsfiles
Use@/lib/motionpresets 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 fromweb/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.tsand presets from@/lib/motion.
A PostToolUse hook (scripts/check_web_design_system.py) enforces design system rules on every Edit/Write toweb/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. Seeweb/CLAUDE.mdfor component inventory and design system reference.
Files:
web/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/pages/OrgChartPage.tsxweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/pages/OrgEditPage.tsxweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsxweb/src/__tests__/pages/org/build-org-tree.test.tsweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/org/TeamGroupNode.tsxweb/src/hooks/useOrgEditData.tsweb/src/pages/org-edit/DepartmentsTab.tsxweb/src/api/endpoints/company.tsweb/src/pages/org-edit/TeamDeleteConfirmDialog.tsxweb/src/pages/org-edit/PackApplyPreviewDialog.tsxweb/src/pages/org-edit/TeamEditDialog.tsxweb/src/utils/budget.tsweb/src/pages/org/build-org-tree.tsweb/src/pages/org-edit/PackSelectionDialog.tsxweb/src/api/types.tsweb/src/pages/org-edit/TeamListSection.tsxweb/src/stores/company.tsweb/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-leaksflag when running tests for the React web dashboard.
Use fast-check for property-based testing in React withfc.assertandfc.propertyinstead of Hypothesis.
Files:
web/src/__tests__/pages/org-edit/DepartmentsTab.test.tsxweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsxweb/src/__tests__/pages/org/build-org-tree.test.ts
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: In Python code, do NOT usefrom __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations.
In Python 3.14 code, use PEP 758 except syntax: useexcept 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__.pysrc/synthorg/observability/events/template.pysrc/synthorg/observability/events/company.pysrc/synthorg/budget/__init__.pysrc/synthorg/core/company.pysrc/synthorg/observability/events/api.pytests/unit/api/controllers/test_template_packs_rebalance.pytests/unit/budget/test_rebalance.pysrc/synthorg/api/controllers/template_packs.pysrc/synthorg/budget/rebalance.pytests/unit/api/controllers/test_teams.pysrc/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), usecopy.deepcopy()at construction and wrap withMappingProxyTypefor read-only enforcement.
Distinguish between frozen Pydantic models for config/identity and separate mutable-via-copy models usingmodel_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) withallow_inf_nan=Falsein allConfigDictdeclarations to rejectNaN/Infin numeric fields at validation time.
Use@computed_fieldfor derived values in Pydantic models instead of storing and validating redundant fields (e.g.,TokenUsage.total_tokens).
UseNotBlankStr(fromcore.types) for all identifier/name fields in Pydantic models -- including optional (NotBlankStr | None) and tuple (tuple[NotBlankStr, ...]) variants -- instead of manual whitespace validators.
Preferasyncio.TaskGroupfor fan-out/fan-in parallel operations in new Python code (e.g., multiple tool invocations, parallel agent calls) instead of barecreate_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 undersynthorg.observability.eventsfor event names (e.g.,API_REQUEST_STARTEDfromevents.api). Import directly:from synthorg.observability.events.<domain> import EVENT_CONSTANT.
In Python logging, use structured kwargs withlogger.info(EVENT, key=value)format -- never uselogger.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__.pysrc/synthorg/observability/events/template.pysrc/synthorg/observability/events/company.pysrc/synthorg/budget/__init__.pysrc/synthorg/core/company.pysrc/synthorg/observability/events/api.pysrc/synthorg/api/controllers/template_packs.pysrc/synthorg/budget/rebalance.pysrc/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__.pysrc/synthorg/observability/events/template.pysrc/synthorg/observability/events/company.pysrc/synthorg/budget/__init__.pysrc/synthorg/core/company.pysrc/synthorg/observability/events/api.pysrc/synthorg/api/controllers/template_packs.pysrc/synthorg/budget/rebalance.pysrc/synthorg/api/controllers/teams.py
web/src/**/*.stories.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.stories.tsx: For Storybook stories withtags: ['autodocs'], ensure@storybook/addon-docsis installed and added to addons
Usestorybook/testandstorybook/actionsimport paths in Storybook stories (not@storybook/testor@storybook/addon-actions)
Files:
web/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/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.parametrizefor testing similar cases in Python.
Use generic test names in Python tests:test-provider,test-small-001instead of real vendor names (Anthropic, OpenAI, Claude, GPT).
For timing-sensitive Python tests, mocktime.monotonic()andasyncio.sleep()to make them deterministic instead of widening timing margins. Useasyncio.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.pytests/unit/budget/test_rebalance.pytests/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.pytests/unit/budget/test_rebalance.pytests/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.tsxweb/src/__tests__/pages/OrgEditPage.test.tsxweb/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsxweb/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.tsxweb/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.tsxweb/src/pages/org/TeamGroupNode.tsxweb/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__.pysrc/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__.pysrc/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.pysrc/synthorg/observability/events/company.pysrc/synthorg/core/company.pysrc/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.pysrc/synthorg/observability/events/company.pysrc/synthorg/core/company.pysrc/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.pysrc/synthorg/observability/events/company.pysrc/synthorg/budget/__init__.pysrc/synthorg/core/company.pyweb/src/pages/org-edit/PackApplyPreviewDialog.tsxweb/src/utils/budget.tstests/unit/budget/test_rebalance.pysrc/synthorg/api/controllers/template_packs.pysrc/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.pysrc/synthorg/core/company.pysrc/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.pysrc/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__.pysrc/synthorg/core/company.pyweb/src/pages/org-edit/PackApplyPreviewDialog.tsxweb/src/utils/budget.tstests/unit/budget/test_rebalance.pysrc/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/src/__tests__/pages/org-edit/DepartmentEditDrawer.test.tsxweb/src/pages/org-edit/PackApplyPreviewDialog.tsxweb/src/pages/org-edit/PackSelectionDialog.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/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.tsxweb/src/pages/org-edit/PackApplyPreviewDialog.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/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.tsxweb/src/pages/org-edit/PackApplyPreviewDialog.tsxweb/src/pages/org-edit/TeamListSection.tsxweb/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.pysrc/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.pysrc/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.tsxweb/src/pages/org-edit/PackApplyPreviewDialog.tsxweb/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.tsxweb/src/pages/org-edit/PackApplyPreviewDialog.tsxweb/src/pages/org-edit/TeamEditDialog.tsxweb/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.tsxweb/src/pages/org-edit/PackApplyPreviewDialog.tsxweb/src/api/types.tsweb/src/pages/org-edit/TeamListSection.tsxweb/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.tsweb/src/pages/org-edit/PackSelectionDialog.tsxweb/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.tsweb/src/pages/org-edit/PackSelectionDialog.tsxweb/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.tsxweb/src/pages/org-edit/PackApplyPreviewDialog.tsxweb/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.tsxweb/src/pages/org-edit/TeamEditDialog.tsxweb/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.tsxweb/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.pytests/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
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (2)
web/src/pages/org-edit/DepartmentsTab.stories.tsx (1)
61-65:⚠️ Potential issue | 🟡 MinorPreserve existing team fields in mock update handler.
At Lines 61-65,
onUpdateTeamreplaces 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
onUpdateTeamfallback can still produce invalid team state.On Line 53-Line 54, defaulting to
lead: 'Unassigned'andmembers: []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
📒 Files selected for processing (5)
tests/unit/api/controllers/test_teams.pytests/unit/budget/test_rebalance.pyweb/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/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 usecreateLoggerfrom@/lib/logger-- never bareconsole.warn/console.error/console.debugin application code
Logger variable name must always beconst 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 throughsanitizeArg
Attacker-controlled fields inside structured objects must be wrapped insanitizeForLog()before embedding in log calls
Files:
web/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/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/.tsfiles
Usefont-sansorfont-mono(Geist tokens) for typography; NEVER setfontFamilydirectly in.tsx/.tsfiles
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/.tsfiles
Use@/lib/motionpresets 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 fromweb/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.tsand presets from@/lib/motion.
A PostToolUse hook (scripts/check_web_design_system.py) enforces design system rules on every Edit/Write toweb/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. Seeweb/CLAUDE.mdfor component inventory and design system reference.
Files:
web/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsxweb/src/pages/org-edit/TeamListSection.tsx
web/src/**/*.stories.tsx
📄 CodeRabbit inference engine (web/CLAUDE.md)
web/src/**/*.stories.tsx: For Storybook stories withtags: ['autodocs'], ensure@storybook/addon-docsis installed and added to addons
Usestorybook/testandstorybook/actionsimport paths in Storybook stories (not@storybook/testor@storybook/addon-actions)
Files:
web/src/pages/org-edit/DepartmentEditDrawer.stories.tsxweb/src/pages/org-edit/DepartmentsTab.stories.tsx
**/*.py
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.py: In Python code, do NOT usefrom __future__ import annotations-- Python 3.14 has PEP 649 native lazy annotations.
In Python 3.14 code, use PEP 758 except syntax: useexcept 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.pytests/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.parametrizefor testing similar cases in Python.
Use generic test names in Python tests:test-provider,test-small-001instead of real vendor names (Anthropic, OpenAI, Claude, GPT).
For timing-sensitive Python tests, mocktime.monotonic()andasyncio.sleep()to make them deterministic instead of widening timing margins. Useasyncio.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.pytests/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.pytests/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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
leadto the team fixture at Line 31 keeps the story data consistent with the team model used byDepartmentsTab.web/src/pages/org-edit/DepartmentEditDrawer.stories.tsx (1)
46-50: Good defaulting inonCreateTeammock.This now returns a structurally valid
TeamConfigand avoids empty-member teams whenmembersis omitted.web/src/pages/org-edit/TeamListSection.tsx (3)
123-142: LGTM! Error handling properly addresses previous review feedback.The
handleDragEndcallback correctly logs failures vialog.errorwith 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
handleDeleteConfirmcorrectly logs failures with context (teamName,reassignTo) and the caught error, usesfinallyfor guaranteed state cleanup, and shows appropriate user feedback via toast.
189-211: LGTM! DnD implementation follows@dnd-kitbest practices.The
DndContextandSortableContextare 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_departmentshelper 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_teamstest verifies that reordering with an emptyteam_nameslist 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
_depthelper 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_factoris explicitlyNonein this mode.
135-150: LGTM! Zero-budget over-max test now includesnew_totalassertion.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_factorisNonefor 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
RebalanceResultis a properNamedTuplewith 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
|
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.
|
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (4)
cli/go.sumis excluded by!**/*.sumsite/package-lock.jsonis excluded by!**/package-lock.jsonuv.lockis excluded by!**/*.lockweb/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (3)
cli/go.modweb/public/mockServiceWorker.jsweb/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 usecreateLoggerfrom@/lib/logger-- never bareconsole.warn/console.error/console.debugin application code
Logger variable name must always beconst 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 throughsanitizeArg
Attacker-controlled fields inside structured objects must be wrapped insanitizeForLog()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/.tsfiles
Usefont-sansorfont-mono(Geist tokens) for typography; NEVER setfontFamilydirectly in.tsx/.tsfiles
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/.tsfiles
Use@/lib/motionpresets 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 fromweb/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.tsand presets from@/lib/motion.
A PostToolUse hook (scripts/check_web_design_system.py) enforces design system rules on every Edit/Write toweb/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. Seeweb/CLAUDE.mdfor 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.modsegments.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_VERSIONis '2.13.0' and the installed MSW version (from package-lock.json) is also 2.13.0. TheINTEGRITY_CHECKSUMvalue is dynamically computed during themsw initprocess 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.
| <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> |
There was a problem hiding this comment.
🛠️ 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) |
There was a problem hiding this comment.
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.
🤖 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).
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, membersPATCH /{team_name}-- rename, change lead, replace membersDELETE /{team_name}?reassign_to=-- delete with optional member reassignment (guards against self-reassignment)PATCH /reorder-- reorder teams within a departmentAll endpoints use the established
AGENT_LOCK+ settings persistence pattern fromtemplate_packs.py.Frontend:
TeamListSectionwith @dnd-kit sortable reorder, edit/delete actions per team cardTeamEditDialog(Base UI Dialog) for create/edit with name, lead, TagInput membersTeamDeleteConfirmDialogwrapping ConfirmDialog with optional reassignment SelectFieldDepartmentEditDrawerreplacing the former read-only teams sectionOrg Chart:
TeamGroupNodecomponent (dashed border, team name + member count)build-org-tree.tsemits team group nodes as intermediate layer between department and agent nodes#1080: Budget Rebalance
Backend:
compute_rebalance()pure function insrc/synthorg/budget/rebalance.pywith 3 modes:none,scale_existing(default),reject_if_overApplyTemplatePackRequestextended withrebalance_modeparam (default:scale_existing)ApplyTemplatePackResponseextended withbudget_before,budget_after,rebalance_mode,scale_factorreject_if_overmodeCOMPANY_BUDGET_UNDER_ALLOCATEDevent in Company model validatorFrontend:
PackApplyPreviewDialogshown before applying packs with departments -- budget snapshot, SegmentedControl for rebalance mode, before/after preview tablecomputeBudgetPreview()client-side utility mirrors backend scaling logicDocs
docs/design/page-structure.mdupdated: "nested teams editing" no longer deferred, team endpoints added to API listTest Plan
uv run python -m pytest tests/ -n 8-- 14349 passednpm --prefix web run test-- 2409 passeduv run ruff check src/ tests/-- cleanuv run mypy src/ tests/-- cleannpm --prefix web run type-check+lint-- cleanReview Coverage
Pre-reviewed by 3 agents (backend code reviewer, frontend reviewer, API contract drift checker). 10 findings addressed:
TeamConfig.leadrequired to match backendCOMPANY_BUDGET_UNDER_ALLOCATEDevent constantDependencies
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