How Paperclip Org-Chart Hierarchy Uses `reportsTo` and Role Permissions

Paperclip implements a directed tree structure where reportsTo defines manager relationships and role-based permissions resolve fallback authority when reporting chains break.

The paperclipai/paperclip repository models every organization as a hierarchy of agents—both human users and AI systems—connected through parent-child relationships. Each agent carries a reportsTo field pointing to their manager and a role string that determines permission grants in the system.

Building the Org-Chart Tree

Agent Definition and reportsTo

When creating an agent, the payload accepts reportsTo as an optional agent ID. A null value designates the agent as a root node—typically the CEO.

// ui/src/lib/new-agent-hire-payload.ts
{
  name: "Backend Engineer",
  role: "engineer",
  reportsTo: "mgr-1",   // manager's agent-id; null for root
}

The reportsTo field is typed as reportsTo?: string | null in the creation payload, making the hierarchy relationship explicit at the data layer.

Computing Agent Order

The UI renders agents in a tree-respecting sequence using orderAgents() in ui/src/lib/agent-order.ts. This function walks each agent's reportsTo link and positions children directly after their manager (line 123).

// ui/src/lib/agent-order.ts
export function orderAgents(agents: Agent[]): Agent[] {
  const byId = new Map(agents.map(a => [a.id, a]));
  return agents.sort((a, b) => {
    const aParent = a.reportsTo && byId.has(a.reportsTo) ? a.reportsTo : null;
    const bParent = b.reportsTo && byId.has(b.reportsTo) ? b.reportsTo : null;
    return aParent === bParent ? 0 : aParent ? -1 : 1;
  });
}

Visualizing the Hierarchy

The OrgChart page renders the ordered list with connector lines drawn from each child to their reportsTo manager. Status dots indicate runtime state (running, idle, error) using color tokens defined in ui/src/index.css.

Role-Based Permission System

Special Roles with Authority

Not all roles carry permission weight. Paperclip treats three categories distinctly:

  • ceo / cto — Top-level roles used for fallback assignments. When a direct manager is missing, the system queries for active agents with these roles.
  • general — Default role for standard agents; no elevated permissions.
  • Free-form roles — Used for UI grouping, skill-catalog filtering, and display only.

Server-Side Permission Enforcement

Permission checks query the agents.role column directly from the database. In server/src/services/recovery/service.ts (lines 1822-1826), the recovery service falls back to CEO/CTO candidates when the reporting chain fails:

// server/src/services/recovery/service.ts
if (!candidateIds.length) {
  // Look for any active CTO or CEO if the direct manager is missing
  const roleCandidates = await db
    .where(and(
      eq(agents.companyId, input.run.companyId),
      inArray(agents.role, ["cto", "ceo"])
    ))
    .orderBy(
      sql`case when ${agents.role} = 'cto' then 0 else 1 end`,
      asc(agents.createdAt)
    );
  candidateIds.push(...roleCandidates.map(a => a.id));
}

Human Role Normalization

Human members receive normalized roles through server/src/services/company-member-roles.ts. The grantsForHumanRole mapping treats ceo and cto specially, granting them inherited permissions unavailable to other roles.

Practical Hierarchy Operations

Task Assignment with Fallback

When Paperclip creates a new task, it attempts assignment in two stages:

  1. Assign to the agent's direct manager via reportsTo
  2. If unavailable, fall back to any active cto or ceo

This guarantees every task maintains ownership within the reporting line.

Recovery and Liveness Detection

The recovery service walks the reportsTo chain to find a live manager for failing agents. It terminates traversal at null (root) or upon cycle detection—implemented in server/src/services/recovery/issue-graph-liveness.ts (lines 342-345).

UI Navigation

The Agent detail view (ui/src/pages/AgentDetail.tsx, lines 909-910) surfaces hierarchy relationships:

// ui/src/pages/AgentDetail.tsx
const reportsToAgent = (allAgents ?? []).find(a => a.id === agent?.reportsTo);

return (
  <>
    {agent.reportsTo && (
      <Link to={agentUrl(reportsToAgent!)} className="hover:underline">
        <Identity name={reportsToAgent!.name} size="sm" />
      </Link>
    )}
  </>
);

Direct reports display by filtering where agent.reportsTo === current.id.

Key Source Files

Purpose File Path
Agent creation payload with reportsTo ui/src/lib/new-agent-hire-payload.ts
Tree-aware ordering algorithm ui/src/lib/agent-order.ts
Manager link and reporting display ui/src/pages/AgentDetail.tsx
Visual org-chart page ui/src/pages/OrgChart.tsx
Recovery service with role fallback server/src/services/recovery/service.ts
Cycle detection and liveness server/src/services/recovery/issue-graph-liveness.ts
Human role permission mapping server/src/services/company-member-roles.ts
Public reportsTo API documentation skills/paperclip/references/api-reference.md

Summary

  • reportsTo stores the manager's agent-id, forming a directed tree with null indicating root nodes
  • Agent ordering in ui/src/lib/agent-order.ts computes visual hierarchy by walking reportsTo links
  • ceo and cto roles receive special treatment as fallback authorities when reporting chains break
  • Server-side enforcement queries agents.role directly, with explicit handling in recovery and permission services
  • Cycle detection prevents infinite traversal when walking the hierarchy

Frequently Asked Questions

What happens if an agent's reportsTo manager is deleted?

Paperclip falls back to any active agent with role cto or ceo, ordered by role priority (CTO first) then creation date. This ensures operations like task assignment always have a valid owner.

Can an agent report to multiple managers?

No. The reportsTo field accepts a single string ID or null. The data model enforces a strict tree structure without multiple parents.

How does Paperclip prevent circular reporting relationships?

The recovery service in server/src/services/recovery/issue-graph-liveness.ts detects cycles during traversal. It tracks visited agent IDs and terminates if it encounters a previously seen node.

Where is the reportsTo field documented in the API?

The public API reference at skills/paperclip/references/api-reference.md documents the reportsTo property, confirming it as an optional string accepting agent identifiers or null for root-level agents.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →