How the Org Chart Tree Structure Works with Reporting Relationships in Paperclip
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, the schema defines:
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_idxensures all reporting relationships exist within a singlecompany_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 handles GET /companies/:id/org by:
- Fetching all agents for the company
- Grouping agents by their
reports_tovalue - Recursively nesting children under each manager
- Returning a JSON structure with root-level agents and their
childrenarrays
The resulting payload looks like this:
[
{
"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 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. 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_tolinks 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:
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:
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:
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 |
Schema definition for reports_to column and indexes |
server/src/services/agent-invokability.ts |
Health validation, cycle/depth detection |
server/src/routes/agents.ts |
/companies/:id/org tree assembly endpoint |
ui/src/components/OrgChart.tsx |
React visualization with status badges |
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_toforeign key per agent to establish reporting relationships - The database enforces company-scoped relationships through composite indexes in
packages/db/src/schema/agents.ts - Runtime validation in
server/src/services/agent-invokability.tsprevents cycles, enforces depth limits, and detects orphaned references - The
/companies/:id/orgendpoint materializes the tree as nested JSON for consumption by the OrgChart UI component - Task delegation flows strictly upward through
reports_tolinks 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 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 specifically includes unparented agents in the top-level array returned to the UI.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →