feat: Org Chart force-directed view and drag-drop reassignment (#872, #873)#912
feat: Org Chart force-directed view and drag-drop reassignment (#872, #873)#912
Conversation
WalkthroughThe 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)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
Dependency ReviewThe following issues were found:
Snapshot WarningsEnsure 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
Scanned Files
|
There was a problem hiding this comment.
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.
web/src/pages/OrgChartPage.tsx
Outdated
| const target = findDropTarget( | ||
| { x: node.position.x + 80, y: node.position.y + 40 }, | ||
| deptBounds, |
There was a problem hiding this comment.
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,
)| <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> |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
| .stop() | ||
|
|
||
| // Run simulation to convergence synchronously | ||
| simulation.tick(TICK_COUNT) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
⛔ Files ignored due to path filters (1)
web/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (22)
CLAUDE.mddocs/design/page-structure.mdweb/package.jsonweb/src/__tests__/hooks/useCommunicationEdges.test.tsweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/src/__tests__/pages/org/OrgChartPage.test.tsxweb/src/__tests__/pages/org/aggregate-messages.test.tsweb/src/__tests__/pages/org/drop-target.test.tsweb/src/__tests__/pages/org/force-layout.test.tsweb/src/__tests__/pages/org/reassign-agent.test.tsweb/src/hooks/useCommunicationEdges.tsweb/src/hooks/useOrgChartData.tsweb/src/pages/OrgChartPage.tsxweb/src/pages/org/CommunicationEdge.stories.tsxweb/src/pages/org/CommunicationEdge.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org/aggregate-messages.tsweb/src/pages/org/build-org-tree.tsweb/src/pages/org/drop-target.tsweb/src/pages/org/force-layout.tsweb/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.mddocs/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 inweb/src/components/ui/with a descriptive kebab-case filename, create a.stories.tsxfile with all states, export props as a TypeScript interface, and use design tokens exclusively.
Files:
web/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org/build-org-tree.tsweb/src/__tests__/pages/org/OrgChartPage.test.tsxweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/src/__tests__/pages/org/drop-target.test.tsweb/src/__tests__/pages/org/reassign-agent.test.tsweb/src/hooks/useCommunicationEdges.tsweb/src/stores/company.tsweb/src/__tests__/pages/org/aggregate-messages.test.tsweb/src/__tests__/hooks/useCommunicationEdges.test.tsweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org/force-layout.tsweb/src/hooks/useOrgChartData.tsweb/src/pages/org/drop-target.tsweb/src/pages/org/CommunicationEdge.stories.tsxweb/src/pages/OrgChartPage.tsxweb/src/__tests__/pages/org/force-layout.test.tsweb/src/pages/org/aggregate-messages.tsweb/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/.tsfiles.
Usefont-sansorfont-monoclasses for typography (mapping to Geist tokens). Never setfontFamilydirectly 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 deprecatedbaseUrlfrom tsconfig;pathsentries 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"formoduleResolution.
Files:
web/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org/build-org-tree.tsweb/src/__tests__/pages/org/OrgChartPage.test.tsxweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/src/__tests__/pages/org/drop-target.test.tsweb/src/__tests__/pages/org/reassign-agent.test.tsweb/src/hooks/useCommunicationEdges.tsweb/src/stores/company.tsweb/src/__tests__/pages/org/aggregate-messages.test.tsweb/src/__tests__/hooks/useCommunicationEdges.test.tsweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org/force-layout.tsweb/src/hooks/useOrgChartData.tsweb/src/pages/org/drop-target.tsweb/src/pages/org/CommunicationEdge.stories.tsxweb/src/pages/OrgChartPage.tsxweb/src/__tests__/pages/org/force-layout.test.tsweb/src/pages/org/aggregate-messages.tsweb/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 withtext-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.tsxweb/src/__tests__/pages/org/OrgChartPage.test.tsxweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org/CommunicationEdge.stories.tsxweb/src/pages/OrgChartPage.tsxweb/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) andstorybook/actions(not@storybook/addon-actions). UsedefineMainfrom@storybook/react-vite/nodeanddefinePreviewfrom@storybook/react-vitewith explicitframeworkfield. Useparameters.backgrounds.options(object keyed by name) +initialGlobals.backgrounds.value. Useparameters.a11y.test: 'error' | 'todo' | 'off'for WCAG compliance.
Files:
web/src/pages/org/DepartmentGroupNode.stories.tsxweb/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 inDESIGN_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 relevantdocs/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.jsonCLAUDE.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.jsonweb/src/__tests__/pages/org/drop-target.test.tsweb/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.jsonCLAUDE.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.tsxweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/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.tsxweb/src/__tests__/pages/org/OrgChartPage.test.tsxweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/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.tsxweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/src/__tests__/pages/org/drop-target.test.tsweb/src/__tests__/pages/org/reassign-agent.test.tsweb/src/__tests__/pages/org/aggregate-messages.test.tsweb/src/__tests__/hooks/useCommunicationEdges.test.tsweb/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.mdweb/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
CommunicationLinkinterface is well-documented with clear field semantics. The alphabetical ordering contract forsource/targetis 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 = 0edge case producingInfinityis acceptable since the consuming code (CommunicationEdge.tsxlines 39-44) guards against this withMath.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
vitestandfast-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 thanPAGE_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
beforeEachreset 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
DropTargetActivestory to demonstrate the drop-target highlight state. The story uses realistic department metrics and correctly setsisDropTarget: true.CLAUDE.md (1)
423-423: LGTM!Documentation correctly updated to include
d3-forcein the web dashboard dependencies list, keeping CLAUDE.md in sync withpackage.json.web/src/pages/org/DepartmentGroupNode.tsx (1)
22-26: LGTM!Clean implementation of drop-target highlighting:
transition-colors duration-200enables smooth visual feedback.- Conditional styling correctly prioritizes
isDropTargetover 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
isDropTargeton 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.
| // 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 }) | ||
| }) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Actionable comments posted: 9
♻️ Duplicate comments (2)
docs/design/page-structure.md (1)
332-332:⚠️ Potential issue | 🟡 MinorResolve the
v0.5.0vsv0.5.1split.This page is still scoped as the
v0.5.0IA, so calling hierarchy drag-drop av0.5.1feature 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 | 🔵 TrivialMissing Storybook background configuration per project conventions.
The meta configuration lacks
parameters.backgrounds.optionsandinitialGlobals.backgrounds.valuerequired 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: "Useparameters.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
📒 Files selected for processing (13)
docs/design/operations.mddocs/design/page-structure.mddocs/design/ux-guidelines.mdweb/src/__tests__/hooks/useCommunicationEdges.test.tsweb/src/__tests__/pages/org/aggregate-messages.test.tsweb/src/__tests__/pages/org/reassign-agent.test.tsweb/src/hooks/useCommunicationEdges.tsweb/src/hooks/useOrgChartData.tsweb/src/pages/OrgChartPage.tsxweb/src/pages/org/CommunicationEdge.stories.tsxweb/src/pages/org/CommunicationEdge.tsxweb/src/pages/org/aggregate-messages.tsweb/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 inDESIGN_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 relevantdocs/design/page to reflect the new reality.
Files:
docs/design/ux-guidelines.mddocs/design/operations.mddocs/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.mddocs/design/operations.mddocs/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 inweb/src/components/ui/with a descriptive kebab-case filename, create a.stories.tsxfile with all states, export props as a TypeScript interface, and use design tokens exclusively.
Files:
web/src/__tests__/pages/org/aggregate-messages.test.tsweb/src/stores/company.tsweb/src/hooks/useCommunicationEdges.tsweb/src/__tests__/pages/org/reassign-agent.test.tsweb/src/pages/OrgChartPage.tsxweb/src/pages/org/CommunicationEdge.tsxweb/src/pages/org/CommunicationEdge.stories.tsxweb/src/pages/org/aggregate-messages.tsweb/src/hooks/useOrgChartData.tsweb/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/.tsfiles.
Usefont-sansorfont-monoclasses for typography (mapping to Geist tokens). Never setfontFamilydirectly 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 deprecatedbaseUrlfrom tsconfig;pathsentries 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"formoduleResolution.
Files:
web/src/__tests__/pages/org/aggregate-messages.test.tsweb/src/stores/company.tsweb/src/hooks/useCommunicationEdges.tsweb/src/__tests__/pages/org/reassign-agent.test.tsweb/src/pages/OrgChartPage.tsxweb/src/pages/org/CommunicationEdge.tsxweb/src/pages/org/CommunicationEdge.stories.tsxweb/src/pages/org/aggregate-messages.tsweb/src/hooks/useOrgChartData.tsweb/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 withtext-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.tsxweb/src/pages/org/CommunicationEdge.tsxweb/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) andstorybook/actions(not@storybook/addon-actions). UsedefineMainfrom@storybook/react-vite/nodeanddefinePreviewfrom@storybook/react-vitewith explicitframeworkfield. Useparameters.backgrounds.options(object keyed by name) +initialGlobals.backgrounds.value. Useparameters.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.mddocs/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.mddocs/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.mddocs/design/page-structure.mdweb/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.mddocs/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.mddocs/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.tsweb/src/__tests__/pages/org/reassign-agent.test.tsweb/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.tsxweb/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.tsxweb/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.tsxweb/src/pages/org/CommunicationEdge.tsxweb/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.tsxweb/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.tsxweb/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.tsxweb/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 (perweb/src/stores/company.ts:328-350), which compares the agent's current department against the capturednewDepartmentat 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
useEffectproperly cancels in-flight animations, respectsprefersReducedMotion(), 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
dashDurationbetweenMIN_DASH_DURATIONandMAX_DASH_DURATIONusing nestedMath.min/Math.max. This prevents animation speed from drifting beyond intended limits.
47-48: Keyframe name sanitization — addressed.The
safeIdis 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
useMemodependency 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
optimisticReassignAgentstore 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, andHighVolumestories 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
sourceandtargetexist in the visible node set (visibleIds). This prevents orphan edges when messages reference deleted or archived agents.
30-45:buildCommunicationEdgeshelper is well-designed.The function correctly:
- Computes
maxVolumewith a floor of 1 to prevent division by zero in consumers- Uses collision-safe IDs with URI encoding
- Satisfies the
CommunicationEdgeDatatype 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
parentIdto prevent department grouping- Filters communication links to visible nodes
- Falls back to dagre layout for hierarchy mode
The memoization dependencies correctly include
viewModeandcommLinks.
| 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) { |
There was a problem hiding this comment.
❓ 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/srcRepository: 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.
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (2)
web/src/hooks/useOrgChartData.ts (1)
24-25:⚠️ Potential issue | 🟠 MajorPropagate
truncatedto the page instead of dropping it here.
useCommunicationEdges()now tells you when the 10-page cap was hit, butuseOrgChartData()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 | 🟠 MajorDon'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 tooriginalDept. 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
📒 Files selected for processing (9)
docs/design/page-structure.mdweb/src/__tests__/hooks/useCommunicationEdges.test.tsweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/src/__tests__/pages/org/reassign-agent.test.tsweb/src/api/endpoints/messages.tsweb/src/hooks/useCommunicationEdges.tsweb/src/hooks/useOrgChartData.tsweb/src/pages/OrgChartPage.tsxweb/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 inweb/src/components/ui/with a descriptive kebab-case filename, create a.stories.tsxfile with all states, export props as a TypeScript interface, and use design tokens exclusively.
Files:
web/src/api/endpoints/messages.tsweb/src/__tests__/pages/org/reassign-agent.test.tsweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/src/__tests__/hooks/useCommunicationEdges.test.tsweb/src/pages/org/CommunicationEdge.tsxweb/src/pages/OrgChartPage.tsxweb/src/hooks/useCommunicationEdges.tsweb/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/.tsfiles.
Usefont-sansorfont-monoclasses for typography (mapping to Geist tokens). Never setfontFamilydirectly 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 deprecatedbaseUrlfrom tsconfig;pathsentries 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"formoduleResolution.
Files:
web/src/api/endpoints/messages.tsweb/src/__tests__/pages/org/reassign-agent.test.tsweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/src/__tests__/hooks/useCommunicationEdges.test.tsweb/src/pages/org/CommunicationEdge.tsxweb/src/pages/OrgChartPage.tsxweb/src/hooks/useCommunicationEdges.tsweb/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 inDESIGN_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 relevantdocs/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 withtext-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.tsxweb/src/pages/org/CommunicationEdge.tsxweb/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.mdweb/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.tsweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/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.tsxweb/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.tsxweb/src/pages/OrgChartPage.tsxweb/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. IfbuildOrgTree()does not explicitly setdraggable: falseon 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
draggableflags or a globalnodesDraggable={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/orgis correctly updated.Good separation between REST-polled communication data and
agentsWebSocket events.
228-228:/orgroute 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.
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>
f8dc9bf to
15069bc
Compare
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (2)
web/src/pages/OrgChartPage.tsx (1)
400-419:⚠️ Potential issue | 🟠 MajorOptimistic 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
optimisticReassignAgentonly checks if the department matches the optimistic value—it doesn't distinguish between different in-flight operations.Consider either:
- Block re-drag while a request is in flight (simplest)
- 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 | 🟡 MinorAdd Storybook backgrounds configuration per project conventions.
The story metadata is missing
parameters.backgrounds.optionsandinitialGlobals.backgrounds.valuerequired 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: "Useparameters.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
⛔ Files ignored due to path filters (1)
web/package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (25)
CLAUDE.mddocs/design/operations.mddocs/design/page-structure.mddocs/design/ux-guidelines.mdweb/package.jsonweb/src/__tests__/hooks/useCommunicationEdges.test.tsweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/src/__tests__/pages/org/OrgChartPage.test.tsxweb/src/__tests__/pages/org/aggregate-messages.test.tsweb/src/__tests__/pages/org/drop-target.test.tsweb/src/__tests__/pages/org/force-layout.test.tsweb/src/__tests__/pages/org/reassign-agent.test.tsweb/src/api/endpoints/messages.tsweb/src/hooks/useCommunicationEdges.tsweb/src/hooks/useOrgChartData.tsweb/src/pages/OrgChartPage.tsxweb/src/pages/org/CommunicationEdge.stories.tsxweb/src/pages/org/CommunicationEdge.tsxweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/pages/org/aggregate-messages.tsweb/src/pages/org/build-org-tree.tsweb/src/pages/org/drop-target.tsweb/src/pages/org/force-layout.tsweb/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 inDESIGN_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 relevantdocs/design/page to reflect the new reality.
Files:
docs/design/ux-guidelines.mddocs/design/operations.mddocs/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.mdCLAUDE.mddocs/design/operations.mddocs/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 inweb/src/components/ui/with a descriptive kebab-case filename, create a.stories.tsxfile with all states, export props as a TypeScript interface, and use design tokens exclusively.
Files:
web/src/pages/org/build-org-tree.tsweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/api/endpoints/messages.tsweb/src/__tests__/pages/org/drop-target.test.tsweb/src/__tests__/pages/org/OrgChartPage.test.tsxweb/src/__tests__/pages/org/aggregate-messages.test.tsweb/src/__tests__/pages/org/force-layout.test.tsweb/src/pages/org/drop-target.tsweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/src/stores/company.tsweb/src/__tests__/hooks/useCommunicationEdges.test.tsweb/src/pages/OrgChartPage.tsxweb/src/hooks/useCommunicationEdges.tsweb/src/__tests__/pages/org/reassign-agent.test.tsweb/src/pages/org/CommunicationEdge.stories.tsxweb/src/pages/org/force-layout.tsweb/src/pages/org/aggregate-messages.tsweb/src/hooks/useOrgChartData.tsweb/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/.tsfiles.
Usefont-sansorfont-monoclasses for typography (mapping to Geist tokens). Never setfontFamilydirectly 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 deprecatedbaseUrlfrom tsconfig;pathsentries 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"formoduleResolution.
Files:
web/src/pages/org/build-org-tree.tsweb/src/pages/org/DepartmentGroupNode.stories.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/api/endpoints/messages.tsweb/src/__tests__/pages/org/drop-target.test.tsweb/src/__tests__/pages/org/OrgChartPage.test.tsxweb/src/__tests__/pages/org/aggregate-messages.test.tsweb/src/__tests__/pages/org/force-layout.test.tsweb/src/pages/org/drop-target.tsweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/src/stores/company.tsweb/src/__tests__/hooks/useCommunicationEdges.test.tsweb/src/pages/OrgChartPage.tsxweb/src/hooks/useCommunicationEdges.tsweb/src/__tests__/pages/org/reassign-agent.test.tsweb/src/pages/org/CommunicationEdge.stories.tsxweb/src/pages/org/force-layout.tsweb/src/pages/org/aggregate-messages.tsweb/src/hooks/useOrgChartData.tsweb/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 withtext-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.tsxweb/src/pages/org/DepartmentGroupNode.tsxweb/src/__tests__/pages/org/OrgChartPage.test.tsxweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/src/pages/OrgChartPage.tsxweb/src/pages/org/CommunicationEdge.stories.tsxweb/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) andstorybook/actions(not@storybook/addon-actions). UsedefineMainfrom@storybook/react-vite/nodeanddefinePreviewfrom@storybook/react-vitewith explicitframeworkfield. Useparameters.backgrounds.options(object keyed by name) +initialGlobals.backgrounds.value. Useparameters.a11y.test: 'error' | 'todo' | 'off'for WCAG compliance.
Files:
web/src/pages/org/DepartmentGroupNode.stories.tsxweb/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.jsonCLAUDE.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.jsonweb/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.jsonCLAUDE.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.mddocs/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.mddocs/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.mddocs/design/page-structure.mdweb/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.mddocs/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.mddocs/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.tsxweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/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.tsxweb/src/__tests__/pages/org/OrgChartPage.test.tsxweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/src/__tests__/hooks/useCommunicationEdges.test.tsweb/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.tsxweb/src/pages/OrgChartPage.tsxweb/src/pages/org/CommunicationEdge.stories.tsxweb/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.tsxweb/src/pages/OrgChartPage.tsxweb/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.tsweb/src/__tests__/pages/org/OrgChartPage.test.tsxweb/src/__tests__/pages/org/aggregate-messages.test.tsweb/src/__tests__/pages/org/force-layout.test.tsweb/src/__tests__/pages/org/CommunicationEdge.test.tsxweb/src/__tests__/hooks/useCommunicationEdges.test.tsweb/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.tsxweb/src/pages/org/CommunicationEdge.stories.tsxweb/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.tsxweb/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:DepartmentGroupDataextension is backward-compatible.Making
isDropTargetoptional 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.
DropTargetActivegives 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
nullinstead 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-forcedependency and its TypeScript types are correctly added to support the new force-directed communication view. The version ranges (^3.0.0and^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-forcedependency 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
#872and#873.web/src/__tests__/pages/org/OrgChartPage.test.tsx (3)
26-27: Good practice: stable reference for empty edges.Using a module-level
EMPTY_EDGESconstant 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 theUseOrgChartDataReturninterface definition fromuseOrgChartData.ts.Also applies to: 43-45
159-164: LGTM!Good coverage for the communication error banner. The test correctly sets
mockCommErrorand verifies the error message is displayed to users.web/src/api/endpoints/messages.ts (1)
4-7: LGTM!Clean implementation of
AbortSignalsupport:
- Signal is correctly destructured and excluded from query params
- Backward compatible with existing callers (signal is optional)
- Enables proper request cancellation for the new
useCommunicationEdgeshookweb/src/__tests__/pages/org/aggregate-messages.test.ts (1)
1-134: LGTM!Comprehensive test suite for
aggregateMessageswith 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-motionbranch. The mock correctly returnstrueand the assertion verifies no animation style is applied.
1-139: LGTM!Well-structured test suite for
CommunicationEdgecovering:
- 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 (seeweb/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
useMemodependency array is complete.web/src/pages/OrgChartPage.tsx (2)
232-280: LGTM!The transition animation correctly handles mid-animation restarts by using
transition.displayNodesas 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)andvar(--so-shadow-card-hover). Theprefers-reduced-motionmedia query disables transitions appropriately.web/src/hooks/useCommunicationEdges.ts (1)
62-102: LGTM!The hook correctly:
- Passes
signaltolistMessagesfor request cancellation- Advances offset by actual
data.length(handles sparse pages)- Sets
truncatedflag 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
useCommunicationEdgestoviewMode === '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
buildOrgTreeare created with onlyparentIdas a grouping property; noextentproperty is set anywhere in the codebase. Clearing onlyparentIdis sufficient to detach nodes for force mode layout.> Likely an incorrect or invalid review comment.
| import type { CommunicationEdgeData } from '@/pages/org/CommunicationEdge' | ||
| import type { ViewMode } from '@/pages/org/OrgChartToolbar' |
There was a problem hiding this comment.
🛠️ 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.
| function buildCommunicationEdges( | ||
| links: CommunicationLink[], | ||
| ): Edge[] { | ||
| const maxVolume = Math.max(1, ...links.map((l) => l.volume)) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "useOrgChartData.ts" -type fRepository: Aureliolo/synthorg
Length of output: 96
🏁 Script executed:
cat -n web/src/hooks/useOrgChartData.ts | head -50Repository: 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 3Repository: Aureliolo/synthorg
Length of output: 23322
🏁 Script executed:
rg "CommunicationLink" web/src/pages/org/aggregate-messages.ts -A 5 | head -30Repository: Aureliolo/synthorg
Length of output: 498
🏁 Script executed:
rg "MAX\|LIMIT\|BATCH" web/src/pages/org/ --context 2Repository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
rg "MAX_PAGES" web/src/hooks/useCommunicationEdges.ts -B 2 -A 2Repository: Aureliolo/synthorg
Length of output: 1018
🏁 Script executed:
rg "PAGE_LIMIT" web/src/hooks/useCommunicationEdges.ts -B 2 -A 2Repository: 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.
| 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.
| 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, | ||
| })) |
There was a problem hiding this comment.
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.
| 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.
| // Run simulation to convergence synchronously | ||
| simulation.tick(TICK_COUNT) | ||
|
|
There was a problem hiding this comment.
🧹 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>
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (1)
web/src/pages/org/force-layout.ts (1)
104-106:⚠️ Potential issue | 🟠 MajorSynchronous 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
📒 Files selected for processing (2)
docs/design/ux-guidelines.mdweb/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 inDESIGN_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 relevantdocs/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 inweb/src/components/ui/with a descriptive kebab-case filename, create a.stories.tsxfile 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/.tsfiles.
Usefont-sansorfont-monoclasses for typography (mapping to Geist tokens). Never setfontFamilydirectly 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 deprecatedbaseUrlfrom tsconfig;pathsentries 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"formoduleResolution.
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 explicit0coordinates.This seeding logic is now semantically correct and avoids conflating
0with missing values.
| const { width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT } = options | ||
| const nodeIdSet = new Set(nodes.map((n) => n.id)) |
There was a problem hiding this comment.
🧩 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=tsRepository: Aureliolo/synthorg
Length of output: 6892
🏁 Script executed:
cat -n web/src/pages/org/force-layout.ts | head -70Repository: 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 -130Repository: Aureliolo/synthorg
Length of output: 5973
🏁 Script executed:
rg -n "useOrgChartData" --type=ts -A 3 -B 1Repository: 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 2Repository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
cat -n web/src/pages/org/layout.ts | head -100Repository: 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 2Repository: Aureliolo/synthorg
Length of output: 1047
🏁 Script executed:
rg -n "width|height|getViewport\|useReactFlow\|computeForceLayout" web/src/pages/OrgChartPage.tsx --type=ts | head -30Repository: 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 1Repository: Aureliolo/synthorg
Length of output: 44
🏁 Script executed:
cat -n web/src/pages/OrgChartPage.tsx | tail -150Repository: Aureliolo/synthorg
Length of output: 6681
🏁 Script executed:
rg -n "ReactFlow\|<ReactFlow" web/src/pages/OrgChartPage.tsx -A 20 --type=tsRepository: 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)) |
There was a problem hiding this comment.
🧹 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.
🤖 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->3 and @types/node 22->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>
Summary
aggregateMessages()for client-side message aggregation,computeForceLayout()for d3-force simulation,CommunicationEdgecustom React Flow edge, anduseCommunicationEdgesdata hook.prefers-reduced-motionsupport for edge animations, stale data clearing on view toggle, documentation updates topage-structure.mdandCLAUDE.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 errorsnpm --prefix web run lint-- zero warnings/org-- smooth transition, edges show varying thicknessCommunicationEdgestories (Low/Medium/High volume) andDropTargetActivestory render correctlyReview coverage
Pre-reviewed by 3 agents (frontend-reviewer, issue-resolution-verifier, docs-consistency), 7 findings addressed.
Closes #872
Closes #873
🤖 Generated with Claude Code