# How the Org Chart Tree Structure Works with Reporting Relationships in Paperclip

> Understand how Paperclip's org chart tree structure uses reports_to for reporting relationships. Learn about DAGs, cycle prevention, and depth violations for reliable task delegation.

- Repository: [Paperclip/paperclip](https://github.com/paperclipai/paperclip)
- Tags: deep-dive
- Published: 2026-08-16

---

**The Paperclip org chart tree structure uses a `reports_to` foreign key on agent records to build a directed acyclic graph, with runtime validation preventing cycles and depth violations to ensure reliable task delegation up and down the hierarchy.**

Paperclip models organizational hierarchy as a **tree of agents** where each agent reports to exactly one manager within the same company. This structure powers both the visual org chart and the core delegation logic that routes tasks through the reporting chain. Understanding how this system works is essential for operators configuring agent teams and developers extending Paperclip's automation capabilities.

## Database Schema: The `reports_to` Foundation

Every agent in Paperclip stores its reporting relationship at the database level. In [`packages/db/src/schema/agents.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/db/src/schema/agents.ts), the schema defines:

```typescript
reportsTo: uuid("reports_to").references(() => agents.id)

```

This self-referencing foreign key creates the parent-child links that form the tree. Two critical constraints scope and optimize these relationships:

- **Company isolation** — The composite index `agents_company_reports_to_idx` ensures all reporting relationships exist within a single `company_id`
- **Query performance** — Indexing accelerates parent lookups when building the org chart or validating chains

The schema design keeps the model simple: one column, one index, no complex nested structures stored in the database.

## Building the Org Chart Tree

When operators view the org chart, Paperclip materializes the tree on demand. The API endpoint in [`server/src/routes/agents.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/routes/agents.ts) handles `GET /companies/:id/org` by:

1. Fetching all agents for the company
2. Grouping agents by their `reports_to` value
3. Recursively nesting children under each manager
4. Returning a JSON structure with root-level agents and their `children` arrays

The resulting payload looks like this:

```json
[
  {
    "id": "ceo-agent-uuid",
    "name": "Executive Assistant",
    "role": "general",
    "status": "idle",
    "children": [
      {
        "id": "eng-manager-uuid",
        "name": "Engineering Lead",
        "role": "technical",
        "status": "running",
        "children": [...]
      }
    ]
  }
]

```

The **OrgChart** React component in [`ui/src/components/OrgChart.tsx`](https://github.com/paperclipai/paperclip/blob/main/ui/src/components/OrgChart.tsx) consumes this structure to render a collapsible, interactive tree. Each node displays live status badges—`idle`, `running`, `paused`, or `error`—giving operators immediate visibility into organizational health.

## Runtime Validation: Keeping the Tree Healthy

Paperclip enforces tree integrity through aggressive validation in [`server/src/services/agent-invokability.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/agent-invokability.ts). The `checkAgentHealth` function runs at startup and on heartbeat updates, rejecting invalid configurations before they break delegation.

Three failure modes trigger specific error codes:

| Validation | Error Code | Trigger Condition |
|------------|-----------|-------------------|
| **Cycle detection** | `reporting_cycle` | An agent's reporting chain loops back to itself |
| **Depth limits** | `reporting_chain_too_deep` | Hierarchy exceeds maximum nesting depth |
| **Missing manager** | `assignee_reporting_chain` / `creator_reporting_chain` | `reports_to` points to non-existent agent |

These checks guarantee that the org chart remains a **true directed acyclic graph**. Without this validation, delegation logic could enter infinite loops or fail silently when tasks route through broken chains.

## Delegation Semantics: How Tasks Flow

The `reports_to` relationships define the only valid paths for **automatic task delegation**:

- **Upward delegation** — When an agent lacks capability to handle a task, it escalates through `reports_to` links until finding a qualified agent
- **Downward assignment** — Managers can push tasks to subordinates, with visibility limited to their reporting subtree

This directional flow ensures predictable routing. An agent never delegates sideways to peers, and the system prevents any workflow that would require jumping between branches without going through common ancestors.

## Working with the Org Chart Programmatically

### Creating Agents with Reporting Relationships

Assign a manager during agent creation:

```typescript
import { db } from "@/db";
import { agents } from "@/db/schema/agents";

await db.insert(agents).values({
  companyId: "c123",
  name: "QA Engineer",
  role: "general",
  reportsTo: "manager-uuid",   // establishes reporting relationship
});

```

### Fetching and Traversing the Tree

Retrieve the complete hierarchy for visualization or analysis:

```typescript
const org = await fetch(`/api/companies/${companyId}/org`)
  .then(r => r.json());

// Recursively process reporting structure
function renderReportingChain(node, depth = 0) {
  const indent = "  ".repeat(depth);
  console.log(`${indent}${node.name} (${node.status})`);
  node.children?.forEach(child => renderReportingChain(child, depth + 1));
}

org.forEach(root => renderReportingChain(root));

```

### Validating Chain Health

Check for structural problems before relying on an agent:

```typescript
import { checkAgentHealth } from "@/services/agent-invokability";

const health = await checkAgentHealth(agentId);

switch (health.reason) {
  case "reporting_cycle":
    console.error("Circular reporting chain detected");
    break;
  case "reporting_chain_too_deep":
    console.error("Reporting chain exceeds depth limit");
    break;
  case "assignee_reporting_chain":
    console.error("Agent reports to non-existent manager");
    break;
}

```

## Key Source Files

| Path | Purpose |
|------|---------|
| [`packages/db/src/schema/agents.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/db/src/schema/agents.ts) | Schema definition for `reports_to` column and indexes |
| [`server/src/services/agent-invokability.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/agent-invokability.ts) | Health validation, cycle/depth detection |
| [`server/src/routes/agents.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/routes/agents.ts) | `/companies/:id/org` tree assembly endpoint |
| [`ui/src/components/OrgChart.tsx`](https://github.com/paperclipai/paperclip/blob/main/ui/src/components/OrgChart.tsx) | React visualization with status badges |
| [`docs/guides/board-operator/org-structure.md`](https://github.com/paperclipai/paperclip/blob/main/docs/guides/board-operator/org-structure.md) | Operator documentation for org chart semantics |

## Summary

- The **org chart tree structure** in Paperclip relies on a single `reports_to` foreign key per agent to establish reporting relationships
- The database enforces company-scoped relationships through composite indexes in [`packages/db/src/schema/agents.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/db/src/schema/agents.ts)
- Runtime validation in [`server/src/services/agent-invokability.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/agent-invokability.ts) prevents cycles, enforces depth limits, and detects orphaned references
- The `/companies/:id/org` endpoint materializes the tree as nested JSON for consumption by the **OrgChart** UI component
- Task delegation flows strictly upward through `reports_to` links or downward through manager assignment, with no lateral routing permitted

## Frequently Asked Questions

### What happens if I create a circular reporting relationship?

Paperclip rejects the configuration with error code `reporting_cycle`. The `checkAgentHealth` function in [`server/src/services/agent-invokability.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/agent-invokability.ts) traverses each agent's reporting chain and fails if it encounters any agent twice, preventing infinite loops in delegation logic.

### Is there a limit to how deep the org chart can be?

Yes. The validation service enforces a maximum depth limit and returns `reporting_chain_too_deep` when exceeded. This prevents performance degradation during chain traversal and encourages flatter organizational structures that delegate more efficiently.

### Can an agent report to someone in a different company?

No. The database schema and application logic both constrain `reports_to` relationships to agents sharing the same `company_id`. The composite index `agents_company_reports_to_idx` enforces this at the database level, ensuring complete isolation between organizational hierarchies.

### How does the org chart handle agents with no manager?

Agents with `NULL` `reports_to` appear as **root nodes** in the org chart. These are typically executive-level agents or company owners. The tree-building query in [`server/src/routes/agents.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/routes/agents.ts) specifically includes unparented agents in the top-level array returned to the UI.