Skip to content

feat: Org Chart force-directed view and drag-drop reassignment (#872, #873)#912

Merged
Aureliolo merged 7 commits intomainfrom
feat/org-chart-features
Mar 29, 2026
Merged

feat: Org Chart force-directed view and drag-drop reassignment (#872, #873)#912
Aureliolo merged 7 commits intomainfrom
feat/org-chart-features

Conversation

@Aureliolo
Copy link
Copy Markdown
Owner

Summary

  • Force-directed communication graph view (feat: Org Chart force-directed communication graph view #872): d3-force layout engine with edge thickness encoding message volume and animated dashes encoding frequency. Smooth 400ms position interpolation between Hierarchy and Communication views. Includes aggregateMessages() for client-side message aggregation, computeForceLayout() for d3-force simulation, CommunicationEdge custom React Flow edge, and useCommunicationEdges data hook.
  • Inline drag-drop agent reassignment (feat: Org Chart inline drag-drop agent reassignment #873): Drag agent nodes between department groups in hierarchy view. Drop zone highlights with accent border, ghost opacity on drag, spring settle animation. Optimistic update with targeted rollback on API failure. ARIA live announcements for all drag phases.
  • Pre-PR review fixes: prefers-reduced-motion support for edge animations, stale data clearing on view toggle, documentation updates to page-structure.md and CLAUDE.md.

Test plan

  • npm --prefix web run test -- 172 test files, 2068 tests pass (includes 7 new test files with 47 new tests)
  • npm --prefix web run type-check -- zero errors
  • npm --prefix web run lint -- zero warnings
  • Visual: toggle between Hierarchy and Communication views on /org -- smooth transition, edges show varying thickness
  • Visual: drag agent between departments -- ghost opacity, drop zone highlight, settle animation
  • Storybook: CommunicationEdge stories (Low/Medium/High volume) and DropTargetActive story render correctly

Review coverage

Pre-reviewed by 3 agents (frontend-reviewer, issue-resolution-verifier, docs-consistency), 7 findings addressed.

Closes #872
Closes #873

🤖 Generated with Claude Code

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 29, 2026

Walkthrough

The PR adds a force-directed Communication view and a Hierarchy/Communication toggle for the Org Chart. It introduces d3-force and typing deps, new modules: message aggregation, force-layout, and drop-target detection; a CommunicationEdge React Flow edge (with stories); a useCommunicationEdges hook and view-mode-aware useOrgChartData + buildCommunicationEdges; optimisticReassignAgent in the company store; OrgChartPage transition/drag-drop integration; API signature update for listMessages to accept AbortSignal; tests and docs updates.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 40.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat: Org Chart force-directed view and drag-drop reassignment (#872, #873)' is directly related to the main changes: implementing force-directed communication graph view and drag-drop agent reassignment.
Description check ✅ Passed The PR description comprehensively covers the changes: force-directed communication graph with d3-force and edge animations, drag-drop reassignment with visual feedback, and pre-PR fixes.
Linked Issues check ✅ Passed All core coding requirements from #872 and #873 are met: force-directed layout (computeForceLayout), communication edges with volume/frequency encoding (CommunicationEdge, aggregateMessages), drag-drop reassignment (findDropTarget, optimisticReassignAgent), API integration (useCommunicationEdges, listMessages with AbortSignal), smooth transitions (interpolation in OrgChartPage), ARIA announcements, and comprehensive tests.
Out of Scope Changes check ✅ Passed All changes are within scope of #872 and #873: d3-force integration, message aggregation, communication edges, drag-drop detection/visuals/API, and supporting dependencies (e.g., DepartmentGroupNode.tsx update). Documentation updates and Storybook stories are expected deliverables.

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


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

@socket-security
Copy link
Copy Markdown

socket-security bot commented Mar 29, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​@​types/​d3-force@​3.0.101001007580100
Addednpm/​d3-force@​3.0.010010010082100

View full report

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 29, 2026 08:51 — with GitHub Actions Inactive
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 29, 2026

Dependency Review

The following issues were found:
  • ✅ 0 vulnerable package(s)
  • ✅ 0 package(s) with incompatible licenses
  • ✅ 0 package(s) with invalid SPDX license definitions
  • ✅ 0 package(s) with unknown licenses.
  • ⚠️ 2 packages with OpenSSF Scorecard issues.
See the Details below.

Snapshot Warnings

⚠️: No snapshots were found for the head SHA 779e4ba.
Ensure that dependencies are being submitted on PR branches. Re-running this action after a short time may resolve the issue. See the documentation for more information and troubleshooting advice.

OpenSSF Scorecard

PackageVersionScoreDetails
npm/@types/d3-force 3.0.10 🟢 6.5
Details
CheckScoreReason
Packaging⚠️ -1packaging workflow not detected
Code-Review🟢 8Found 26/30 approved changesets -- score normalized to 8
Maintained🟢 1030 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
License🟢 9license file detected
Security-Policy🟢 10security policy file detected
Signed-Releases⚠️ -1no releases found
Branch-Protection⚠️ -1internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
Pinned-Dependencies🟢 8dependency not pinned by hash detected -- score normalized to 8
Binary-Artifacts🟢 10no binaries found in the repo
Fuzzing⚠️ 0project is not fuzzed
npm/d3-force 3.0.0 ⚠️ 2.8
Details
CheckScoreReason
Packaging⚠️ -1packaging workflow not detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Binary-Artifacts🟢 10no binaries found in the repo
Code-Review🟢 3Found 9/24 approved changesets -- score normalized to 3
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ 0Project has not signed or included provenance with any releases.
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0
npm/d3-quadtree 3.0.1 ⚠️ 2.7
Details
CheckScoreReason
Maintained⚠️ 00 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Packaging⚠️ -1packaging workflow not detected
Code-Review⚠️ 2Found 5/22 approved changesets -- score normalized to 2
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Binary-Artifacts🟢 10no binaries found in the repo
Token-Permissions⚠️ 0detected GitHub workflow tokens with excessive permissions
Pinned-Dependencies⚠️ 0dependency not pinned by hash detected -- score normalized to 0
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Security-Policy⚠️ 0security policy file not detected
Fuzzing⚠️ 0project is not fuzzed
License🟢 10license file detected
Signed-Releases⚠️ 0Project has not signed or included provenance with any releases.
Branch-Protection⚠️ 0branch protection not enabled on development/release branches
SAST⚠️ 0SAST tool is not run on all commits -- score normalized to 0

Scanned Files

  • web/package-lock.json

Copy link
Copy Markdown
Contributor

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

Choose a reason for hiding this comment

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

Code Review

This pull request enhances the Org Chart with a new communication view using d3-force and implements drag-and-drop agent reassignment between departments with optimistic updates. The reviewer feedback highlights several areas for improvement, including the replacement of magic numbers with descriptive constants, moving inline CSS to external files for better maintainability, and converting the synchronous D3 simulation to an asynchronous model to prevent UI blocking.

Comment on lines +357 to +359
const target = findDropTarget(
{ x: node.position.x + 80, y: node.position.y + 40 },
deptBounds,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The offsets 80 and 40 used here to find the center of the dragged node are magic numbers. This logic is also repeated in handleNodeDragStop. Using magic numbers makes the code harder to understand and maintain, especially if the node size changes in the future. It would be better to define and use constants for the agent node's width and height, for example:

const AGENT_NODE_WIDTH = 160;
const AGENT_NODE_HEIGHT = 80;

// ...
findDropTarget(
  { 
    x: node.position.x + AGENT_NODE_WIDTH / 2, 
    y: node.position.y + AGENT_NODE_HEIGHT / 2 
  },
  deptBounds,
)

Comment on lines +479 to +494
<style>{`
.react-flow__node.dragging {
opacity: 0.6;
transform: scale(1.02);
z-index: 1000 !important;
}
.react-flow__node.dragging > div {
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.3);
}
.react-flow__node {
transition: transform 0.4s cubic-bezier(0.17, 0.67, 0.29, 1.01);
}
@media (prefers-reduced-motion: reduce) {
.react-flow__node { transition: none; }
}
`}</style>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Injecting CSS via a <style> tag inside a component can make styles harder to manage, debug, and override compared to using external CSS files or CSS Modules. Additionally, the use of !important in z-index: 1000 !important; is generally a code smell indicating CSS specificity issues and should be avoided if possible. Consider moving these styles to a dedicated CSS file for better separation of concerns and maintainability.

}
}

const timeWindowHours = timeWindowMs / 3_600_000
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The number 3_600_000 is a magic number representing the number of milliseconds in an hour. To improve readability and maintainability, it's best to extract this into a named constant, for example:

const MS_PER_HOUR = 60 * 60 * 1000;
const timeWindowHours = timeWindowMs / MS_PER_HOUR;

.stop()

// Run simulation to convergence synchronously
simulation.tick(TICK_COUNT)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Running the d3 simulation synchronously for a fixed number of ticks (TICK_COUNT = 300) can cause UI freezing on the main thread, especially with a large number of nodes. This will negatively impact the user experience when switching to the communication view.

For better scalability and a more fluid UI, consider exploring an asynchronous approach where the simulation runs and updates node positions over multiple frames using requestAnimationFrame until it 'cools down'. This would show the graph settling into place rather than freezing the UI.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 14

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

Inline comments:
In `@docs/design/page-structure.md`:
- Line 332: Update the version mention in the Org Chart description so it
matches the shipped IA version: replace the parenthetical "v0.5.1" in the
sentence that reads "Drag-drop agent reassignment between departments is
available in hierarchy view (v0.5.1)" with "v0.5.0" (or the correct released
version if different), ensuring the page's versioning aligns with the IA/release
notes.

In `@web/src/__tests__/pages/org/aggregate-messages.test.ts`:
- Around line 95-99: The test's description is incorrect: aggregateMessages
returns frequency = Infinity for a zero time window, but the test message claims
it's clamped to volume (which happens later in CommunicationEdge). Update the
test in aggregate-messages.test.ts by either changing the description to state
that aggregateMessages yields Infinity for a zero time window or by asserting
the clamped value if you instead want to test CommunicationEdge behavior;
specifically modify the test around aggregateMessages(...) to make the
description and expectation consistent (referencing the aggregateMessages
function and the CommunicationEdge clamping behavior to decide which behavior
the test should cover).

In `@web/src/__tests__/pages/org/reassign-agent.test.ts`:
- Around line 82-90: Add a new test in
web/src/__tests__/pages/org/reassign-agent.test.ts that covers the same-agent
concurrent rollback path: call
useCompanyStore.getState().optimisticReassignAgent twice for the same agent
(e.g., 'alice' to two different departments), then invoke the first returned
rollback after invoking the second rollback (so the first rollback executes
last) and assert final config.agents reflects the correct department; use the
existing optimisticReassignAgent helper and the config assertions pattern used
in the current test to locate and mirror behavior.

In `@web/src/hooks/useOrgChartData.ts`:
- Line 35: The edge id generation using the template id:
`comm-${link.source}-${link.target}` can collide when agent names contain
hyphens; update the construction in useOrgChartData (the id assignment for
communication edges) to produce collision-safe IDs—for example, encode each part
or include their lengths or use JSON.stringify/encodeURIComponent on link.source
and link.target or join them with a delimiter that cannot appear in IDs (e.g.,
base64 or percent-encoding). Replace the existing template string with the
chosen safe-encoding approach so ids are unique for each (link.source,
link.target) pair.
- Around line 102-109: In the force view branch, filter commLinks to only
include links whose source and target IDs are present in the visible node set
before building edges so no orphan edges are emitted; create a set of visible
IDs from agentNodes/freeNodes (using the node id field), produce
filteredCommLinks = commLinks.filter(l => visibleIds.has(l.source) &&
visibleIds.has(l.target)), then call buildCommunicationEdges(filteredCommLinks)
(and optionally pass filteredCommLinks into computeForceLayout for consistency)
instead of using the original commLinks.

In `@web/src/pages/org/CommunicationEdge.stories.tsx`:
- Around line 61-63: The story metadata currently only sets parameters.a11y;
update the Storybook configuration for this story by adding
parameters.backgrounds.options (provide the project's standard background color
options array) and set initialGlobals.backgrounds.value to the project's default
background key so Storybook 10 and our conventions are satisfied; modify the
exported story metadata object (the one with the parameters property in
CommunicationEdge.stories.tsx) to include both parameters.backgrounds.options
and initialGlobals.backgrounds.value.
- Line 41: Replace the hardcoded inline style on the story wrapper div
(currently using style={{ width: 500, height: 250 }}) with repo-compliant sizing
utilities: use Tailwind classes or density-aware tokens/CSS variables (e.g.,
width and height utility classes or token-based styles) so the story wrapper in
CommunicationEdge.stories.tsx follows "Use density-aware tokens or standard
Tailwind spacing" rules; update the div that wraps the story examples to use
those classes/variables instead of inline pixel values.

In `@web/src/pages/org/CommunicationEdge.tsx`:
- Line 46: The dynamic edgeId (`const edgeId = \`comm-dash-\${props.id}\``) is
used directly in generated CSS keyframe names and must be normalized to a safe
identifier to avoid broken keyframes or CSS injection; create a sanitized
version (e.g., compute `safeEdgeId` from `edgeId` by replacing/escaping any
characters not allowed in CSS identifiers or by using `CSS.escape` in
environments that support it) and use that sanitized identifier wherever the
keyframe name or className is interpolated (references: `edgeId` constant and
the inline `<style>` keyframe/template usage around lines where keyframes are
generated). Ensure the sanitization is applied consistently to all places
mentioned (including the other usage around line 63).
- Around line 41-44: The dashDuration calculation can exceed MAX_DASH_DURATION
for very low maxFreq; update the computation for dashDuration (used where
MIN_DASH_DURATION, MAX_DASH_DURATION and maxFreq are referenced) to clamp the
result between MIN_DASH_DURATION and MAX_DASH_DURATION — compute the
unconstrained duration (e.g., MAX_DASH_DURATION / Math.max(maxFreq, 0.1)), then
apply Math.min(Math.max(unconstrained, MIN_DASH_DURATION), MAX_DASH_DURATION) so
dashDuration never falls outside the desired bounds.

In `@web/src/pages/org/force-layout.ts`:
- Around line 104-106: The current synchronous call simulation.tick(TICK_COUNT)
blocks the main thread for large graphs; change TICK_COUNT to be adaptive (e.g.,
compute ticks = clamp(BASE_TICKS * Math.log1p(nodeCount + edgeCount), MIN_TICKS,
MAX_TICKS) using your graph's node/edge counts) and replace the single blocking
call with an incremental non-blocking loop (use requestAnimationFrame or
setTimeout) that calls simulation.tick(chunkSize) repeatedly until ticks are
consumed or the simulation's alpha has cooled; update references to TICK_COUNT
and the location that calls simulation.tick so the code uses the new adaptive
ticks and the async loop to avoid jank.

In `@web/src/pages/OrgChartPage.tsx`:
- Around line 394-407: The optimistic rollback is vulnerable to out-of-order
responses; generate an operation token/version when calling
useCompanyStore.getState().optimisticReassignAgent(agentName, newDept) and
attach it to the returned rollback closure (or to agent state), then pass that
token along with the updateAgent promise; in the catch handler verify the token
still matches the agent's current in-flight token (or version) before invoking
rollback() so a stale failure cannot revert a later successful move; update
optimisticReassignAgent, the rollback closure, and the catch path in
OrgChartPage.tsx to perform this token/version check.
- Around line 344-345: The guard that currently reads "if (node.type !== 'agent'
&& node.type !== 'ceo') return" should be tightened to only allow agent nodes:
change it to check for node.type === 'agent' (e.g., if (node.type !== 'agent')
return) while keeping the viewMode === 'hierarchy' check; do the same for the
other identical guard further down in the file so that only agent nodes can be
dragged/reassigned and CEO drags are prevented.
- Around line 479-493: The inline style block in OrgChartPage.tsx that targets
.react-flow__node.dragging and .react-flow__node.dragging > div must stop
hardcoding rgba(...) and the shadow string; replace the hardcoded values with
design token CSS variables (e.g. use var(--color-overlay) for the
overlay/opacity and var(--shadow-elev-3) or your project’s shadow token for the
box-shadow) inside the same style block for .react-flow__node.dragging and
.react-flow__node.dragging > div, and remove the !important on z-index (or
replace it with the appropriate token/utility class) while ensuring those tokens
are defined in your global CSS/Tailwind theme so the components use the semantic
tokens instead of literal rgba/box-shadow strings.

In `@web/src/stores/company.ts`:
- Around line 336-343: The rollback currently always writes prevDepartment and
can overwrite a newer change; when constructing the rollback closure capture the
department value that this change set (e.g., newDepartment) and in the returned
function first read get().config, find the agent by agentName and only perform
set({ config: ... }) to restore prevDepartment if the agent’s current department
strictly equals newDepartment (otherwise do nothing). Reference the existing
get(), set(), config, agents, agentName and prevDepartment symbols so the check
is colocated with the current rollback logic.
🪄 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: 7a8a96a8-8821-40e1-ba0c-606702cacad3

📥 Commits

Reviewing files that changed from the base of the PR and between 41fbedc and b034b32.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (22)
  • CLAUDE.md
  • docs/design/page-structure.md
  • web/package.json
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/__tests__/pages/org/OrgChartPage.test.tsx
  • web/src/__tests__/pages/org/aggregate-messages.test.ts
  • web/src/__tests__/pages/org/drop-target.test.ts
  • web/src/__tests__/pages/org/force-layout.test.ts
  • web/src/__tests__/pages/org/reassign-agent.test.ts
  • web/src/hooks/useCommunicationEdges.ts
  • web/src/hooks/useOrgChartData.ts
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
  • web/src/pages/org/CommunicationEdge.tsx
  • web/src/pages/org/DepartmentGroupNode.stories.tsx
  • web/src/pages/org/DepartmentGroupNode.tsx
  • web/src/pages/org/aggregate-messages.ts
  • web/src/pages/org/build-org-tree.ts
  • web/src/pages/org/drop-target.ts
  • web/src/pages/org/force-layout.ts
  • web/src/stores/company.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). (6)
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Documentation must follow Markdown conventions: use consistent heading levels, link to relevant sections, include code examples where appropriate, and keep content up-to-date with implementation changes.

Files:

  • CLAUDE.md
  • docs/design/page-structure.md
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always reuse existing components from web/src/components/ui/ before creating new ones. When a component is needed that is not in the existing inventory, place it in web/src/components/ui/ with a descriptive kebab-case filename, create a .stories.tsx file with all states, export props as a TypeScript interface, and use design tokens exclusively.

Files:

  • web/src/pages/org/DepartmentGroupNode.stories.tsx
  • web/src/pages/org/build-org-tree.ts
  • web/src/__tests__/pages/org/OrgChartPage.test.tsx
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/__tests__/pages/org/drop-target.test.ts
  • web/src/__tests__/pages/org/reassign-agent.test.ts
  • web/src/hooks/useCommunicationEdges.ts
  • web/src/stores/company.ts
  • web/src/__tests__/pages/org/aggregate-messages.test.ts
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
  • web/src/pages/org/DepartmentGroupNode.tsx
  • web/src/pages/org/force-layout.ts
  • web/src/hooks/useOrgChartData.ts
  • web/src/pages/org/drop-target.ts
  • web/src/pages/org/CommunicationEdge.stories.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/__tests__/pages/org/force-layout.test.ts
  • web/src/pages/org/aggregate-messages.ts
  • web/src/pages/org/CommunicationEdge.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground, bg-card, text-accent, etc.) or CSS variables (var(--so-*)) for colors. Never hardcode hex values or rgba() with hardcoded values in .tsx/.ts files.
Use font-sans or font-mono classes for typography (mapping to Geist tokens). Never set fontFamily directly in component code.
Use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing for layout. Never hardcode pixel values for layout spacing.
Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders. Never hardcode shadow or border values.
TypeScript 6.0: remove deprecated baseUrl from tsconfig; paths entries are relative to the tsconfig directory. Remove explicit "esModuleInterop": true (always true in TS 6). List needed types explicitly (e.g., "types": ["vitest/globals"]). DOM.Iterable is merged into DOM. Use "bundler" or "nodenext" for moduleResolution.

Files:

  • web/src/pages/org/DepartmentGroupNode.stories.tsx
  • web/src/pages/org/build-org-tree.ts
  • web/src/__tests__/pages/org/OrgChartPage.test.tsx
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/__tests__/pages/org/drop-target.test.ts
  • web/src/__tests__/pages/org/reassign-agent.test.ts
  • web/src/hooks/useCommunicationEdges.ts
  • web/src/stores/company.ts
  • web/src/__tests__/pages/org/aggregate-messages.test.ts
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
  • web/src/pages/org/DepartmentGroupNode.tsx
  • web/src/pages/org/force-layout.ts
  • web/src/hooks/useOrgChartData.ts
  • web/src/pages/org/drop-target.ts
  • web/src/pages/org/CommunicationEdge.stories.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/__tests__/pages/org/force-layout.test.ts
  • web/src/pages/org/aggregate-messages.ts
  • web/src/pages/org/CommunicationEdge.tsx
web/src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Do NOT recreate status dots inline—use <StatusBadge>. Do NOT build card-with-header layouts from scratch—use <SectionCard>. Do NOT create metric displays with text-metric font-bold—use <MetricCard>. Do NOT render initials circles manually—use <Avatar>. Do NOT create complex (>8 line) JSX inside .map() — extract to a shared component.

Files:

  • web/src/pages/org/DepartmentGroupNode.stories.tsx
  • web/src/__tests__/pages/org/OrgChartPage.test.tsx
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/pages/org/DepartmentGroupNode.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.tsx
web/src/**/*.stories.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Storybook 10: ESM-only, no CJS. Use storybook/test (not @storybook/test) and storybook/actions (not @storybook/addon-actions). Use defineMain from @storybook/react-vite/node and definePreview from @storybook/react-vite with explicit framework field. Use parameters.backgrounds.options (object keyed by name) + initialGlobals.backgrounds.value. Use parameters.a11y.test: 'error' | 'todo' | 'off' for WCAG compliance.

Files:

  • web/src/pages/org/DepartmentGroupNode.stories.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
docs/design/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Before implementing any feature or planning any issue, always read the relevant docs/design/ page referenced in DESIGN_SPEC.md. The design spec is the starting point for architecture, data models, and behavior. If implementation deviates from the spec, alert the user and explain why—user decides whether to proceed or update the spec. When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.

Files:

  • docs/design/page-structure.md
🧠 Learnings (29)
📓 Common learnings
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/).
📚 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 web/package.json : Web dashboard Node.js 20+; dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, ESLint, vue-tsc)

Applied to files:

  • web/package.json
  • CLAUDE.md
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.{ts,tsx} : TypeScript 6.0: remove deprecated `baseUrl` from tsconfig; `paths` entries are relative to the tsconfig directory. Remove explicit `"esModuleInterop": true` (always true in TS 6). List needed types explicitly (e.g., `"types": ["vitest/globals"]`). DOM.Iterable is merged into DOM. Use `"bundler"` or `"nodenext"` for `moduleResolution`.

Applied to files:

  • web/package.json
  • web/src/__tests__/pages/org/drop-target.test.ts
  • web/src/__tests__/pages/org/force-layout.test.ts
📚 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 web/** : Web dashboard: Node.js 20+, dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, fast-check, ESLint, vue-tsc).

Applied to files:

  • web/package.json
  • CLAUDE.md
📚 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 pyproject.toml : Dependencies: all versions use == in pyproject.toml. Groups: test (pytest + plugins, hypothesis), dev (includes test + ruff, mypy, pre-commit, commitizen, pip-audit). Required: mem0ai (Mem0 memory backend — the default and currently only backend). Install: uv sync installs everything (dev group is default).

Applied to files:

  • CLAUDE.md
📚 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:

  • CLAUDE.md
📚 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:

  • CLAUDE.md
📚 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 docker/{Dockerfile*,compose.yml} : Docker: Backend uses 3-stage build (builder → setup → distroless runtime), Chainguard Python, non-root (UID 65532), CIS-hardened. Web uses nginxinc/nginx-unprivileged, Vue 3 SPA with PrimeVue + Tailwind CSS, SPA routing, API/WebSocket proxy to backend.

Applied to files:

  • CLAUDE.md
📚 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:

  • CLAUDE.md
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to pyproject.toml : Pin all dependency versions using `==` in `pyproject.toml`. Organize dependencies into groups: `test` (pytest + plugins, hypothesis), `dev` (includes test + ruff, mypy, pre-commit, commitizen, pip-audit). Include `mem0ai`, `cryptography`, and `faker` as required dependencies.

Applied to files:

  • CLAUDE.md
📚 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:

  • CLAUDE.md
📚 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:

  • CLAUDE.md
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/components/ui/**/*.tsx : Each new shared component in `web/src/components/ui/` must have a corresponding `.stories.tsx` file with all states (default, hover, loading, error, empty). Export props as a TypeScript interface and use design tokens exclusively (no hardcoded colors, fonts, or spacing).

Applied to files:

  • web/src/pages/org/DepartmentGroupNode.stories.tsx
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: ESM-only, no CJS. Use `storybook/test` (not `storybook/test`) and `storybook/actions` (not `storybook/addon-actions`). Use `defineMain` from `storybook/react-vite/node` and `definePreview` from `storybook/react-vite` with explicit `framework` field. Use `parameters.backgrounds.options` (object keyed by name) + `initialGlobals.backgrounds.value`. Use `parameters.a11y.test: 'error' | 'todo' | 'off'` for WCAG compliance.

Applied to files:

  • web/src/pages/org/DepartmentGroupNode.stories.tsx
  • web/src/__tests__/pages/org/OrgChartPage.test.tsx
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
📚 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/OrgChartPage.test.tsx
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/__tests__/pages/org/drop-target.test.ts
  • web/src/__tests__/pages/org/reassign-agent.test.ts
  • web/src/__tests__/pages/org/aggregate-messages.test.ts
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
  • web/src/__tests__/pages/org/force-layout.test.ts
📚 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-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-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-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-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: Always read the relevant `docs/design/` page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file linking to the 7 design 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
  • web/src/pages/OrgChartPage.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/**/*.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:

  • 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-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/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)

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: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.

Applied to files:

  • docs/design/page-structure.md
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, etc.) or CSS variables (`var(--so-*)`) for colors. Never hardcode hex values or rgba() with hardcoded values in `.tsx`/`.ts` files.

Applied to files:

  • web/src/pages/org/DepartmentGroupNode.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
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 shadow or border values.

Applied to files:

  • web/src/pages/org/DepartmentGroupNode.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to **/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. When a component is needed that is not in the existing inventory, place it in `web/src/components/ui/` with a descriptive kebab-case filename, create a `.stories.tsx` file with all states, export props as a TypeScript interface, and use design tokens exclusively.

Applied to files:

  • web/src/pages/org/CommunicationEdge.stories.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.tsx : Do NOT recreate status dots inline—use `<StatusBadge>`. Do NOT build card-with-header layouts from scratch—use `<SectionCard>`. Do NOT create metric displays with `text-metric font-bold`—use `<MetricCard>`. Do NOT render initials circles manually—use `<Avatar>`. Do NOT create complex (>8 line) JSX inside `.map()` — extract to a shared component.

Applied to files:

  • web/src/pages/OrgChartPage.tsx
🔇 Additional comments (22)
web/src/pages/org/aggregate-messages.ts (2)

1-17: LGTM!

The CommunicationLink interface is well-documented with clear field semantics. The alphabetical ordering contract for source/target is clearly specified.


26-57: LGTM!

The aggregation logic is clean and correct:

  • Bidirectional normalization using alphabetical comparison is effective.
  • Self-message exclusion is handled early.
  • Volume-descending sort ensures consistent output ordering.

The timeWindowMs = 0 edge case producing Infinity is acceptable since the consuming code (CommunicationEdge.tsx lines 39-44) guards against this with Math.max(MIN_DASH_DURATION, ...).

web/src/__tests__/pages/org/aggregate-messages.test.ts (2)

1-10: LGTM!

Good test setup with clear constants and proper imports from vitest and fast-check.


101-132: LGTM!

Good use of property-based testing with fast-check. The properties verify essential invariants: positive integer volumes and no self-links in output.

web/src/hooks/useCommunicationEdges.ts (3)

1-18: LGTM!

Clear interface definition and well-documented constants. The 24-hour default time window and pagination limits are reasonable.


67-79: Pagination offset uses server-returned limit instead of requested PAGE_LIMIT.

Line 77 increments offset by result.limit (server response) rather than PAGE_LIMIT (requested). If the server returns a different limit than requested (e.g., caps at 50), pagination will still work correctly. However, if this is intentional, consider adding a comment to clarify.

This is defensive and works correctly—just noting the subtlety.


81-93: LGTM!

Proper cancellation guard prevents stale state updates after unmount or re-render. Error handling provides both the error message and a fallback string.

web/src/__tests__/hooks/useCommunicationEdges.test.ts (4)

1-24: LGTM!

Clean mock setup with proper import ordering (mock before import). The beforeEach reset ensures test isolation.


26-50: LGTM!

Good coverage of the happy path including loading state transitions and proper aggregation verification.


52-78: LGTM!

Pagination test correctly verifies multiple API calls and cross-page aggregation.


80-109: LGTM!

Error and empty state handling tests are comprehensive, verifying both the error message propagation and the empty result case.

web/package.json (2)

67-67: LGTM!

Type definitions for d3-force are properly added to devDependencies with a compatible version.


41-41: d3-force ^3.0.0 is the current latest version on npm.

Version is valid and correctly specified with standard semver range.

web/src/pages/org/DepartmentGroupNode.stories.tsx (1)

88-102: LGTM!

Good addition of the DropTargetActive story to demonstrate the drop-target highlight state. The story uses realistic department metrics and correctly sets isDropTarget: true.

CLAUDE.md (1)

423-423: LGTM!

Documentation correctly updated to include d3-force in the web dashboard dependencies list, keeping CLAUDE.md in sync with package.json.

web/src/pages/org/DepartmentGroupNode.tsx (1)

22-26: LGTM!

Clean implementation of drop-target highlighting:

  • transition-colors duration-200 enables smooth visual feedback.
  • Conditional styling correctly prioritizes isDropTarget over health-based colors.
  • Uses semantic Tailwind classes (border-accent, bg-accent/5) per coding guidelines.
web/src/pages/org/build-org-tree.ts (1)

31-31: Type extension is clean and backward-compatible.

Optional isDropTarget on Line 31 is a safe addition for department highlight state without affecting existing node data construction.

web/src/__tests__/pages/org/OrgChartPage.test.tsx (1)

27-43: Mock contract and new error-path coverage look correct.

The added communication fields and reduced-motion mock match the updated page behavior, and the new error-banner test exercises the intended failure UX.

Also applies to: 71-73, 156-161

web/src/__tests__/pages/org/CommunicationEdge.test.tsx (1)

57-121: Good behavioral coverage for edge rendering and animation semantics.

This suite validates the key visual encodings (stroke width/dash animation/keyframes) with deterministic mocks.

web/src/__tests__/pages/org/drop-target.test.ts (1)

15-112: Great coverage breadth for hit-testing behavior.

Deterministic cases plus fast-check properties provide solid confidence in boundary, overlap, and null-hit behavior.

web/src/__tests__/pages/org/force-layout.test.ts (1)

14-140: Force-layout test coverage is comprehensive and well-targeted.

The suite checks both correctness invariants and edge conditions (including malformed links), which is exactly what this layout utility needs.

web/src/pages/org/drop-target.ts (1)

25-48: Hit-testing implementation is clear and deterministic.

Inclusive bounds plus smallest-area selection is a good conflict-resolution rule for overlapping department regions.

Comment on lines +394 to +407
// Optimistic update with rollback
const rollback = useCompanyStore.getState().optimisticReassignAgent(agentName, newDept)

useCompanyStore.getState().updateAgent(agentName, { department: newDept })
.then(() => {
announce(`Moved ${agentName} to ${newDept}`)
addToast({ variant: 'success', title: `Moved ${agentName} to ${newDept}` })
})
.catch((err: unknown) => {
rollback()
const msg = err instanceof Error ? err.message : 'Unknown error'
announce(`Failed to move ${agentName}, returned to ${originalDept}`)
addToast({ variant: 'error', title: 'Reassignment failed', description: msg })
})
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Protect optimistic rollback from out-of-order responses.

If the same agent is moved multiple times quickly, an earlier failed request can rollback a later successful move. Guard rollback with an operation token/version per agent (or block re-drag while request is in flight).

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

In `@web/src/pages/OrgChartPage.tsx` around lines 394 - 407, The optimistic
rollback is vulnerable to out-of-order responses; generate an operation
token/version when calling
useCompanyStore.getState().optimisticReassignAgent(agentName, newDept) and
attach it to the returned rollback closure (or to agent state), then pass that
token along with the updateAgent promise; in the catch handler verify the token
still matches the agent's current in-flight token (or version) before invoking
rollback() so a stale failure cannot revert a later successful move; update
optimisticReassignAgent, the rollback closure, and the catch path in
OrgChartPage.tsx to perform this token/version check.

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 29, 2026 09:26 — with GitHub Actions Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 9

♻️ Duplicate comments (2)
docs/design/page-structure.md (1)

332-332: ⚠️ Potential issue | 🟡 Minor

Resolve the v0.5.0 vs v0.5.1 split.

This page is still scoped as the v0.5.0 IA, so calling hierarchy drag-drop a v0.5.1 feature here leaves the release boundary unclear. Either bump the doc version or remove the later-version qualifier consistently across the Org Chart docs.

As per coding guidelines **/*.md: “Documentation must follow Markdown conventions: use consistent heading levels, link to relevant sections, include code examples where appropriate, and keep content up-to-date with implementation changes.”

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

In `@docs/design/page-structure.md` at line 332, The doc currently mixes v0.5.0
scope with a v0.5.1 feature callout (“Drag-drop agent reassignment... (v0.5.1)”)
which creates a release boundary mismatch; update the Org Chart docs to be
consistent by either (A) bumping the page version to v0.5.1 and updating any
other v0.5.0 references to v0.5.1, or (B) remove the “(v0.5.1)” qualifier and
rephrase the sentence to state the feature without a version tag; apply the
chosen change across all occurrences in this page (search for “v0.5.0”/“v0.5.1”
and the phrase “Drag-drop agent reassignment”) and ensure headings and links
remain valid per the Markdown conventions noted in the guidelines.
web/src/pages/org/CommunicationEdge.stories.tsx (1)

57-67: 🧹 Nitpick | 🔵 Trivial

Missing Storybook background configuration per project conventions.

The meta configuration lacks parameters.backgrounds.options and initialGlobals.backgrounds.value required by Storybook 10 conventions.

♻️ Suggested fix
 const meta = {
   title: 'OrgChart/CommunicationEdge',
   component: Wrapper,
   tags: ['autodocs'],
   parameters: {
     a11y: { test: 'error' },
+    backgrounds: {
+      options: {
+        light: { name: 'Light', value: '#ffffff' },
+        dark: { name: 'Dark', value: '#1a1a1a' },
+      },
+    },
   },
+  initialGlobals: {
+    backgrounds: { value: 'light' },
+  },
 } satisfies Meta<typeof Wrapper>

As per coding guidelines web/src/**/*.stories.tsx: "Use parameters.backgrounds.options ... + initialGlobals.backgrounds.value."

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

In `@web/src/pages/org/CommunicationEdge.stories.tsx` around lines 57 - 67, The
Storybook meta object named "meta" is missing the required background
configuration; add a backgrounds options array under meta.parameters (e.g.,
parameters.backgrounds.options) and set the default background via
meta.initialGlobals.backgrounds.value to follow Storybook 10 conventions. Update
the meta declaration (the const meta = { ... } satisfies Meta<typeof Wrapper>)
to include these two keys with the project-standard background options and the
initial global background value so stories for Wrapper pick up the correct
background.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/design/page-structure.md`:
- Around line 31-36: The routing table row for the /org route must be updated to
match the new interactive behavior described above: replace the “Read-only
visualization” label with a succinct description such as “Interactive
organization visualization — Hierarchy view (default) with drag-drop inline
agent reassignment (optimistic update with rollback, ARIA live announcements,
drop-zone highlight) and Communication view (d3-force) with
message-aggregation”; ensure the row mentions the 400ms view transition and that
clicking nodes opens the Agent Detail panel so the route description and the
“Hierarchy view”/“Communication view” sections are consistent.

In `@web/src/__tests__/pages/org/reassign-agent.test.ts`:
- Around line 63-67: Add an assertion to the test that calling rollback when
config is null does not mutate state: after setting useCompanyStore.setState({
config: null }) and invoking the rollback returned from
useCompanyStore.getState().optimisticReassignAgent('alice', 'design'), capture
the store state (or relevant fields, e.g., getState().config and
getState().agents or assignments) before calling rollback and assert they are
unchanged after rollback; reference the optimisticReassignAgent call and the
rollback function in your assertions to ensure no state mutation occurs on null
config.

In `@web/src/hooks/useCommunicationEdges.ts`:
- Around line 72-80: The loop in useCommunicationEdges (the for loop using
MAX_PAGES, PAGE_LIMIT, offset and calling listMessages) can silently truncate
data when result.total > MAX_PAGES * PAGE_LIMIT; change the logic to either
enforce a documented time-range parameter on listMessages (pass start/end or
limit the query server-side) or explicitly detect truncation (if page reaches
MAX_PAGES or offset < result.total after the loop) and surface that to callers
(e.g., return a truncated flag or throw a clear error) so the UI can show that
data is partial instead of rendering a misleading complete graph.
- Around line 64-75: The AbortController created in useCommunicationEdges
(variable controller) isn't forwarded to cancel in-flight requests; update the
listMessages function signature (in web/src/api/endpoints/messages.ts) to accept
an optional AbortSignal (e.g., signal?: AbortSignal) and pass it into the axios
call (apiClient.get) via the request config so the HTTP request can be aborted,
then update all call sites (including the listMessages calls inside fetchAll in
useCommunicationEdges) to pass controller.signal as that argument.

In `@web/src/hooks/useOrgChartData.ts`:
- Around line 66-68: The empty catch blocks in useOrgChartData.ts (for
companyStore.fetchDepartmentHealths and the other fetch call) suppress useful
debug info; keep the store-based error handling but update each catch to log the
caught error (e.g., using console.error or the existing app logger) with a short
contextual message referencing the function name
(companyStore.fetchDepartmentHealths and the other fetch method) so errors are
visible during debugging while still letting the store manage state.

In `@web/src/pages/org/CommunicationEdge.tsx`:
- Around line 63-68: The component CommunicationEdge currently injects a
per-instance <style> with a unique `@keyframes` name derived from edgeId; change
this to reuse a single global keyframe name (e.g., comm-dash) instead of
generating comm-dash-{edgeId}: remove the per-edge <style> injection in the
return value and have all edges use the shared keyframe name when setting
animation (via the style object passed into BaseEdge or a CSS variable). Keep
per-edge animation-duration by setting style.animationDuration (or a CSS
variable) on the style object used by BaseEdge so each instance can vary timing
while avoiding many duplicated `@keyframes` rules.

In `@web/src/pages/OrgChartPage.tsx`:
- Around line 484-498: The .react-flow__node.dragging rule currently uses a
hardcoded opacity: 0.6; change this to use a CSS variable (e.g.
--so-opacity-dragging) instead of the literal number, update the style block
where .react-flow__node.dragging is defined to use var(--so-opacity-dragging,
0.6) for a safe fallback, and add that variable to the relevant scope (root or
the same style block) so it aligns with the design system alongside the existing
var(--so-shadow-card-hover) usage.
- Around line 366-372: Move the announcement out of the setDragOverDeptId state
updater: read the current dragOverDeptId state (or a ref that mirrors it) into a
local variable, compute shouldAnnounce = current !== newOverId && target, call
setDragOverDeptId(newOverId) normally, then if shouldAnnounce call
queueMicrotask(() => announce(`Over ${target.departmentName}`)); update/create a
dragOverDeptIdRef if you need a stable current value outside renders so the
comparison is reliable across async handlers; remove the announce call from
inside the updater function.
- Around line 56-58: AGENT_NODE_WIDTH and AGENT_NODE_HEIGHT are hardcoded for
drag hit testing; update the drag handlers (where AGENT_NODE_WIDTH /
AGENT_NODE_HEIGHT are referenced) to use each node's measured dimensions (e.g.,
n.measured?.width and n.measured?.height) with the existing constants as
fallbacks so hit testing matches rendered AgentNode size; mirror the approach
used in the deptBounds calculation (which already reads
n.measured?.height/width) to compute center-point offsets and comparisons.

---

Duplicate comments:
In `@docs/design/page-structure.md`:
- Line 332: The doc currently mixes v0.5.0 scope with a v0.5.1 feature callout
(“Drag-drop agent reassignment... (v0.5.1)”) which creates a release boundary
mismatch; update the Org Chart docs to be consistent by either (A) bumping the
page version to v0.5.1 and updating any other v0.5.0 references to v0.5.1, or
(B) remove the “(v0.5.1)” qualifier and rephrase the sentence to state the
feature without a version tag; apply the chosen change across all occurrences in
this page (search for “v0.5.0”/“v0.5.1” and the phrase “Drag-drop agent
reassignment”) and ensure headings and links remain valid per the Markdown
conventions noted in the guidelines.

In `@web/src/pages/org/CommunicationEdge.stories.tsx`:
- Around line 57-67: The Storybook meta object named "meta" is missing the
required background configuration; add a backgrounds options array under
meta.parameters (e.g., parameters.backgrounds.options) and set the default
background via meta.initialGlobals.backgrounds.value to follow Storybook 10
conventions. Update the meta declaration (the const meta = { ... } satisfies
Meta<typeof Wrapper>) to include these two keys with the project-standard
background options and the initial global background value so stories for
Wrapper pick up the correct background.
🪄 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: 06e3ecf8-e2c1-4f70-b94d-5134df847f6c

📥 Commits

Reviewing files that changed from the base of the PR and between b034b32 and e30a01d.

📒 Files selected for processing (13)
  • docs/design/operations.md
  • docs/design/page-structure.md
  • docs/design/ux-guidelines.md
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
  • web/src/__tests__/pages/org/aggregate-messages.test.ts
  • web/src/__tests__/pages/org/reassign-agent.test.ts
  • web/src/hooks/useCommunicationEdges.ts
  • web/src/hooks/useOrgChartData.ts
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
  • web/src/pages/org/CommunicationEdge.tsx
  • web/src/pages/org/aggregate-messages.ts
  • web/src/stores/company.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). (4)
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Backend
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
docs/design/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Before implementing any feature or planning any issue, always read the relevant docs/design/ page referenced in DESIGN_SPEC.md. The design spec is the starting point for architecture, data models, and behavior. If implementation deviates from the spec, alert the user and explain why—user decides whether to proceed or update the spec. When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.

Files:

  • docs/design/ux-guidelines.md
  • docs/design/operations.md
  • docs/design/page-structure.md
**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Documentation must follow Markdown conventions: use consistent heading levels, link to relevant sections, include code examples where appropriate, and keep content up-to-date with implementation changes.

Files:

  • docs/design/ux-guidelines.md
  • docs/design/operations.md
  • docs/design/page-structure.md
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always reuse existing components from web/src/components/ui/ before creating new ones. When a component is needed that is not in the existing inventory, place it in web/src/components/ui/ with a descriptive kebab-case filename, create a .stories.tsx file with all states, export props as a TypeScript interface, and use design tokens exclusively.

Files:

  • web/src/__tests__/pages/org/aggregate-messages.test.ts
  • web/src/stores/company.ts
  • web/src/hooks/useCommunicationEdges.ts
  • web/src/__tests__/pages/org/reassign-agent.test.ts
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
  • web/src/pages/org/aggregate-messages.ts
  • web/src/hooks/useOrgChartData.ts
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground, bg-card, text-accent, etc.) or CSS variables (var(--so-*)) for colors. Never hardcode hex values or rgba() with hardcoded values in .tsx/.ts files.
Use font-sans or font-mono classes for typography (mapping to Geist tokens). Never set fontFamily directly in component code.
Use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing for layout. Never hardcode pixel values for layout spacing.
Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders. Never hardcode shadow or border values.
TypeScript 6.0: remove deprecated baseUrl from tsconfig; paths entries are relative to the tsconfig directory. Remove explicit "esModuleInterop": true (always true in TS 6). List needed types explicitly (e.g., "types": ["vitest/globals"]). DOM.Iterable is merged into DOM. Use "bundler" or "nodenext" for moduleResolution.

Files:

  • web/src/__tests__/pages/org/aggregate-messages.test.ts
  • web/src/stores/company.ts
  • web/src/hooks/useCommunicationEdges.ts
  • web/src/__tests__/pages/org/reassign-agent.test.ts
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
  • web/src/pages/org/aggregate-messages.ts
  • web/src/hooks/useOrgChartData.ts
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
web/src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Do NOT recreate status dots inline—use <StatusBadge>. Do NOT build card-with-header layouts from scratch—use <SectionCard>. Do NOT create metric displays with text-metric font-bold—use <MetricCard>. Do NOT render initials circles manually—use <Avatar>. Do NOT create complex (>8 line) JSX inside .map() — extract to a shared component.

Files:

  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
web/src/**/*.stories.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Storybook 10: ESM-only, no CJS. Use storybook/test (not @storybook/test) and storybook/actions (not @storybook/addon-actions). Use defineMain from @storybook/react-vite/node and definePreview from @storybook/react-vite with explicit framework field. Use parameters.backgrounds.options (object keyed by name) + initialGlobals.backgrounds.value. Use parameters.a11y.test: 'error' | 'todo' | 'off' for WCAG compliance.

Files:

  • web/src/pages/org/CommunicationEdge.stories.tsx
🧠 Learnings (20)
📓 Common learnings
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/).
📚 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/operations.md
  • 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/operations.md
  • 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/operations.md
  • docs/design/page-structure.md
  • web/src/pages/OrgChartPage.tsx
📚 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/operations.md
  • 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/operations.md
  • docs/design/page-structure.md
📚 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/aggregate-messages.test.ts
  • web/src/__tests__/pages/org/reassign-agent.test.ts
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
📚 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-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: Always read the relevant `docs/design/` page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file linking to the 7 design pages (index, 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: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.

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 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:

  • 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: Applies to src/synthorg/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)

Applied to files:

  • docs/design/page-structure.md
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.tsx : Do NOT recreate status dots inline—use `<StatusBadge>`. Do NOT build card-with-header layouts from scratch—use `<SectionCard>`. Do NOT create metric displays with `text-metric font-bold`—use `<MetricCard>`. Do NOT render initials circles manually—use `<Avatar>`. Do NOT create complex (>8 line) JSX inside `.map()` — extract to a shared component.

Applied to files:

  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, etc.) or CSS variables (`var(--so-*)`) for colors. Never hardcode hex values or rgba() with hardcoded values in `.tsx`/`.ts` files.

Applied to files:

  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
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 shadow or border values.

Applied to files:

  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind spacing for layout. Never hardcode pixel values for layout spacing.

Applied to files:

  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/components/ui/**/*.tsx : Each new shared component in `web/src/components/ui/` must have a corresponding `.stories.tsx` file with all states (default, hover, loading, error, empty). Export props as a TypeScript interface and use design tokens exclusively (no hardcoded colors, fonts, or spacing).

Applied to files:

  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to **/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. When a component is needed that is not in the existing inventory, place it in `web/src/components/ui/` with a descriptive kebab-case filename, create a `.stories.tsx` file with all states, export props as a TypeScript interface, and use design tokens exclusively.

Applied to files:

  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use `font-sans` or `font-mono` classes for typography (mapping to Geist tokens). Never set `fontFamily` directly in component code.

Applied to files:

  • web/src/pages/OrgChartPage.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: ESM-only, no CJS. Use `storybook/test` (not `storybook/test`) and `storybook/actions` (not `storybook/addon-actions`). Use `defineMain` from `storybook/react-vite/node` and `definePreview` from `storybook/react-vite` with explicit `framework` field. Use `parameters.backgrounds.options` (object keyed by name) + `initialGlobals.backgrounds.value`. Use `parameters.a11y.test: 'error' | 'todo' | 'off'` for WCAG compliance.

Applied to files:

  • web/src/pages/org/CommunicationEdge.stories.tsx
🔇 Additional comments (16)
web/src/__tests__/pages/org/aggregate-messages.test.ts (1)

101-132: Nice invariant coverage.

These fast-check properties are a strong fit for guarding normalization and self-link filtering regressions.

Based on learnings Dashboard testing: Vitest unit tests organized by feature under web/src/__tests__/. Use fast-check for property-based testing (fc.assert + fc.property).

web/src/stores/company.ts (1)

338-345: Good stale-rollback guard.

The current-department check prevents an older failed reassignment from undoing a newer optimistic move for the same agent.

web/src/pages/OrgChartPage.tsx (3)

348-349: Drag restriction to agent nodes only — addressed.

The guards at Lines 348-349 and 384 now correctly check node.type !== 'agent' only, preventing CEO nodes from being dragged for reassignment. This aligns with the feature objective.

Also applies to: 384-384


399-412: Rollback protection relies on store's reference-equality check.

The optimistic rollback now depends on optimisticReassignAgent's internal guard (per web/src/stores/company.ts:328-350), which compares the agent's current department against the captured newDepartment at rollback time. This prevents stale rollbacks when the same agent is moved multiple times quickly. The approach is sound given the store implementation.


231-277: Transition animation effect is well-structured.

The useEffect properly cancels in-flight animations, respects prefersReducedMotion(), and uses a reducer to avoid set-state-in-effect issues. The cleanup function correctly cancels pending animation frames.

web/src/pages/org/CommunicationEdge.tsx (3)

42-45: Dash duration clamping — addressed.

The formula now correctly clamps dashDuration between MIN_DASH_DURATION and MAX_DASH_DURATION using nested Math.min/Math.max. This prevents animation speed from drifting beyond intended limits.


47-48: Keyframe name sanitization — addressed.

The safeId is now derived by replacing non-alphanumeric characters (except _ and -) with underscores, preventing CSS injection and broken keyframes from unsafe edge IDs.


52-61: Style object memoization is correct.

The useMemo dependency array correctly includes all values that affect the computed style object, ensuring proper re-renders when animation parameters change.

web/src/__tests__/pages/org/reassign-agent.test.ts (2)

92-100: Stale rollback regression test — addressed.

The test at Lines 92-100 correctly verifies that an older rollback does not overwrite a newer reassignment of the same agent. This covers the critical race condition path identified in previous reviews.


35-101: Test coverage is comprehensive for optimistic reassignment.

The suite covers:

  • Basic move operation
  • Rollback restoration
  • No-op when reassigning to same department
  • No-op when config is null
  • No-op when agent not found
  • Preservation of other agents
  • Concurrent rollback for different agents
  • Stale rollback for same agent

This provides good coverage for the optimisticReassignAgent store method.

web/src/pages/org/CommunicationEdge.stories.tsx (2)

41-41: Wrapper sizing uses Tailwind arbitrary values.

Lines 41 and 123 use w-[500px] which is a Tailwind arbitrary value rather than a standard spacing token. While this works, the coding guidelines prefer standard Tailwind spacing or density-aware tokens. However, for Storybook story wrappers with fixed preview dimensions, this is a reasonable exception since it's not application layout code.

Also applies to: 123-123


69-106: Story variants provide good visual coverage.

The LowVolume, MediumVolume, and HighVolume stories effectively demonstrate the edge's visual encoding of communication volume and frequency, allowing visual verification of stroke width and animation speed scaling.

web/src/hooks/useOrgChartData.ts (4)

35-35: Collision-safe edge IDs — addressed.

The edge ID format now uses comm:${encodeURIComponent(link.source)}::${encodeURIComponent(link.target)}, which prevents ID collisions when agent names contain hyphens or other special characters.


113-119: Orphan edge filtering — addressed.

Communication links are now filtered to only include edges where both source and target exist in the visible node set (visibleIds). This prevents orphan edges when messages reference deleted or archived agents.


30-45: buildCommunicationEdges helper is well-designed.

The function correctly:

  • Computes maxVolume with a floor of 1 to prevent division by zero in consumers
  • Uses collision-safe IDs with URI encoding
  • Satisfies the CommunicationEdgeData type constraint

102-125: View-mode branching logic is clear and correct.

The force/hierarchy branching properly:

  • Filters to agent/ceo nodes only in force mode
  • Removes parentId to prevent department grouping
  • Filters communication links to visible nodes
  • Falls back to dagre layout for hierarchy mode

The memoization dependencies correctly include viewMode and commLinks.

Comment on lines +64 to +75
const controller = new AbortController()
dispatch({ type: 'loading' })

async function fetchAll() {
try {
const allMessages: Array<{ sender: string; to: string }> = []
let offset = 0

for (let page = 0; page < MAX_PAGES; page++) {
if (controller.signal.aborted) return
const result = await listMessages({ offset, limit: PAGE_LIMIT })
for (const msg of result.data) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

❓ Verification inconclusive

Script executed:

#!/bin/bash
set -euo pipefail

echo "== listMessages endpoint wrapper =="
fd -i 'messages.ts' web/src/api/endpoints --exec sed -n '1,220p' {}

echo
echo "== shared API client helpers =="
fd -i 'client.ts' web/src/api --exec sed -n '1,220p' {}

echo
echo "== current listMessages call sites =="
rg -n --type=ts --type=tsx '\blistMessages\s*\(' web/src

Repository: Aureliolo/synthorg


Repository: Aureliolo/synthorg
Exit code: 2

stdout:

== listMessages endpoint wrapper ==
import { apiClient, unwrap, unwrapPaginated, type PaginatedResult } from '../client'
import type { ApiResponse, Channel, Message, PaginatedResponse, PaginationParams } from '../types'

export async function listMessages(params?: PaginationParams & { channel?: string }): Promise<PaginatedResult<Message>> {
  const response = await apiClient.get<PaginatedResponse<Message>>('/messages', { params })
  return unwrapPaginated<Message>(response)
}

export async function listChannels(): Promise<Channel[]> {
  const response = await apiClient.get<ApiResponse<Channel[]>>('/messages/channels')
  return unwrap(response)
}

== shared API client helpers ==
/**
 * Axios client with JWT interceptor and ApiResponse envelope unwrapping.
 */

import axios, { type AxiosError, type AxiosResponse } from 'axios'
import { IS_DEV_AUTH_BYPASS } from '@/utils/dev'
import type { ApiResponse, ErrorDetail, PaginatedResponse } from './types'

// Normalize: strip trailing slashes and any existing /api/v1 suffix
const RAW_BASE = import.meta.env.VITE_API_BASE_URL ?? ''
const BASE_URL = RAW_BASE.replace(/\/+$/, '').replace(/\/api\/v1\/?$/, '')

export const apiClient = axios.create({
  baseURL: `${BASE_URL}/api/v1`,
  headers: { 'Content-Type': 'application/json' },
  timeout: 30_000,
})

// ── Request interceptor: attach JWT ──────────────────────────
// SECURITY NOTE: JWT is stored in localStorage, which is accessible to any JS
// running in the page context (XSS risk). HttpOnly cookies would eliminate this
// attack surface but require backend cookie-based auth support plus CSRF
// protection. Mitigations in place: short-lived tokens with server-controlled
// expiry, automatic 401 cleanup, and expiry checks on page load (see auth
// store). Default token lifetime is 24 hours (configurable via jwt_expiry_minutes).
// CSP headers in security-headers.conf (included by nginx.conf) restrict
// script sources. If the deployment
// architecture changes to support cookie-based auth, migrate away from
// localStorage -- see docs/security.md for the full threat model.

apiClient.interceptors.request.use((config) => {
  const token = localStorage.getItem('auth_token')
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
})

// ── Response interceptor: 401 redirect + error passthrough ──

apiClient.interceptors.response.use(
  (response: AxiosResponse) => response,
  (error: AxiosError<{ error?: string; success?: boolean }>) => {
    if (error.response?.status === 401 && !IS_DEV_AUTH_BYPASS) {
      // Clear credentials synchronously to prevent stale-token retries
      localStorage.removeItem('auth_token')
      localStorage.removeItem('auth_token_expires_at')
      localStorage.removeItem('auth_must_change_password')
      // Sync Zustand auth state -- dynamic import avoids circular dependency.
      // We intentionally fire-and-forget: the rejection below reaches the
      // caller immediately, while auth state cleanup happens concurrently.
      import('@/stores/auth').then(({ useAuthStore }) => {
        useAuthStore.getState().logout()
      }).catch((importErr: unknown) => {
        console.error('Auth store cleanup failed during 401 handling:', importErr)
        // Fallback if store import fails: redirect directly
        if (window.location.pathname !== '/login' && window.location.pathname !== '/setup') {
          window.location.href = '/login'
        }
      })
    }
    return Promise.reject(error)
  },
)

/**
 * Error thrown when the API returns an error response.
 * Carries the structured RFC 9457 error detail when available.
 */
export class ApiRequestError extends Error {
  readonly errorDetail: ErrorDetail | null

  constructor(message: string, errorDetail: ErrorDetail | null = null) {
    super(message)
    this.name = 'ApiRequestError'
    this.errorDetail = errorDetail
  }
}

/**
 * Extract data from an ApiResponse envelope.
 * Throws if the response indicates an error.
 */
export function unwrap<T>(response: AxiosResponse<ApiResponse<T>>): T {
  const body = response.data
  if (!body || typeof body !== 'object') {
    throw new ApiRequestError('Unknown API error')
  }
  if (!body.success || body.data === null || body.data === undefined) {
    const detail = 'error_detail' in body ? (body.error_detail as ErrorDetail | null) : null
    throw new ApiRequestError(body.error ?? 'Unknown API error', detail)
  }
  return body.data
}

/**
 * Validate an ApiResponse envelope without extracting data.
 * Use for endpoints that return `ApiResponse<null>` (including 204 No Content).
 */
export function unwrapVoid(response: AxiosResponse<ApiResponse<null>>): void {
  // 204 No Content: empty body is expected and valid
  if (response.status === 204) return
  const body = response.data
  if (!body || typeof body !== 'object') {
    throw new ApiRequestError('Unknown API error')
  }
  if (!body.success) {
    const detail = 'error_detail' in body ? (body.error_detail as ErrorDetail | null) : null
    throw new ApiRequestError(body.error ?? 'Unknown API error', detail)
  }
}

/** Return type for paginated API calls. */
export interface PaginatedResult<T> {
  data: T[]
  total: number
  offset: number
  limit: number
}

/**
 * Extract data from a paginated response.
 * Validates the response structure to avoid cryptic TypeErrors.
 */
export function unwrapPaginated<T>(
  response: AxiosResponse<PaginatedResponse<T>>,
): PaginatedResult<T> {
  const body = response.data
  if (!body || typeof body !== 'object') {
    throw new ApiRequestError('Unknown API error')
  }
  if (!body.success) {
    const detail = 'error_detail' in body ? (body.error_detail as ErrorDetail | null) : null
    throw new ApiRequestError(body.error ?? 'Unknown API error', detail)
  }
  if (!body.pagination || !Array.isArray(body.data)) {
    throw new ApiRequestError('Unexpected API response format')
  }
  return {
    data: body.data,
    total: body.pagination.total,
    offset: body.pagination.offset,
    limit: body.pagination.limit,
  }
}

== current listMessages call sites ==

stderr:

unrecognized file type: tsx

AbortController signal is not forwarded to listMessages() — in-flight requests cannot be cancelled.

The controller.signal check at line 72 only prevents additional page fetches from starting; it does not cancel HTTP requests that are already in flight. The listMessages() function in web/src/api/endpoints/messages.ts accepts a params object but does not accept or forward an AbortSignal to the underlying axios client.

To fix this, modify listMessages() to accept signal as a config option and pass it through to apiClient.get():

export async function listMessages(
  params?: PaginationParams & { channel?: string },
  signal?: AbortSignal,
): Promise<PaginatedResult<Message>> {
  const response = await apiClient.get<PaginatedResponse<Message>>('/messages', { 
    params,
    signal,
  })
  return unwrapPaginated<Message>(response)
}

Then forward controller.signal at each call site.

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

In `@web/src/hooks/useCommunicationEdges.ts` around lines 64 - 75, The
AbortController created in useCommunicationEdges (variable controller) isn't
forwarded to cancel in-flight requests; update the listMessages function
signature (in web/src/api/endpoints/messages.ts) to accept an optional
AbortSignal (e.g., signal?: AbortSignal) and pass it into the axios call
(apiClient.get) via the request config so the HTTP request can be aborted, then
update all call sites (including the listMessages calls inside fetchAll in
useCommunicationEdges) to pass controller.signal as that argument.

@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 29, 2026 09:51 — with GitHub Actions Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

♻️ Duplicate comments (2)
web/src/hooks/useOrgChartData.ts (1)

24-25: ⚠️ Potential issue | 🟠 Major

Propagate truncated to the page instead of dropping it here.

useCommunicationEdges() now tells you when the 10-page cap was hit, but useOrgChartData() discards that signal. Once the cap is reached, force view still renders a partial communication graph as if it were complete.

🛠️ Suggested change
 export interface UseOrgChartDataReturn {
   nodes: Node[]
   edges: Edge[]
   loading: boolean
   error: string | null
   commLoading: boolean
   commError: string | null
+  commTruncated: boolean
   wsConnected: boolean
   wsSetupError: string | null
 }
…
-  const { links: commLinks, loading: commLoading, error: commError } = useCommunicationEdges(
+  const {
+    links: commLinks,
+    loading: commLoading,
+    error: commError,
+    truncated: commTruncated,
+  } = useCommunicationEdges(
     viewMode === 'force',
   )
…
   return {
     nodes,
     edges,
     loading,
     error,
     commLoading,
     commError,
+    commTruncated,
     wsConnected,
     wsSetupError,
   }

Also applies to: 97-99, 127-133

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

In `@web/src/hooks/useOrgChartData.ts` around lines 24 - 25, useOrgChartData is
swallowing the `truncated` flag returned by useCommunicationEdges so the UI
never knows the 10-page cap was hit; update useOrgChartData to accept/receive
the `truncated` value from useCommunicationEdges and include it in the hook's
returned state alongside commLoading and commError (i.e., propagate the
truncated boolean through the return object used by the page/component), and
update all return sites in useOrgChartData (the spots around
commLoading/commError and the other return blocks) so callers can render a
partial graph when truncated is true.
web/src/pages/OrgChartPage.tsx (1)

398-410: ⚠️ Potential issue | 🟠 Major

Don't announce a rollback that didn't happen.

rollback() now no-ops stale failures, but this catch still tells users and screen readers the agent was returned to originalDept. In the same-agent race, that message can be false if a newer reassignment already won.

🛠️ Suggested change
         .catch((err: unknown) => {
+          const currentDept = useCompanyStore.getState().config
+            ?.agents.find((a) => a.name === agentName)?.department
+          if (currentDept !== newDept) return
+
           rollback()
           const msg = err instanceof Error ? err.message : 'Unknown error'
           announce(`Failed to move ${agentName}, returned to ${originalDept}`)
           addToast({ variant: 'error', title: 'Reassignment failed', description: msg })
         })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/OrgChartPage.tsx` around lines 398 - 410, The catch currently
always announces that the agent was returned to originalDept even though
rollback() may be a no-op; change the error handling to check whether the
rollback actually changed state before announcing/alerting: call rollback() and
then read the current department from
useCompanyStore.getState().getAgent(agentName) (or check the boolean/return
value if optimisticReassignAgent/rollback returns one) and only call
announce/addToast about a return-to-originalDept if the agent's current
department equals originalDept; otherwise, report a generic failure for
agentName without claiming a rollback. Use the existing symbols
optimisticReassignAgent, rollback, updateAgent, announce, addToast, agentName,
newDept, and originalDept to locate and implement this conditional 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 `@docs/design/page-structure.md`:
- Line 332: The sentence calling the default view “read-only” conflicts with
later behavior (drag-drop reassignment) — update the phrasing around the default
view in page-structure.md to describe the org chart as an interactive
visualization with two view modes (hierarchy (dagre) and communication
(d3-force)), and clarify that the "Edit Organization" button enters a form-based
edit mode (sub-tabs: General, Agents, Departments) while hierarchy view
additionally supports drag-and-drop agent reassignment as a mutating action;
replace the word “read-only” and ensure the "Edit Organization" button and
hierarchy (dagre) / drag-drop reassignment semantics are described consistently.

In `@web/src/__tests__/pages/org/CommunicationEdge.test.tsx`:
- Around line 16-18: Add a test case to the CommunicationEdge test suite that
covers the reduced-motion branch: mock prefersReducedMotion to return true
(override the existing vi.mock for '@/lib/motion' or use vi.spyOn to force
prefersReducedMotion() === true), render the component in the same way as the
other tests, then assert that the rendered SVG path (the element rendered by
CommunicationEdge) does not receive any animation-related style (e.g., no
animation or transition properties). Update or add this assertion alongside the
existing tests that mock false so the reduced-motion path cannot regress (also
apply the same change pattern for the other similar tests around the 99-122
region).

In `@web/src/pages/org/CommunicationEdge.tsx`:
- Around line 28-34: The stylesheet injection currently performed by
ensureKeyframe (which mutates document.head and sets keyframeInjected using
KEYFRAME_NAME) must be moved out of render into a useInsertionEffect hook that
runs once; replace calls to ensureKeyframe from render with a noop and instead
call ensureKeyframe from a useInsertionEffect(() => { ensureKeyframe(); }, [])
so the keyframe is injected exactly once (empty dependency array) and not tied
to the reduced prop (reduced only controls whether animation CSS is applied).
Ensure ensureKeyframe still guards with keyframeInjected and typeof document
checks but remove any direct DOM mutation from render paths (including the other
call sites referenced around the 61-62 region).

In `@web/src/pages/OrgChartPage.tsx`:
- Around line 240-252: The code overwrites prevNodesRef.current with the next
target nodes before starting the interpolation, so mid-animation restarts use
the canceled target instead of the on-screen positions; fix by capturing the
displayed starting state into a local (e.g., const fromNodes =
prevNodesRef.current or compute on-screen positions) before assigning
prevNodesRef.current = nodes, then call dispatch({ type: 'start', edges,
fromNodes }) (or otherwise pass fromNodes to the start logic) and finally update
prevNodesRef.current = nodes so tweens restart from the actual displayed
positions rather than the already-overwritten targets.

---

Duplicate comments:
In `@web/src/hooks/useOrgChartData.ts`:
- Around line 24-25: useOrgChartData is swallowing the `truncated` flag returned
by useCommunicationEdges so the UI never knows the 10-page cap was hit; update
useOrgChartData to accept/receive the `truncated` value from
useCommunicationEdges and include it in the hook's returned state alongside
commLoading and commError (i.e., propagate the truncated boolean through the
return object used by the page/component), and update all return sites in
useOrgChartData (the spots around commLoading/commError and the other return
blocks) so callers can render a partial graph when truncated is true.

In `@web/src/pages/OrgChartPage.tsx`:
- Around line 398-410: The catch currently always announces that the agent was
returned to originalDept even though rollback() may be a no-op; change the error
handling to check whether the rollback actually changed state before
announcing/alerting: call rollback() and then read the current department from
useCompanyStore.getState().getAgent(agentName) (or check the boolean/return
value if optimisticReassignAgent/rollback returns one) and only call
announce/addToast about a return-to-originalDept if the agent's current
department equals originalDept; otherwise, report a generic failure for
agentName without claiming a rollback. Use the existing symbols
optimisticReassignAgent, rollback, updateAgent, announce, addToast, agentName,
newDept, and originalDept to locate and implement this conditional 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: bc59dc0b-c701-494b-a972-df2afe1eea40

📥 Commits

Reviewing files that changed from the base of the PR and between e30a01d and f8dc9bf.

📒 Files selected for processing (9)
  • docs/design/page-structure.md
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/__tests__/pages/org/reassign-agent.test.ts
  • web/src/api/endpoints/messages.ts
  • web/src/hooks/useCommunicationEdges.ts
  • web/src/hooks/useOrgChartData.ts
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.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). (6)
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Backend
  • GitHub Check: Build Web
  • GitHub Check: Build Sandbox
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always reuse existing components from web/src/components/ui/ before creating new ones. When a component is needed that is not in the existing inventory, place it in web/src/components/ui/ with a descriptive kebab-case filename, create a .stories.tsx file with all states, export props as a TypeScript interface, and use design tokens exclusively.

Files:

  • web/src/api/endpoints/messages.ts
  • web/src/__tests__/pages/org/reassign-agent.test.ts
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
  • web/src/pages/org/CommunicationEdge.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/hooks/useCommunicationEdges.ts
  • web/src/hooks/useOrgChartData.ts
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground, bg-card, text-accent, etc.) or CSS variables (var(--so-*)) for colors. Never hardcode hex values or rgba() with hardcoded values in .tsx/.ts files.
Use font-sans or font-mono classes for typography (mapping to Geist tokens). Never set fontFamily directly in component code.
Use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing for layout. Never hardcode pixel values for layout spacing.
Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders. Never hardcode shadow or border values.
TypeScript 6.0: remove deprecated baseUrl from tsconfig; paths entries are relative to the tsconfig directory. Remove explicit "esModuleInterop": true (always true in TS 6). List needed types explicitly (e.g., "types": ["vitest/globals"]). DOM.Iterable is merged into DOM. Use "bundler" or "nodenext" for moduleResolution.

Files:

  • web/src/api/endpoints/messages.ts
  • web/src/__tests__/pages/org/reassign-agent.test.ts
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
  • web/src/pages/org/CommunicationEdge.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/hooks/useCommunicationEdges.ts
  • web/src/hooks/useOrgChartData.ts
docs/design/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Before implementing any feature or planning any issue, always read the relevant docs/design/ page referenced in DESIGN_SPEC.md. The design spec is the starting point for architecture, data models, and behavior. If implementation deviates from the spec, alert the user and explain why—user decides whether to proceed or update the spec. When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.

Files:

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

📄 CodeRabbit inference engine (CLAUDE.md)

Documentation must follow Markdown conventions: use consistent heading levels, link to relevant sections, include code examples where appropriate, and keep content up-to-date with implementation changes.

Files:

  • docs/design/page-structure.md
web/src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Do NOT recreate status dots inline—use <StatusBadge>. Do NOT build card-with-header layouts from scratch—use <SectionCard>. Do NOT create metric displays with text-metric font-bold—use <MetricCard>. Do NOT render initials circles manually—use <Avatar>. Do NOT create complex (>8 line) JSX inside .map() — extract to a shared component.

Files:

  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/pages/org/CommunicationEdge.tsx
  • web/src/pages/OrgChartPage.tsx
🧠 Learnings (21)
📓 Common learnings
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/).
📚 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-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-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-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-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-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to docs/design/**/*.md : Before implementing any feature or planning any issue, always read the relevant `docs/design/` page referenced in `DESIGN_SPEC.md`. The design spec is the starting point for architecture, data models, and behavior. If implementation deviates from the spec, alert the user and explain why—user decides whether to proceed or update the spec. 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-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: Always read the relevant `docs/design/` page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file linking to the 7 design pages (index, agents, organization, communication, engine, memory, operations).

Applied to files:

  • docs/design/page-structure.md
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to **/*.md : Documentation must follow Markdown conventions: use consistent heading levels, link to relevant sections, include code examples where appropriate, and keep content up-to-date with implementation changes.

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
  • web/src/pages/OrgChartPage.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/**/*.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:

  • 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-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/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)

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: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.

Applied to files:

  • docs/design/page-structure.md
📚 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/reassign-agent.test.ts
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: ESM-only, no CJS. Use `storybook/test` (not `storybook/test`) and `storybook/actions` (not `storybook/addon-actions`). Use `defineMain` from `storybook/react-vite/node` and `definePreview` from `storybook/react-vite` with explicit `framework` field. Use `parameters.backgrounds.options` (object keyed by name) + `initialGlobals.backgrounds.value`. Use `parameters.a11y.test: 'error' | 'todo' | 'off'` for WCAG compliance.

Applied to files:

  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/components/ui/**/*.tsx : Each new shared component in `web/src/components/ui/` must have a corresponding `.stories.tsx` file with all states (default, hover, loading, error, empty). Export props as a TypeScript interface and use design tokens exclusively (no hardcoded colors, fonts, or spacing).

Applied to files:

  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
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 shadow or border values.

Applied to files:

  • web/src/pages/org/CommunicationEdge.tsx
  • web/src/pages/OrgChartPage.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.tsx : Do NOT recreate status dots inline—use `<StatusBadge>`. Do NOT build card-with-header layouts from scratch—use `<SectionCard>`. Do NOT create metric displays with `text-metric font-bold`—use `<MetricCard>`. Do NOT render initials circles manually—use `<Avatar>`. Do NOT create complex (>8 line) JSX inside `.map()` — extract to a shared component.

Applied to files:

  • web/src/pages/org/CommunicationEdge.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/hooks/useOrgChartData.ts
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, etc.) or CSS variables (`var(--so-*)`) for colors. Never hardcode hex values or rgba() with hardcoded values in `.tsx`/`.ts` files.

Applied to files:

  • web/src/pages/OrgChartPage.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind spacing for layout. Never hardcode pixel values for layout spacing.

Applied to files:

  • web/src/pages/OrgChartPage.tsx
🔇 Additional comments (5)
web/src/pages/OrgChartPage.tsx (1)

499-516: Verify only agent nodes are actually draggable.

nodesDraggable={viewMode === 'hierarchy'} enables React Flow dragging for the whole hierarchy view. If buildOrgTree() does not explicitly set draggable: false on department/CEO nodes, they can still move even though this flow only supports agent reassignment.

Run this to confirm only agents opt in to dragging; expected result is either per-node draggable flags or a global nodesDraggable={false} with agent-only overrides:

#!/bin/bash
set -euo pipefail

echo "== ReactFlow draggable config =="
sed -n '499,516p' web/src/pages/OrgChartPage.tsx

echo
echo "== explicit draggable flags in org-chart code =="
rg -n '\bdraggable\s*:' web/src/pages/org web/src/hooks/useOrgChartData.ts web/src/pages/OrgChartPage.tsx

echo
echo "== buildOrgTree node definitions =="
fd -i 'build-org-tree.ts' web/src/pages/org --exec sed -n '1,260p' {}
docs/design/page-structure.md (4)

31-36: Org Chart mode documentation is clear and implementation-aligned.

This section accurately captures hierarchy drag-drop behavior, communication-force layout semantics, and transition timing.


40-41: API and WS mapping for /org is correctly updated.

Good separation between REST-polled communication data and agents WebSocket events.


228-228: /org route notes now match the shipped interaction model.

This row is now consistent with the detailed Org Chart section.


277-279: Org Chart WS subscription split is well documented.

The hierarchy/communication distinction is explicit and actionable for QA and implementation.

Aureliolo and others added 6 commits March 29, 2026 12:11
Add force-directed layout engine using d3-force for the Communication view
toggle. Edge thickness encodes communication volume between agents, and
animated dashes encode message frequency. Smooth 400ms position interpolation
animates the transition between Hierarchy and Communication views.

New files:
- aggregate-messages.ts: pure function aggregating messages into links
- force-layout.ts: d3-force simulation computing node positions
- CommunicationEdge.tsx: custom React Flow edge with volume/frequency encoding
- useCommunicationEdges.ts: hook fetching and aggregating message data
- Storybook stories and comprehensive unit tests (including fast-check)

Modified:
- useOrgChartData: accepts viewMode, computes force layout when 'force'
- OrgChartPage: removes force-mode stub, adds CommunicationEdge type,
  animated view transition via useReducer, communication error banner

Closes #872

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add drag-and-drop agent reassignment between departments in the Org Chart
hierarchy view. Dragging an agent node over a department highlights the
drop zone with accent border and background (per design spec Section 3.4).
Dropping performs an optimistic update with automatic rollback on API failure.

New files:
- drop-target.ts: pure point-in-rect function for department hit detection
- Tests for drop target detection (including fast-check properties)
- Tests for optimistic reassignment (targeted rollback)

Modified:
- DepartmentGroupNode: isDropTarget visual state (accent border + bg)
- build-org-tree: added isDropTarget to DepartmentGroupData
- company store: optimisticReassignAgent with targeted rollback
- OrgChartPage: drag event handlers, ARIA live announcements,
  renderedNodes with drop target highlighting
- DepartmentGroupNode stories: DropTargetActive story

Closes #873

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pre-reviewed by 3 agents, 7 findings addressed:
- CommunicationEdge: respect prefers-reduced-motion for dash animation
- useCommunicationEdges: clear stale data on disable (useReducer pattern)
- OrgChartPage: add drag ghost opacity, scale lift, and settle animation CSS
- CLAUDE.md: add d3-force to web dashboard dependencies
- page-structure.md: document Communication view, drag-drop reassignment,
  update WS channel map, remove "deferred to v0.6+" for drag-drop
- CommunicationEdge test: mock @/lib/motion for jsdom compatibility

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…mini

Security: sanitize CSS keyframe names from dynamic edge IDs (#2), replace
hardcoded rgba() with design token (#1). Correctness: guard rollback against
stale overwrites (#6), filter orphan edges in force view (#4), use
collision-safe edge IDs (#3), restrict drag to agent nodes only (#5), fix
pagination offset to use data.length (#8), clamp dashDuration to bounds (#10).
Robustness: add AbortController for fetch cancellation (#9), move announce()
out of state updater (#7), add .catch() on fetchCompanyData (#29), log dept
health errors (#28). Docs: fix WS channel table (#11-12), classify PATCH
endpoint correctly (#13), update ux-guidelines version (#14), update
operations.md summary (#15). Tests: fix misleading test description (#16),
add stale rollback regression test (#24). Polish: extract magic numbers (#21-
22), add JSDoc on CommunicationEdgeData fields (#18-19), simplify Pick<>
type (#20), use Tailwind in stories (#17), add console.warn to empty catch
blocks (#25).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Single global keyframe for CommunicationEdge instead of per-instance <style>
injection. Forward AbortSignal through listMessages to cancel in-flight HTTP
requests. Add truncated flag to useCommunicationEdges when MAX_PAGES is hit.
Use node.measured dimensions with constant fallbacks for drag hit testing.
Move announce() fully outside state updater using dragOverDeptIdRef. Use CSS
variable for dragging opacity. Add console.warn to useOrgChartData catch
blocks. Strengthen null-config rollback test assertion. Update routing table
row and remove version qualifier from page-structure.md.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move keyframe injection from render to useInsertionEffect to avoid DOM
mutation during render. Add reduced-motion test case for CommunicationEdge.
Fix mid-animation restart to interpolate from on-screen positions (use
transition.displayNodes) instead of canceled target. Propagate truncated
flag from useCommunicationEdges through useOrgChartData to OrgChartPage
with partial-data warning banner. Check post-rollback state before
announcing return-to-department. Remove "read-only" wording from
page-structure.md merged section.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo force-pushed the feat/org-chart-features branch from f8dc9bf to 15069bc Compare March 29, 2026 10:16
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 29, 2026 10:17 — with GitHub Actions Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

♻️ Duplicate comments (2)
web/src/pages/OrgChartPage.tsx (1)

400-419: ⚠️ Potential issue | 🟠 Major

Optimistic rollback remains vulnerable to out-of-order responses.

If the same agent is dragged twice rapidly (A→B, then B→C before the first API call returns), an out-of-order failure can corrupt state. The existing guard in optimisticReassignAgent only checks if the department matches the optimistic value—it doesn't distinguish between different in-flight operations.

Consider either:

  1. Block re-drag while a request is in flight (simplest)
  2. Track an operation version/token per agent
🛡️ Option 1: Block re-drag during in-flight request
+  const pendingReassignRef = useRef<Set<string>>(new Set())
+
   const handleNodeDragStop = useCallback(
     (_event: ReactMouseEvent, node: Node) => {
       // ... existing early returns ...
+
+      if (pendingReassignRef.current.has(agentName)) {
+        announce(`${agentName} reassignment in progress`)
+        return
+      }
+      pendingReassignRef.current.add(agentName)

       const rollback = useCompanyStore.getState().optimisticReassignAgent(agentName, newDept)

       useCompanyStore.getState().updateAgent(agentName, { department: newDept })
         .then(() => {
+          pendingReassignRef.current.delete(agentName)
           // ... success handling ...
         })
         .catch((err: unknown) => {
+          pendingReassignRef.current.delete(agentName)
           rollback()
           // ... error handling ...
         })
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/OrgChartPage.tsx` around lines 400 - 419, The optimistic
rollback is vulnerable to out-of-order responses; fix by adding per-agent
in-flight tracking and token checks: modify the store function
optimisticReassignAgent to set an inFlight flag and return a unique operation
token (or incrementing version) for that agent, have the UI drag handler (where
updateAgent is called in OrgChartPage.tsx) refuse new drags when the agent is
inFlight (or attach the new token to the update call), and on the
updateAgent.catch rollback only apply the rollback if the stored token/version
for that agent still matches the token returned by optimisticReassignAgent; this
ensures out-of-order responses don't overwrite later successful changes.
web/src/pages/org/CommunicationEdge.stories.tsx (1)

57-67: ⚠️ Potential issue | 🟡 Minor

Add Storybook backgrounds configuration per project conventions.

The story metadata is missing parameters.backgrounds.options and initialGlobals.backgrounds.value required by the Storybook 10 setup.

♻️ Add backgrounds configuration
 const meta = {
   title: 'OrgChart/CommunicationEdge',
   component: Wrapper,
   tags: ['autodocs'],
   parameters: {
     a11y: { test: 'error' },
+    backgrounds: {
+      options: {
+        light: { name: 'Light', value: '#ffffff' },
+        dark: { name: 'Dark', value: '#1a1a1a' },
+      },
+    },
+  },
+  initialGlobals: {
+    backgrounds: { value: 'light' },
   },
 } satisfies Meta<typeof Wrapper>

As per coding guidelines web/src/**/*.stories.tsx: "Use parameters.backgrounds.options (object keyed by name) + initialGlobals.backgrounds.value."

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

In `@web/src/pages/org/CommunicationEdge.stories.tsx` around lines 57 - 67, The
story meta for CommunicationEdge (the `meta` object for component `Wrapper`) is
missing the Storybook backgrounds configuration; add
`parameters.backgrounds.options` as an object keyed by background names (e.g., {
default: { value: '#fff' }, dark: { value: '#000' } }) and set
`meta.initialGlobals.backgrounds.value` to one of those keys (e.g., 'default')
to match project conventions; keep the `satisfies Meta<typeof Wrapper>` typing
and ensure the keys used in `parameters.backgrounds.options` exactly match the
string assigned to `initialGlobals.backgrounds.value`.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/design/ux-guidelines.md`:
- Line 322: The document declares itself as v0.5.0 but the line "For task board
kanban columns (v0.5.0) and org chart hierarchy view (v0.5.1):" introduces
v0.5.1 behavior, causing ambiguity; update the document scope by either changing
the page-level version label to v0.5.1, or split the content into clearly
labeled sections (e.g., "v0.5.0 — Task board" and "v0.5.1 — Org chart") and
adjust the header/front-matter and any mentions of the phrase "For task board
kanban columns (v0.5.0) and org chart hierarchy view (v0.5.1):" so every version
reference consistently matches the page-level versioning scheme.

In `@web/src/hooks/useOrgChartData.ts`:
- Around line 12-13: The hook useOrgChartData.ts should not import
presentation-only types from CommunicationEdge.tsx and OrgChartToolbar; extract
the types ViewMode and CommunicationEdgeData into a new non-UI types module
(e.g., orgChartTypes.ts) and update imports: define and export the types from
that module, then change useOrgChartData.ts to import ViewMode and
CommunicationEdgeData from the new orgChartTypes module instead of
'@/pages/org/CommunicationEdge' and '@/pages/org/OrgChartToolbar'; also update
the two UI components (CommunicationEdge.tsx and OrgChartToolbar) to import the
types from the new module so all references remain consistent.
- Around line 31-34: In buildCommunicationEdges replace the Math.max(1,
...links.map((l) => l.volume)) pattern (which allocates an array and spreads it)
with a reducer that computes the maximum volume safely; iterate links with
Array.prototype.reduce to compute maxVolume = links.reduce((m, l) => Math.max(m,
l.volume), 1) (or equivalent) so you avoid creating the intermediate array while
preserving the minimum-of-1 behavior.

In `@web/src/pages/org/aggregate-messages.ts`:
- Around line 48-57: Guard against division by zero by checking timeWindowMs
before computing timeWindowHours and frequency: if timeWindowMs is 0 (or
extremely small), treat timeWindowHours as a non-zero fallback (e.g., 0) and
compute frequency accordingly to avoid Infinity. Update the block that defines
MS_PER_HOUR, timeWindowHours and the map over [...volumeMap.values()] —
specifically adjust how frequency is computed (or short-circuit when
timeWindowMs === 0) so frequency is a finite number (e.g., 0) instead of
Infinity before it’s consumed by components like CommunicationEdge.

In `@web/src/pages/org/force-layout.ts`:
- Around line 104-106: The call simulation.tick(TICK_COUNT) blocks the main
thread for large graphs; replace this synchronous 300-tick call with an async,
non-blocking approach by either (1) implementing an adaptive tick scheduler
(e.g., run ticks in small chunks using a CHUNK_SIZE loop and yield to the event
loop via setTimeout/requestIdleCallback until simulation.alpha() <
simulation.alphaMin() or total ticks reached) using a new helper like
runSimulationBudgeted(simulation, TICK_COUNT, CHUNK_SIZE) that returns a
Promise, or (2) offload the simulation to a Web Worker (create a force-layout
worker that runs simulation.tick(TICK_COUNT) inside the worker and posts node
positions back) and wire messaging in the existing force-layout logic; update
callers to await the Promise or handle worker messages instead of calling
simulation.tick(TICK_COUNT) directly.
- Around line 58-62: The node seeding uses logical OR which conflates explicit 0
with falsy values; update the simNodes mapping (the const simNodes: SimNode[] =
nodes.map(...) block) to use nullish coalescing (??) for x and y (e.g.,
n.position.x ?? 0 and n.position.y ?? 0) so only null/undefined fall back to 0
while preserving intentional 0 coordinates.

---

Duplicate comments:
In `@web/src/pages/org/CommunicationEdge.stories.tsx`:
- Around line 57-67: The story meta for CommunicationEdge (the `meta` object for
component `Wrapper`) is missing the Storybook backgrounds configuration; add
`parameters.backgrounds.options` as an object keyed by background names (e.g., {
default: { value: '#fff' }, dark: { value: '#000' } }) and set
`meta.initialGlobals.backgrounds.value` to one of those keys (e.g., 'default')
to match project conventions; keep the `satisfies Meta<typeof Wrapper>` typing
and ensure the keys used in `parameters.backgrounds.options` exactly match the
string assigned to `initialGlobals.backgrounds.value`.

In `@web/src/pages/OrgChartPage.tsx`:
- Around line 400-419: The optimistic rollback is vulnerable to out-of-order
responses; fix by adding per-agent in-flight tracking and token checks: modify
the store function optimisticReassignAgent to set an inFlight flag and return a
unique operation token (or incrementing version) for that agent, have the UI
drag handler (where updateAgent is called in OrgChartPage.tsx) refuse new drags
when the agent is inFlight (or attach the new token to the update call), and on
the updateAgent.catch rollback only apply the rollback if the stored
token/version for that agent still matches the token returned by
optimisticReassignAgent; this ensures out-of-order responses don't overwrite
later successful changes.
🪄 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: ee915c56-9d23-4fe0-b1b2-bb36bbe138c7

📥 Commits

Reviewing files that changed from the base of the PR and between f8dc9bf and 15069bc.

⛔ Files ignored due to path filters (1)
  • web/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (25)
  • CLAUDE.md
  • docs/design/operations.md
  • docs/design/page-structure.md
  • docs/design/ux-guidelines.md
  • web/package.json
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/__tests__/pages/org/OrgChartPage.test.tsx
  • web/src/__tests__/pages/org/aggregate-messages.test.ts
  • web/src/__tests__/pages/org/drop-target.test.ts
  • web/src/__tests__/pages/org/force-layout.test.ts
  • web/src/__tests__/pages/org/reassign-agent.test.ts
  • web/src/api/endpoints/messages.ts
  • web/src/hooks/useCommunicationEdges.ts
  • web/src/hooks/useOrgChartData.ts
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
  • web/src/pages/org/CommunicationEdge.tsx
  • web/src/pages/org/DepartmentGroupNode.stories.tsx
  • web/src/pages/org/DepartmentGroupNode.tsx
  • web/src/pages/org/aggregate-messages.ts
  • web/src/pages/org/build-org-tree.ts
  • web/src/pages/org/drop-target.ts
  • web/src/pages/org/force-layout.ts
  • web/src/stores/company.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). (6)
  • GitHub Check: Dashboard Test
  • GitHub Check: Build Web
  • GitHub Check: Build Sandbox
  • GitHub Check: Build Backend
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (6)
docs/design/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Before implementing any feature or planning any issue, always read the relevant docs/design/ page referenced in DESIGN_SPEC.md. The design spec is the starting point for architecture, data models, and behavior. If implementation deviates from the spec, alert the user and explain why—user decides whether to proceed or update the spec. When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.

Files:

  • docs/design/ux-guidelines.md
  • docs/design/operations.md
  • docs/design/page-structure.md
**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Documentation must follow Markdown conventions: use consistent heading levels, link to relevant sections, include code examples where appropriate, and keep content up-to-date with implementation changes.

Files:

  • docs/design/ux-guidelines.md
  • CLAUDE.md
  • docs/design/operations.md
  • docs/design/page-structure.md
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always reuse existing components from web/src/components/ui/ before creating new ones. When a component is needed that is not in the existing inventory, place it in web/src/components/ui/ with a descriptive kebab-case filename, create a .stories.tsx file with all states, export props as a TypeScript interface, and use design tokens exclusively.

Files:

  • web/src/pages/org/build-org-tree.ts
  • web/src/pages/org/DepartmentGroupNode.stories.tsx
  • web/src/pages/org/DepartmentGroupNode.tsx
  • web/src/api/endpoints/messages.ts
  • web/src/__tests__/pages/org/drop-target.test.ts
  • web/src/__tests__/pages/org/OrgChartPage.test.tsx
  • web/src/__tests__/pages/org/aggregate-messages.test.ts
  • web/src/__tests__/pages/org/force-layout.test.ts
  • web/src/pages/org/drop-target.ts
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/stores/company.ts
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
  • web/src/pages/OrgChartPage.tsx
  • web/src/hooks/useCommunicationEdges.ts
  • web/src/__tests__/pages/org/reassign-agent.test.ts
  • web/src/pages/org/CommunicationEdge.stories.tsx
  • web/src/pages/org/force-layout.ts
  • web/src/pages/org/aggregate-messages.ts
  • web/src/hooks/useOrgChartData.ts
  • web/src/pages/org/CommunicationEdge.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground, bg-card, text-accent, etc.) or CSS variables (var(--so-*)) for colors. Never hardcode hex values or rgba() with hardcoded values in .tsx/.ts files.
Use font-sans or font-mono classes for typography (mapping to Geist tokens). Never set fontFamily directly in component code.
Use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing for layout. Never hardcode pixel values for layout spacing.
Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders. Never hardcode shadow or border values.
TypeScript 6.0: remove deprecated baseUrl from tsconfig; paths entries are relative to the tsconfig directory. Remove explicit "esModuleInterop": true (always true in TS 6). List needed types explicitly (e.g., "types": ["vitest/globals"]). DOM.Iterable is merged into DOM. Use "bundler" or "nodenext" for moduleResolution.

Files:

  • web/src/pages/org/build-org-tree.ts
  • web/src/pages/org/DepartmentGroupNode.stories.tsx
  • web/src/pages/org/DepartmentGroupNode.tsx
  • web/src/api/endpoints/messages.ts
  • web/src/__tests__/pages/org/drop-target.test.ts
  • web/src/__tests__/pages/org/OrgChartPage.test.tsx
  • web/src/__tests__/pages/org/aggregate-messages.test.ts
  • web/src/__tests__/pages/org/force-layout.test.ts
  • web/src/pages/org/drop-target.ts
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/stores/company.ts
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
  • web/src/pages/OrgChartPage.tsx
  • web/src/hooks/useCommunicationEdges.ts
  • web/src/__tests__/pages/org/reassign-agent.test.ts
  • web/src/pages/org/CommunicationEdge.stories.tsx
  • web/src/pages/org/force-layout.ts
  • web/src/pages/org/aggregate-messages.ts
  • web/src/hooks/useOrgChartData.ts
  • web/src/pages/org/CommunicationEdge.tsx
web/src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Do NOT recreate status dots inline—use <StatusBadge>. Do NOT build card-with-header layouts from scratch—use <SectionCard>. Do NOT create metric displays with text-metric font-bold—use <MetricCard>. Do NOT render initials circles manually—use <Avatar>. Do NOT create complex (>8 line) JSX inside .map() — extract to a shared component.

Files:

  • web/src/pages/org/DepartmentGroupNode.stories.tsx
  • web/src/pages/org/DepartmentGroupNode.tsx
  • web/src/__tests__/pages/org/OrgChartPage.test.tsx
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
  • web/src/pages/org/CommunicationEdge.tsx
web/src/**/*.stories.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Storybook 10: ESM-only, no CJS. Use storybook/test (not @storybook/test) and storybook/actions (not @storybook/addon-actions). Use defineMain from @storybook/react-vite/node and definePreview from @storybook/react-vite with explicit framework field. Use parameters.backgrounds.options (object keyed by name) + initialGlobals.backgrounds.value. Use parameters.a11y.test: 'error' | 'todo' | 'off' for WCAG compliance.

Files:

  • web/src/pages/org/DepartmentGroupNode.stories.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
🧠 Learnings (33)
📓 Common learnings
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/).
📚 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 web/package.json : Web dashboard Node.js 20+; dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, ESLint, vue-tsc)

Applied to files:

  • web/package.json
  • CLAUDE.md
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.{ts,tsx} : TypeScript 6.0: remove deprecated `baseUrl` from tsconfig; `paths` entries are relative to the tsconfig directory. Remove explicit `"esModuleInterop": true` (always true in TS 6). List needed types explicitly (e.g., `"types": ["vitest/globals"]`). DOM.Iterable is merged into DOM. Use `"bundler"` or `"nodenext"` for `moduleResolution`.

Applied to files:

  • web/package.json
  • web/src/__tests__/pages/org/force-layout.test.ts
📚 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 web/** : Web dashboard: Node.js 20+, dependencies in web/package.json (Vue 3, PrimeVue, Tailwind CSS, Pinia, VueFlow, ECharts, Axios, vue-draggable-plus, Vitest, fast-check, ESLint, vue-tsc).

Applied to files:

  • web/package.json
  • CLAUDE.md
📚 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 pyproject.toml : Dependencies: all versions use == in pyproject.toml. Groups: test (pytest + plugins, hypothesis), dev (includes test + ruff, mypy, pre-commit, commitizen, pip-audit). Required: mem0ai (Mem0 memory backend — the default and currently only backend). Install: uv sync installs everything (dev group is default).

Applied to files:

  • CLAUDE.md
📚 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:

  • CLAUDE.md
📚 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:

  • CLAUDE.md
📚 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 docker/{Dockerfile*,compose.yml} : Docker: Backend uses 3-stage build (builder → setup → distroless runtime), Chainguard Python, non-root (UID 65532), CIS-hardened. Web uses nginxinc/nginx-unprivileged, Vue 3 SPA with PrimeVue + Tailwind CSS, SPA routing, API/WebSocket proxy to backend.

Applied to files:

  • CLAUDE.md
📚 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:

  • CLAUDE.md
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to pyproject.toml : Pin all dependency versions using `==` in `pyproject.toml`. Organize dependencies into groups: `test` (pytest + plugins, hypothesis), `dev` (includes test + ruff, mypy, pre-commit, commitizen, pip-audit). Include `mem0ai`, `cryptography`, and `faker` as required dependencies.

Applied to files:

  • CLAUDE.md
📚 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:

  • CLAUDE.md
📚 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:

  • CLAUDE.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/operations.md
  • 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/operations.md
  • 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/operations.md
  • docs/design/page-structure.md
  • web/src/pages/OrgChartPage.tsx
📚 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/operations.md
  • 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/operations.md
  • docs/design/page-structure.md
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/components/ui/**/*.tsx : Each new shared component in `web/src/components/ui/` must have a corresponding `.stories.tsx` file with all states (default, hover, loading, error, empty). Export props as a TypeScript interface and use design tokens exclusively (no hardcoded colors, fonts, or spacing).

Applied to files:

  • web/src/pages/org/DepartmentGroupNode.stories.tsx
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.stories.tsx : Storybook 10: ESM-only, no CJS. Use `storybook/test` (not `storybook/test`) and `storybook/actions` (not `storybook/addon-actions`). Use `defineMain` from `storybook/react-vite/node` and `definePreview` from `storybook/react-vite` with explicit `framework` field. Use `parameters.backgrounds.options` (object keyed by name) + `initialGlobals.backgrounds.value`. Use `parameters.a11y.test: 'error' | 'todo' | 'off'` for WCAG compliance.

Applied to files:

  • web/src/pages/org/DepartmentGroupNode.stories.tsx
  • web/src/__tests__/pages/org/OrgChartPage.test.tsx
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
  • web/src/pages/org/CommunicationEdge.stories.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
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 shadow or border values.

Applied to files:

  • web/src/pages/org/DepartmentGroupNode.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
  • web/src/pages/org/CommunicationEdge.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use Tailwind semantic classes (`text-foreground`, `bg-card`, `text-accent`, etc.) or CSS variables (`var(--so-*)`) for colors. Never hardcode hex values or rgba() with hardcoded values in `.tsx`/`.ts` files.

Applied to files:

  • web/src/pages/org/DepartmentGroupNode.tsx
  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
📚 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/drop-target.test.ts
  • web/src/__tests__/pages/org/OrgChartPage.test.tsx
  • web/src/__tests__/pages/org/aggregate-messages.test.ts
  • web/src/__tests__/pages/org/force-layout.test.ts
  • web/src/__tests__/pages/org/CommunicationEdge.test.tsx
  • web/src/__tests__/hooks/useCommunicationEdges.test.ts
  • web/src/__tests__/pages/org/reassign-agent.test.ts
📚 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-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: Always read the relevant `docs/design/` page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file linking to the 7 design pages (index, 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: Documentation source in `docs/` (Markdown, built with Zensical). Design spec in `docs/design/` (7 pages: index, agents, organization, communication, engine, memory, operations). Architecture in `docs/architecture/` (overview, tech-stack, decision log). Roadmap in `docs/roadmap/`. Security in `docs/security.md`. Licensing in `docs/licensing.md`. Reference in `docs/reference/`. REST API reference in `docs/rest-api.md`. Library reference in `docs/api/` (auto-generated from docstrings). Custom templates in `docs/overrides/`. Config in `mkdocs.yml`.

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-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to docs/design/**/*.md : Before implementing any feature or planning any issue, always read the relevant `docs/design/` page referenced in `DESIGN_SPEC.md`. The design spec is the starting point for architecture, data models, and behavior. If implementation deviates from the spec, alert the user and explain why—user decides whether to proceed or update the spec. 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-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to **/*.md : Documentation must follow Markdown conventions: use consistent heading levels, link to relevant sections, include code examples where appropriate, and keep content up-to-date with implementation changes.

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 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:

  • 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: Applies to src/synthorg/hr/**/*.py : HR package (hr/): hiring, firing, onboarding, offboarding, agent registry, performance tracking (task metrics, collaboration scoring, LLM calibration, collaboration overrides, trend detection), promotion/demotion (criteria evaluation, approval strategies, model mapping)

Applied to files:

  • docs/design/page-structure.md
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.tsx : Do NOT recreate status dots inline—use `<StatusBadge>`. Do NOT build card-with-header layouts from scratch—use `<SectionCard>`. Do NOT create metric displays with `text-metric font-bold`—use `<MetricCard>`. Do NOT render initials circles manually—use `<Avatar>`. Do NOT create complex (>8 line) JSX inside `.map()` — extract to a shared component.

Applied to files:

  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
  • web/src/pages/org/CommunicationEdge.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.{ts,tsx} : Use density-aware tokens (`p-card`, `gap-section-gap`, `gap-grid-gap`) or standard Tailwind spacing for layout. Never hardcode pixel values for layout spacing.

Applied to files:

  • web/src/pages/OrgChartPage.tsx
  • web/src/pages/org/CommunicationEdge.stories.tsx
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to **/*.{ts,tsx} : Always reuse existing components from `web/src/components/ui/` before creating new ones. When a component is needed that is not in the existing inventory, place it in `web/src/components/ui/` with a descriptive kebab-case filename, create a `.stories.tsx` file with all states, export props as a TypeScript interface, and use design tokens exclusively.

Applied to files:

  • web/src/pages/org/CommunicationEdge.stories.tsx
🔇 Additional comments (31)
web/src/pages/org/build-org-tree.ts (1)

31-31: DepartmentGroupData extension is backward-compatible.

Making isDropTarget optional is the right shape for incremental adoption by drag/highlight flows without breaking existing tree consumers.

web/src/pages/org/drop-target.ts (1)

25-48: Hit-testing implementation looks solid.

Inclusive boundary checks plus smallest-area tie-break for overlaps is a good fit for deterministic drop-target selection.

web/src/__tests__/pages/org/drop-target.test.ts (1)

71-111: Great property-based coverage for geometric invariants.

The properties around “inside is always found” and “far outside returns null” complement the example tests well.

web/src/pages/org/DepartmentGroupNode.tsx (1)

23-26: Drop-target visual state is implemented cleanly.

Line 25 cleanly overrides health tint with semantic accent styles during drag-over, and Line 23 keeps the transition smooth.

web/src/pages/org/DepartmentGroupNode.stories.tsx (1)

89-102: Nice addition of explicit drop-target story state.

DropTargetActive gives clear Storybook coverage for the new drag-over highlight path.

web/src/stores/company.ts (2)

83-86: Good partial-failure handling for health fan-out.

Line 83-86 keeps the batch resilient by degrading failed departments to null instead of failing the entire refresh path.


328-350: Rollback guard correctly prevents stale overwrite.

The Line 344 condition (currentAgent.department !== newDepartment) is the right safety check to avoid older rollback closures undoing newer same-agent moves.

web/src/__tests__/pages/org/reassign-agent.test.ts (1)

94-102: Excellent stale-rollback regression coverage.

This case captures the key ordering hazard for same-agent reassignment and verifies the guard logic in the store.

web/package.json (1)

41-41: LGTM!

The d3-force dependency and its TypeScript types are correctly added to support the new force-directed communication view. The version ranges (^3.0.0 and ^3.0.10) are appropriate and compatible.

Also applies to: 67-67

CLAUDE.md (1)

423-423: LGTM!

Documentation correctly updated to reflect the new d3-force dependency addition, maintaining alphabetical ordering in the dependency list.

docs/design/operations.md (1)

1177-1177: LGTM!

Design spec correctly updated to document the new Org Chart capabilities: dual view modes (hierarchy + communication graph) and drag-drop agent reassignment. This aligns with the PR objectives for issues #872 and #873.

web/src/__tests__/pages/org/OrgChartPage.test.tsx (3)

26-27: Good practice: stable reference for empty edges.

Using a module-level EMPTY_EDGES constant prevents infinite re-render loops that would occur if returning a new empty array on each hook call. This is a correct fix for React's reference equality checks in useEffect dependencies.


31-33: LGTM!

The new communication-related mock fields (commLoading, commError, commTruncated) correctly match the UseOrgChartDataReturn interface definition from useOrgChartData.ts.

Also applies to: 43-45


159-164: LGTM!

Good coverage for the communication error banner. The test correctly sets mockCommError and verifies the error message is displayed to users.

web/src/api/endpoints/messages.ts (1)

4-7: LGTM!

Clean implementation of AbortSignal support:

  • Signal is correctly destructured and excluded from query params
  • Backward compatible with existing callers (signal is optional)
  • Enables proper request cancellation for the new useCommunicationEdges hook
web/src/__tests__/pages/org/aggregate-messages.test.ts (1)

1-134: LGTM!

Comprehensive test suite for aggregateMessages with excellent coverage:

  • Edge cases (empty input, self-messages, zero time window)
  • Core functionality (bidirectional merge, alphabetical normalization, frequency calculation)
  • Output ordering (descending by volume)
  • Property-based tests using fast-check for invariant verification

The zero time window test description has been corrected per prior review feedback.

web/src/__tests__/pages/org/CommunicationEdge.test.tsx (2)

116-125: Good: reduced-motion accessibility test added.

This test addresses the prior review feedback by explicitly testing the prefers-reduced-motion branch. The mock correctly returns true and the assertion verifies no animation style is applied.


1-139: LGTM!

Well-structured test suite for CommunicationEdge covering:

  • SVG path rendering with correct test IDs
  • Design token usage (var(--color-accent))
  • Volume-based stroke width scaling
  • Animation styling with proper dash array
  • Accessibility (reduced-motion support)
  • Keyframe stylesheet injection

The mock setup cleanly isolates the component for unit testing.

web/src/__tests__/pages/org/force-layout.test.ts (1)

1-140: LGTM!

Excellent test coverage for computeForceLayout:

  • Edge cases (empty input, single node, missing link targets)
  • Core invariants (finite positions, node separation, data preservation)
  • Determinism verification (important for layout stability)
  • Force simulation behavior (volume affects node proximity, repulsion prevents overlap)
  • Container-aware centering with width/height options

The helper functions (makeNode, makeLink) keep tests readable while reducing duplication.

web/src/pages/org/aggregate-messages.ts (1)

26-46: LGTM!

Clean aggregation logic with proper canonical key ordering to deduplicate bidirectional pairs. Self-message exclusion and map-based accumulation are correctly implemented.

web/src/__tests__/hooks/useCommunicationEdges.test.ts (1)

1-115: LGTM!

Comprehensive test coverage for the hook's key behaviors: disabled state, message aggregation, pagination across pages, error handling, and empty results. Mock setup correctly precedes the import.

docs/design/page-structure.md (1)

31-42: LGTM!

Documentation accurately reflects the new dual-mode Org Chart: Hierarchy view with drag-drop reassignment and Communication view with d3-force layout. API endpoints and WS channels are correctly updated.

web/src/pages/org/CommunicationEdge.tsx (2)

59-59: Reduced motion preference is not reactive.

prefersReducedMotion() captures a point-in-time snapshot. If a user changes their OS preference mid-session, the animation won't update until component remount. This is documented behavior (see web/src/lib/motion.ts:273-288) and acceptable for this use case, but worth noting for future enhancements.


64-75: LGTM!

Style computation is well-structured: stroke width/opacity scale with volume ratio, animation duration inversely scales with frequency, and the clamping logic correctly bounds values. The useMemo dependency array is complete.

web/src/pages/OrgChartPage.tsx (2)

232-280: LGTM!

The transition animation correctly handles mid-animation restarts by using transition.displayNodes as the starting position. The cleanup cancels in-flight frames, and reduced motion is respected with an instant snap.


496-512: LGTM!

CSS styles correctly use design tokens: var(--so-opacity-dragging, 0.6) and var(--so-shadow-card-hover). The prefers-reduced-motion media query disables transitions appropriately.

web/src/hooks/useCommunicationEdges.ts (1)

62-102: LGTM!

The hook correctly:

  • Passes signal to listMessages for request cancellation
  • Advances offset by actual data.length (handles sparse pages)
  • Sets truncated flag when pagination limit is reached with more data remaining
  • Guards against dispatching state updates after abort
web/src/pages/org/CommunicationEdge.stories.tsx (1)

69-106: LGTM!

Stories cover the key visual states: low/medium/high volume edges demonstrating stroke width and animation speed variations. The data values exercise the full range of the component's rendering logic.

web/src/hooks/useOrgChartData.ts (3)

67-74: Nice self-healing fetch path.

Starting polling even after the initial company fetch fails keeps the page recoverable on transient backend errors, and the added warnings preserve enough context to debug it.


98-100: Good communication-fetch gating.

Tying useCommunicationEdges to viewMode === 'force' avoids paging the messages API while hierarchy mode is active and clears stale communication state cleanly when toggling back.


110-112: No extent clearing needed—extent is not set on agent/CEO nodes.

The code at line 112 is correct. Agent/CEO nodes in buildOrgTree are created with only parentId as a grouping property; no extent property is set anywhere in the codebase. Clearing only parentId is sufficient to detach nodes for force mode layout.

			> Likely an incorrect or invalid review comment.

Comment on lines +12 to +13
import type { CommunicationEdgeData } from '@/pages/org/CommunicationEdge'
import type { ViewMode } from '@/pages/org/OrgChartToolbar'
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Move these org-chart types out of .tsx view modules.

useOrgChartData is a reusable hook, but it now depends on CommunicationEdge.tsx and OrgChartToolbar just to import types. Extract ViewMode and CommunicationEdgeData into a non-UI org-chart types module so the hook layer does not depend on presentation files.

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

In `@web/src/hooks/useOrgChartData.ts` around lines 12 - 13, The hook
useOrgChartData.ts should not import presentation-only types from
CommunicationEdge.tsx and OrgChartToolbar; extract the types ViewMode and
CommunicationEdgeData into a new non-UI types module (e.g., orgChartTypes.ts)
and update imports: define and export the types from that module, then change
useOrgChartData.ts to import ViewMode and CommunicationEdgeData from the new
orgChartTypes module instead of '@/pages/org/CommunicationEdge' and
'@/pages/org/OrgChartToolbar'; also update the two UI components
(CommunicationEdge.tsx and OrgChartToolbar) to import the types from the new
module so all references remain consistent.

Comment on lines +31 to +34
function buildCommunicationEdges(
links: CommunicationLink[],
): Edge[] {
const maxVolume = Math.max(1, ...links.map((l) => l.volume))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "useOrgChartData.ts" -type f

Repository: Aureliolo/synthorg

Length of output: 96


🏁 Script executed:

cat -n web/src/hooks/useOrgChartData.ts | head -50

Repository: Aureliolo/synthorg

Length of output: 2270


🏁 Script executed:

rg "buildCommunicationEdges" --context 10 web/src/

Repository: Aureliolo/synthorg

Length of output: 3054


🏁 Script executed:

rg "commTruncated|truncate" web/src/ --context 3

Repository: Aureliolo/synthorg

Length of output: 23322


🏁 Script executed:

rg "CommunicationLink" web/src/pages/org/aggregate-messages.ts -A 5 | head -30

Repository: Aureliolo/synthorg

Length of output: 498


🏁 Script executed:

rg "MAX\|LIMIT\|BATCH" web/src/pages/org/ --context 2

Repository: Aureliolo/synthorg

Length of output: 44


🏁 Script executed:

rg "MAX_PAGES" web/src/hooks/useCommunicationEdges.ts -B 2 -A 2

Repository: Aureliolo/synthorg

Length of output: 1018


🏁 Script executed:

rg "PAGE_LIMIT" web/src/hooks/useCommunicationEdges.ts -B 2 -A 2

Repository: Aureliolo/synthorg

Length of output: 485


Avoid spread operator in Math.max for potentially large arrays.

Math.max(1, ...links.map(...)) creates an intermediate array and expands all values as arguments. Use a reducer pattern instead to avoid unnecessary allocations:

Suggested fix
-  const maxVolume = Math.max(1, ...links.map((l) => l.volume))
+  const maxVolume = links.reduce((max, l) => Math.max(max, l.volume), 1)
📝 Committable suggestion

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

Suggested change
function buildCommunicationEdges(
links: CommunicationLink[],
): Edge[] {
const maxVolume = Math.max(1, ...links.map((l) => l.volume))
function buildCommunicationEdges(
links: CommunicationLink[],
): Edge[] {
const maxVolume = links.reduce((max, l) => Math.max(max, l.volume), 1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/hooks/useOrgChartData.ts` around lines 31 - 34, In
buildCommunicationEdges replace the Math.max(1, ...links.map((l) => l.volume))
pattern (which allocates an array and spreads it) with a reducer that computes
the maximum volume safely; iterate links with Array.prototype.reduce to compute
maxVolume = links.reduce((m, l) => Math.max(m, l.volume), 1) (or equivalent) so
you avoid creating the intermediate array while preserving the minimum-of-1
behavior.

Comment on lines +48 to +57
const MS_PER_HOUR = 3_600_000
const timeWindowHours = timeWindowMs / MS_PER_HOUR

return [...volumeMap.values()]
.map(({ source, target, volume }) => ({
source,
target,
volume,
frequency: volume / timeWindowHours,
}))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Guard against division by zero when timeWindowMs is zero.

If timeWindowMs is 0, then timeWindowHours becomes 0, causing frequency to be Infinity. This can propagate unexpected values to downstream consumers (e.g., animation duration calculations in CommunicationEdge).

🛡️ Proposed fix
   const MS_PER_HOUR = 3_600_000
-  const timeWindowHours = timeWindowMs / MS_PER_HOUR
+  const timeWindowHours = Math.max(timeWindowMs / MS_PER_HOUR, 1 / 60) // floor at 1 minute
📝 Committable suggestion

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

Suggested change
const MS_PER_HOUR = 3_600_000
const timeWindowHours = timeWindowMs / MS_PER_HOUR
return [...volumeMap.values()]
.map(({ source, target, volume }) => ({
source,
target,
volume,
frequency: volume / timeWindowHours,
}))
const MS_PER_HOUR = 3_600_000
const timeWindowHours = Math.max(timeWindowMs / MS_PER_HOUR, 1 / 60) // floor at 1 minute
return [...volumeMap.values()]
.map(({ source, target, volume }) => ({
source,
target,
volume,
frequency: volume / timeWindowHours,
}))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/org/aggregate-messages.ts` around lines 48 - 57, Guard against
division by zero by checking timeWindowMs before computing timeWindowHours and
frequency: if timeWindowMs is 0 (or extremely small), treat timeWindowHours as a
non-zero fallback (e.g., 0) and compute frequency accordingly to avoid Infinity.
Update the block that defines MS_PER_HOUR, timeWindowHours and the map over
[...volumeMap.values()] — specifically adjust how frequency is computed (or
short-circuit when timeWindowMs === 0) so frequency is a finite number (e.g., 0)
instead of Infinity before it’s consumed by components like CommunicationEdge.

Comment on lines +104 to +106
// Run simulation to convergence synchronously
simulation.tick(TICK_COUNT)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Synchronous 300-tick simulation may block the main thread for large graphs.

This is acceptable for initial implementation, but consider adaptive tick budgeting or web worker offloading for organizations with 50+ agents.

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

In `@web/src/pages/org/force-layout.ts` around lines 104 - 106, The call
simulation.tick(TICK_COUNT) blocks the main thread for large graphs; replace
this synchronous 300-tick call with an async, non-blocking approach by either
(1) implementing an adaptive tick scheduler (e.g., run ticks in small chunks
using a CHUNK_SIZE loop and yield to the event loop via
setTimeout/requestIdleCallback until simulation.alpha() < simulation.alphaMin()
or total ticks reached) using a new helper like
runSimulationBudgeted(simulation, TICK_COUNT, CHUNK_SIZE) that returns a
Promise, or (2) offload the simulation to a Web Worker (create a force-layout
worker that runs simulation.tick(TICK_COUNT) inside the worker and posts node
positions back) and wire messaging in the existing force-layout logic; update
callers to await the Promise or handle worker messages instead of calling
simulation.tick(TICK_COUNT) directly.

… from ux-guidelines

Use ?? instead of || for d3-force node seeding so explicit x:0/y:0
coordinates are preserved. Remove version qualifiers from drag-drop
spec line in ux-guidelines.md to avoid frontmatter version mismatch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 29, 2026 11:06 — with GitHub Actions Inactive
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
web/src/pages/org/force-layout.ts (1)

104-106: ⚠️ Potential issue | 🟠 Major

Synchronous fixed-tick simulation still risks main-thread blocking on larger orgs.

simulation.tick(TICK_COUNT) runs in one synchronous block and can stall interaction as node/link counts grow.

#!/bin/bash
# Verify synchronous simulation usage and where it's invoked from UI data hooks.
# Expected: identify direct blocking tick calls on request/render paths.

rg -n -C3 'simulation\.tick\(' --type=ts
rg -n -C3 '\bcomputeForceLayout\s*\(' --type=ts
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/org/force-layout.ts` around lines 104 - 106, The synchronous
call simulation.tick(TICK_COUNT) in computeForceLayout blocks the main thread
for large graphs; change it to an incremental, non-blocking runner that
processes small batches of ticks (e.g., 1–10 ticks per frame) using
requestAnimationFrame or setTimeout and returns a Promise so callers can await
progressive layout updates; update computeForceLayout to use that async runner
(or offload ticks to a web worker) and ensure UI data hooks await or subscribe
to incremental updates rather than calling simulation.tick(...) directly.
🤖 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/force-layout.ts`:
- Line 70: Clamp and sanitize link volumes before any math: replace usages like
validLinks.map((l) => l.volume) and the distance computation in the block around
lines 93-97 with a clamped value (e.g., const v = Number.isFinite(l.volume) ?
Math.max(0, l.volume) : 0; or Math.max(1e-6, ...) if you need non-zero) so
maxVolume and subsequent ratio/distance calculations use a finite, non-negative
(or small positive) number; consider adding a small helper getClampedVolume(l)
and use it in validLinks.map and in the distance math to avoid NaN/out-of-range
distances.
- Around line 54-55: computeForceLayout is defaulting to
DEFAULT_WIDTH/DEFAULT_HEIGHT when options is omitted, causing centering
mismatches; pass the actual viewport dimensions from OrgChartPage into
useOrgChartData and forward them into computeForceLayout so it uses the real
canvas size. Update OrgChartPage to provide current width/height to the hook
(useOrgChartData), update useOrgChartData to accept those width/height params
and include them in the options object passed to computeForceLayout (instead of
calling computeForceLayout() with no options), and ensure the options
destructuring in computeForceLayout still falls back to
DEFAULT_WIDTH/DEFAULT_HEIGHT.

---

Duplicate comments:
In `@web/src/pages/org/force-layout.ts`:
- Around line 104-106: The synchronous call simulation.tick(TICK_COUNT) in
computeForceLayout blocks the main thread for large graphs; change it to an
incremental, non-blocking runner that processes small batches of ticks (e.g.,
1–10 ticks per frame) using requestAnimationFrame or setTimeout and returns a
Promise so callers can await progressive layout updates; update
computeForceLayout to use that async runner (or offload ticks to a web worker)
and ensure UI data hooks await or subscribe to incremental updates rather than
calling simulation.tick(...) directly.
🪄 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: 581137f0-f7e4-4c89-a2c9-426cf970f801

📥 Commits

Reviewing files that changed from the base of the PR and between 15069bc and 779e4ba.

📒 Files selected for processing (2)
  • docs/design/ux-guidelines.md
  • web/src/pages/org/force-layout.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: Dashboard Test
  • GitHub Check: Build Web
  • GitHub Check: Build Backend
  • GitHub Check: Build Sandbox
  • GitHub Check: Socket Security: Pull Request Alerts
  • GitHub Check: Dependency Review
  • GitHub Check: Analyze (python)
🧰 Additional context used
📓 Path-based instructions (4)
docs/design/**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Before implementing any feature or planning any issue, always read the relevant docs/design/ page referenced in DESIGN_SPEC.md. The design spec is the starting point for architecture, data models, and behavior. If implementation deviates from the spec, alert the user and explain why—user decides whether to proceed or update the spec. When approved deviations occur, update the relevant docs/design/ page to reflect the new reality.

Files:

  • docs/design/ux-guidelines.md
**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Documentation must follow Markdown conventions: use consistent heading levels, link to relevant sections, include code examples where appropriate, and keep content up-to-date with implementation changes.

Files:

  • docs/design/ux-guidelines.md
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Always reuse existing components from web/src/components/ui/ before creating new ones. When a component is needed that is not in the existing inventory, place it in web/src/components/ui/ with a descriptive kebab-case filename, create a .stories.tsx file with all states, export props as a TypeScript interface, and use design tokens exclusively.

Files:

  • web/src/pages/org/force-layout.ts
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

web/src/**/*.{ts,tsx}: Use Tailwind semantic classes (text-foreground, bg-card, text-accent, etc.) or CSS variables (var(--so-*)) for colors. Never hardcode hex values or rgba() with hardcoded values in .tsx/.ts files.
Use font-sans or font-mono classes for typography (mapping to Geist tokens). Never set fontFamily directly in component code.
Use density-aware tokens (p-card, gap-section-gap, gap-grid-gap) or standard Tailwind spacing for layout. Never hardcode pixel values for layout spacing.
Use token variables (var(--so-shadow-card-hover), border-border, border-bright) for shadows and borders. Never hardcode shadow or border values.
TypeScript 6.0: remove deprecated baseUrl from tsconfig; paths entries are relative to the tsconfig directory. Remove explicit "esModuleInterop": true (always true in TS 6). List needed types explicitly (e.g., "types": ["vitest/globals"]). DOM.Iterable is merged into DOM. Use "bundler" or "nodenext" for moduleResolution.

Files:

  • web/src/pages/org/force-layout.ts
🧠 Learnings (10)
📓 Common learnings
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/).
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to docs/design/**/*.md : Before implementing any feature or planning any issue, always read the relevant `docs/design/` page referenced in `DESIGN_SPEC.md`. The design spec is the starting point for architecture, data models, and behavior. If implementation deviates from the spec, alert the user and explain why—user decides whether to proceed or update the spec. When approved deviations occur, update the relevant `docs/design/` page to reflect the new reality.

Applied to files:

  • docs/design/ux-guidelines.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/ux-guidelines.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/ux-guidelines.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/ux-guidelines.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/ux-guidelines.md
📚 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: Always read the relevant `docs/design/` page before implementing any feature or planning any issue. DESIGN_SPEC.md is a pointer file linking to the 7 design pages (index, agents, organization, communication, engine, memory, operations).

Applied to files:

  • docs/design/ux-guidelines.md
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to web/src/**/*.tsx : Do NOT recreate status dots inline—use `<StatusBadge>`. Do NOT build card-with-header layouts from scratch—use `<SectionCard>`. Do NOT create metric displays with `text-metric font-bold`—use `<MetricCard>`. Do NOT render initials circles manually—use `<Avatar>`. Do NOT create complex (>8 line) JSX inside `.map()` — extract to a shared component.

Applied to files:

  • docs/design/ux-guidelines.md
📚 Learning: 2026-03-28T21:29:12.755Z
Learnt from: CR
Repo: Aureliolo/synthorg PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-28T21:29:12.755Z
Learning: Applies to **/*.md : Documentation must follow Markdown conventions: use consistent heading levels, link to relevant sections, include code examples where appropriate, and keep content up-to-date with implementation changes.

Applied to files:

  • docs/design/ux-guidelines.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/ux-guidelines.md
🔇 Additional comments (2)
docs/design/ux-guidelines.md (1)

322-322: Good scope clarification in drag-and-drop applicability.

This wording removes the prior version-mixing ambiguity and keeps the guideline aligned with current org chart hierarchy terminology.

web/src/pages/org/force-layout.ts (1)

58-62: Good fix: nullish coalescing preserves explicit 0 coordinates.

This seeding logic is now semantically correct and avoids conflating 0 with missing values.

Comment on lines +54 to +55
const { width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT } = options
const nodeIdSet = new Set(nodes.map((n) => n.id))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify all call sites and whether width/height options are passed.
# Expected: force-mode call sites should pass runtime viewport/canvas dimensions.

ast-grep --pattern 'computeForceLayout($_, $_)'
ast-grep --pattern 'computeForceLayout($_, $_, $_)'
rg -n -C2 '\bcomputeForceLayout\s*\(' --type=ts

Repository: Aureliolo/synthorg

Length of output: 6892


🏁 Script executed:

cat -n web/src/pages/org/force-layout.ts | head -70

Repository: Aureliolo/synthorg

Length of output: 2504


🏁 Script executed:

cat -n web/src/hooks/useOrgChartData.ts | sed -n '110,130p'

Repository: Aureliolo/synthorg

Length of output: 1174


🏁 Script executed:

cat -n web/src/hooks/useOrgChartData.ts | head -130

Repository: Aureliolo/synthorg

Length of output: 5973


🏁 Script executed:

rg -n "useOrgChartData" --type=ts -A 3 -B 1

Repository: Aureliolo/synthorg

Length of output: 2540


🏁 Script executed:

cat -n web/src/pages/OrgChartPage.tsx | sed -n '1,250p'

Repository: Aureliolo/synthorg

Length of output: 10133


🏁 Script executed:

rg -n "useRef\|viewportRef\|containerRef\|getBoundingClientRect\|clientWidth\|clientHeight" web/src/pages/OrgChartPage.tsx --type=ts -B 2 -A 2

Repository: Aureliolo/synthorg

Length of output: 44


🏁 Script executed:

cat -n web/src/pages/org/layout.ts | head -100

Repository: Aureliolo/synthorg

Length of output: 4054


🏁 Script executed:

rg -n "DEFAULT_WIDTH|DEFAULT_HEIGHT|800|600" web/src/pages/org/ --type=ts -B 2 -A 2

Repository: Aureliolo/synthorg

Length of output: 1047


🏁 Script executed:

rg -n "width|height|getViewport\|useReactFlow\|computeForceLayout" web/src/pages/OrgChartPage.tsx --type=ts | head -30

Repository: Aureliolo/synthorg

Length of output: 576


🏁 Script executed:

cat -n web/src/pages/org/force-layout.ts | sed -n '75,110p'

Repository: Aureliolo/synthorg

Length of output: 1731


🏁 Script executed:

cat -n web/src/pages/OrgChartPage.tsx | sed -n '177,250p'

Repository: Aureliolo/synthorg

Length of output: 3420


🏁 Script executed:

rg -n "getViewport\|getNodes\|getEdges\|viewport\|containerRef" web/src/pages/OrgChartPage.tsx --type=ts -B 1 -A 1

Repository: Aureliolo/synthorg

Length of output: 44


🏁 Script executed:

cat -n web/src/pages/OrgChartPage.tsx | tail -150

Repository: Aureliolo/synthorg

Length of output: 6681


🏁 Script executed:

rg -n "ReactFlow\|<ReactFlow" web/src/pages/OrgChartPage.tsx -A 20 --type=ts

Repository: Aureliolo/synthorg

Length of output: 44


Pass viewport dimensions to computeForceLayout to prevent centering mismatch.

computeForceLayout defaults to 800×600 centering when the options parameter is omitted. The production call in useOrgChartData.ts:118 does not pass options, so the force layout always centers at (400, 300) regardless of actual canvas size. The hierarchy view uses dagre layout with dynamic sizing, so switching between views can cause visible node position jumps if the viewport dimensions differ from the defaults. Pass the actual viewport width/height from OrgChartPage through useOrgChartData to computeForceLayout to keep both layouts centered consistently.

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

In `@web/src/pages/org/force-layout.ts` around lines 54 - 55, computeForceLayout
is defaulting to DEFAULT_WIDTH/DEFAULT_HEIGHT when options is omitted, causing
centering mismatches; pass the actual viewport dimensions from OrgChartPage into
useOrgChartData and forward them into computeForceLayout so it uses the real
canvas size. Update OrgChartPage to provide current width/height to the hook
(useOrgChartData), update useOrgChartData to accept those width/height params
and include them in the options object passed to computeForceLayout (instead of
calling computeForceLayout() with no options), and ensure the options
destructuring in computeForceLayout still falls back to
DEFAULT_WIDTH/DEFAULT_HEIGHT.

)

// Compute max volume for distance scaling
const maxVolume = Math.max(1, ...validLinks.map((l) => l.volume))
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Sanitize/clamp volume before distance math to avoid NaN/out-of-range distances.

CommunicationLink.volume is unconstrained at runtime; non-finite/negative values can produce invalid ratios and unstable positioning. Add a defensive clamp for robustness.

♻️ Defensive distance computation
-  const maxVolume = Math.max(1, ...validLinks.map((l) => l.volume))
+  const maxVolume = Math.max(
+    1,
+    ...validLinks.map((l) => (Number.isFinite(l.volume) && l.volume > 0 ? l.volume : 1)),
+  )

...
-          const volume = volumeMap.get(`${srcId}::${tgtId}`) ?? 1
+          const rawVolume = volumeMap.get(`${srcId}::${tgtId}`) ?? 1
+          const volume = Number.isFinite(rawVolume) && rawVolume > 0 ? rawVolume : 1
           // Higher volume = shorter distance (inverse relationship)
-          const ratio = volume / maxVolume
+          const ratio = Math.min(1, volume / maxVolume)
           return MAX_LINK_DISTANCE - ratio * (MAX_LINK_DISTANCE - MIN_LINK_DISTANCE)

Also applies to: 93-97

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

In `@web/src/pages/org/force-layout.ts` at line 70, Clamp and sanitize link
volumes before any math: replace usages like validLinks.map((l) => l.volume) and
the distance computation in the block around lines 93-97 with a clamped value
(e.g., const v = Number.isFinite(l.volume) ? Math.max(0, l.volume) : 0; or
Math.max(1e-6, ...) if you need non-zero) so maxVolume and subsequent
ratio/distance calculations use a finite, non-negative (or small positive)
number; consider adding a small helper getClampedVolume(l) and use it in
validLinks.map and in the distance math to avoid NaN/out-of-range distances.

@Aureliolo Aureliolo merged commit a68a938 into main Mar 29, 2026
32 checks passed
@Aureliolo Aureliolo deleted the feat/org-chart-features branch March 29, 2026 11:32
@Aureliolo Aureliolo temporarily deployed to cloudflare-preview March 29, 2026 11:32 — with GitHub Actions Inactive
Aureliolo added a commit that referenced this pull request Mar 30, 2026
🤖 I have created a release *beep* *boop*
---
#MAJOR CHANGES; We got a somewhat working webui :)

##
[0.5.0](v0.4.9...v0.5.0)
(2026-03-30)


### Features

* add analytics trends and budget forecast API endpoints
([#798](#798))
([16b61f5](16b61f5))
* add department policies to default templates
([#852](#852))
([7a41548](7a41548))
* add remaining activity event types (task_started, tool_used,
delegation, cost_incurred)
([#832](#832))
([4252fac](4252fac))
* agent performance, activity, and history API endpoints
([#811](#811))
([9b75c1d](9b75c1d))
* Agent Profiles and Detail pages (biography, career, performance)
([#874](#874))
([62d7880](62d7880))
* app shell, Storybook, and CI/CD pipeline
([#819](#819))
([d4dde90](d4dde90))
* Approvals page with risk grouping, urgency indicators, batch actions
([#889](#889))
([4e9673d](4e9673d))
* Budget Panel page (P&L dashboard, breakdown charts, forecast)
([#890](#890))
([b63b0f1](b63b0f1))
* build infrastructure layer (API client, auth, WebSocket)
([#815](#815))
([9f01d3e](9f01d3e))
* CLI global options infrastructure, UI modes, exit codes, env vars
([#891](#891))
([fef4fc5](fef4fc5))
* CodeMirror editor and theme preferences toggle
([#905](#905),
[#807](#807))
([#909](#909))
([41fbedc](41fbedc))
* Company page (department/agent management)
([#888](#888))
([cfb88b0](cfb88b0))
* comprehensive hint coverage across all CLI commands
([#900](#900))
([937974e](937974e))
* config system extensions, per-command flags for
init/start/stop/status/logs
([#895](#895))
([32f83fe](32f83fe))
* configurable currency system replacing hardcoded USD
([#854](#854))
([b372551](b372551))
* Dashboard page (metric cards, activity feed, budget burn)
([#861](#861))
([7d519d5](7d519d5))
* department health, provider status, and activity feed endpoints
([#818](#818))
([6d5f196](6d5f196))
* design tokens and core UI components
([#833](#833))
([ed887f2](ed887f2))
* extend approval, meeting, and budget API responses
([#834](#834))
([31472bf](31472bf))
* frontend polish -- real-time UX, accessibility, responsive,
performance ([#790](#790),
[#792](#792),
[#791](#791),
[#793](#793))
([#917](#917))
([f04a537](f04a537))
* implement human roles and access control levels
([#856](#856))
([d6d8a06](d6d8a06))
* implement semantic conflict detection in workspace merge
([#860](#860))
([d97283b](d97283b))
* interaction components and animation patterns
([#853](#853))
([82d4b01](82d4b01))
* Login page + first-run bootstrap + Company page
([#789](#789),
[#888](#888))
([#896](#896))
([8758e8d](8758e8d))
* Meetings page with timeline viz, token bars, contribution formatting
([#788](#788))
([#904](#904))
([b207f46](b207f46))
* Messages page with threading, channel badges, sender indicators
([#787](#787))
([#903](#903))
([28293ad](28293ad))
* Org Chart force-directed view and drag-drop reassignment
([#872](#872),
[#873](#873))
([#912](#912))
([a68a938](a68a938))
* Org Chart page (living nodes, status, CRUD, department health)
([#870](#870))
([0acbdae](0acbdae))
* per-command flags for remaining commands, auto-behavior wiring,
help/discoverability
([#897](#897))
([3f7afa2](3f7afa2))
* Providers page with backend rework -- health, CRUD, subscription auth
([#893](#893))
([9f8dd98](9f8dd98))
* scaffold React + Vite + TypeScript + Tailwind project
([#799](#799))
([bd151aa](bd151aa))
* Settings page with search, dependency indicators, grouped rendering
([#784](#784))
([#902](#902))
([a7b9870](a7b9870))
* Setup Wizard rebuild with template comparison, cost estimator, theme
customization ([#879](#879))
([ae8b50b](ae8b50b))
* setup wizard UX -- template filters, card metadata, provider form
reuse ([#910](#910))
([7f04676](7f04676))
* setup wizard UX overhaul -- mode choice, step reorder, provider fixes
([#907](#907))
([ee964c4](ee964c4))
* structured ModelRequirement in template agent configs
([#795](#795))
([7433548](7433548))
* Task Board page (rich Kanban, filtering, dependency viz)
([#871](#871))
([04a19b0](04a19b0))


### Bug Fixes

* align frontend types with backend and debounce WS refetches
([#916](#916))
([134c11b](134c11b))
* auto-cleanup targets newly pulled images instead of old ones
([#884](#884))
([50e6591](50e6591))
* correct wipe backup-skip flow and harden error handling
([#808](#808))
([c05860f](c05860f))
* improve provider setup in wizard, subscription auth, dashboard bugs
([#914](#914))
([87bf8e6](87bf8e6))
* improve update channel detection and add config get command
([#814](#814))
([6b137f0](6b137f0))
* resolve all ESLint warnings, add zero-warnings enforcement
([#899](#899))
([079b46a](079b46a))
* subscription auth uses api_key, base URL optional for cloud providers
([#915](#915))
([f0098dd](f0098dd))


### Refactoring

* semantic analyzer cleanup -- shared filtering, concurrency, extraction
([#908](#908))
([81372bf](81372bf))


### Documentation

* brand identity and UX design system from
[#765](#765) exploration
([#804](#804))
([389a9f4](389a9f4))
* page structure and information architecture for v0.5.0 dashboard
([#809](#809))
([f8d6d4a](f8d6d4a))
* write UX design guidelines with WCAG-verified color system
([#816](#816))
([4a4594e](4a4594e))


### Tests

* add unit tests for agent hooks and page components
([#875](#875))
([#901](#901))
([1d81546](1d81546))


### CI/CD

* bump actions/deploy-pages from 4.0.5 to 5.0.0 in the major group
([#831](#831))
([01c19de](01c19de))
* bump astral-sh/setup-uv from 7.6.0 to 8.0.0 in
/.github/actions/setup-python-uv in the all group
([#920](#920))
([5f6ba54](5f6ba54))
* bump codecov/codecov-action from 5.5.3 to 6.0.0 in the major group
([#868](#868))
([f22a181](f22a181))
* bump github/codeql-action from 4.34.1 to 4.35.0 in the all group
([#883](#883))
([87a4890](87a4890))
* bump sigstore/cosign-installer from 4.1.0 to 4.1.1 in the
minor-and-patch group
([#830](#830))
([7a69050](7a69050))
* bump the all group with 3 updates
([#923](#923))
([ff27c8e](ff27c8e))
* bump wrangler from 4.76.0 to 4.77.0 in /.github in the minor-and-patch
group ([#822](#822))
([07d43eb](07d43eb))
* bump wrangler from 4.77.0 to 4.78.0 in /.github in the all group
([#882](#882))
([f84118d](f84118d))


### Maintenance

* add design system enforcement hook and component inventory
([#846](#846))
([15abc43](15abc43))
* add dev-only auth bypass for frontend testing
([#885](#885))
([6cdcd8a](6cdcd8a))
* add pre-push rebase check hook
([#855](#855))
([b637a04](b637a04))
* backend hardening -- eviction/size-caps and model validation
([#911](#911))
([81253d9](81253d9))
* bump axios from 1.13.6 to 1.14.0 in /web in the all group across 1
directory ([#922](#922))
([b1b0232](b1b0232))
* bump brace-expansion from 5.0.4 to 5.0.5 in /web
([#862](#862))
([ba4a565](ba4a565))
* bump eslint-plugin-react-refresh from 0.4.26 to 0.5.2 in /web
([#801](#801))
([7574bb5](7574bb5))
* bump faker from 40.11.0 to 40.11.1 in the minor-and-patch group
([#803](#803))
([14d322e](14d322e))
* bump https://github.com/astral-sh/ruff-pre-commit from v0.15.7 to
0.15.8 ([#864](#864))
([f52901e](f52901e))
* bump nginxinc/nginx-unprivileged from `6582a34` to `f99cc61` in
/docker/web in the all group
([#919](#919))
([df85e4f](df85e4f))
* bump nginxinc/nginx-unprivileged from `ccbac1a` to `6582a34` in
/docker/web ([#800](#800))
([f4e9450](f4e9450))
* bump node from `44bcbf4` to `71be405` in /docker/sandbox
([#827](#827))
([91bec67](91bec67))
* bump node from `5209bca` to `cf38e1f` in /docker/web
([#863](#863))
([66d6043](66d6043))
* bump picomatch in /site
([#842](#842))
([5f20bcc](5f20bcc))
* bump recharts 2-&gt;3 and @types/node 22-&gt;25 in /web
([#802](#802))
([a908800](a908800))
* Bump requests from 2.32.5 to 2.33.0
([#843](#843))
([41daf69](41daf69))
* bump smol-toml from 1.6.0 to 1.6.1 in /site
([#826](#826))
([3e5dbe4](3e5dbe4))
* bump the all group with 3 updates
([#921](#921))
([7bace0b](7bace0b))
* bump the minor-and-patch group across 1 directory with 2 updates
([#829](#829))
([93e611f](93e611f))
* bump the minor-and-patch group across 1 directory with 3 updates
([#841](#841))
([7010c8e](7010c8e))
* bump the minor-and-patch group across 1 directory with 3 updates
([#869](#869))
([548cee5](548cee5))
* bump the minor-and-patch group in /site with 2 updates
([#865](#865))
([9558101](9558101))
* bump the minor-and-patch group with 2 updates
([#867](#867))
([4830706](4830706))
* consolidate Dependabot groups to 1 PR per ecosystem
([06d2556](06d2556))
* consolidate Dependabot groups to 1 PR per ecosystem
([#881](#881))
([06d2556](06d2556))
* improve worktree skill with full dep sync and status enhancements
([#906](#906))
([772c625](772c625))
* remove Vue remnants and document framework decision
([#851](#851))
([bf2adf6](bf2adf6))
* update web dependencies and fix brace-expansion CVE
([#880](#880))
([a7a0ed6](a7a0ed6))
* upgrade to Storybook 10 and TypeScript 6
([#845](#845))
([52d95f2](52d95f2))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: Org Chart inline drag-drop agent reassignment feat: Org Chart force-directed communication graph view

1 participant