# How Paperclip Propagates Goal Hierarchy Context to Agents

> Discover how Paperclip propagates goal hierarchy context to agents via a scoped ctx.goals service and RPC host injection. Learn about parent-child relationships.

- Repository: [Paperclip/paperclip](https://github.com/paperclipai/paperclip)
- Tags: internals
- Published: 2026-08-18

---

**Paperclip propagates hierarchical goal context to agents through a scoped `ctx.goals` service that exposes parent-child relationships from the database layer, automatically injected into the agent sandbox via the plugin SDK's RPC host.**

The Paperclip AI platform orchestrates autonomous agents using a tree-structured goal system that maps organizational objectives to executable tasks. Understanding how this **Paperclip goal hierarchy** propagates context to agents requires tracing the flow from the database schema through the service layer to the runtime sandbox environment.

## The Database Schema: Parent-Child Relationships

At the foundation of the hierarchy lies the goals table defined in [`packages/db/src/schema/goal.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/db/src/schema/goal.ts). This schema establishes a self-referential foreign key relationship through the `parentId` field, which enables the construction of an arbitrary depth goal tree.

The schema enforces referential integrity at the database level, ensuring that child goals cannot exist without valid parents. It also defines enumerated fields such as `level` (organizational scope) and `status` (execution state), which agents frequently query to understand their operational context within the hierarchy.

## The Service Layer: GoalService Implementation

The `GoalService` class in [`packages/db/src/client.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/db/src/client.ts) abstracts database operations into a company-scoped API. This service implements four primary methods that power the hierarchy:

- **`list({ companyId, … })`** – Returns paginated goal arrays ordered by the `parentId` relationship, allowing reconstruction of the tree structure client-side.
- **`get(goalId, companyId)`** – Retrieves a single node, including its `parentId` and `level`, with lazy resolution of ancestor chains.
- **`create(payload, companyId)`** – Inserts new goals, accepting an optional `parentId` to establish hierarchy position.
- **`update(goalId, patch, companyId)`** – Modifies goal attributes, including reparenting via `parentId` updates, with automatic cache invalidation for descendant references.

Crucially, every method requires a `companyId` parameter, enforcing strict tenant isolation at the service boundary.

## The Agent Runtime: ctx.goals Injection

When an agent initializes, the system constructs a runtime context object that bridges the service layer and the sandboxed agent code. The interface declared in [`packages/plugins/sdk/src/types.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/sdk/src/types.ts) exposes the goal service as `ctx.goals`, providing agents with typed access to the hierarchy.

The [`packages/plugins/sdk/src/worker-rpc-host.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/sdk/src/worker-rpc-host.ts) file implements the `WorkerHostCallContext` builder. During agent startup, this module instantiates a `GoalService` instance bound to the authenticated company's ID, then injects it into the context object passed to the sandbox. This injection occurs over an RPC boundary, ensuring agents execute in isolated processes while maintaining access to the hierarchical data.

## The Context Propagation Pipeline

The propagation of goal hierarchy context follows a four-stage pipeline that ensures security and data consistency:

1. **Authentication scoping** – Agents authenticate using an API key bound to a specific company. The extracted `companyId` becomes the mandatory scope for all subsequent `ctx.goals` operations, preventing cross-tenant data leakage.

2. **Run initialization** – When a run starts (via CLI or API), the system loads the associated `goalId` from the originating issue or project into the `RunContext`. This ID represents the agent's entry point into the goal tree.

3. **Context assembly** – The worker RPC host in [`packages/plugins/sdk/src/worker-rpc-host.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/sdk/src/worker-rpc-host.ts) assembles the final context. It instantiates `GoalService` with the resolved `companyId` and maps the service methods onto the `ctx.goals` property, preserving the run's specific `goalId` for reference.

4. **Sandbox exposure** – Inside the isolated agent process, the SDK exposes `ctx.goals` as a proxied object. Method calls serialize across the RPC boundary to the host process, which executes the database queries and returns hierarchical data to the agent.

## Working with Goal Hierarchy in Agent Code

Agents interact with the hierarchy through the `ctx.goals` API to discover their operational context or create structured sub-tasks. The following example demonstrates listing root goals, resolving ancestry, and creating child objectives:

```typescript
// packages/plugins/example-agent/src/index.ts
export async function run(ctx: AgentContext) {
  // Retrieve top-level goals (those without parents)
  const rootGoals = await ctx.goals.list({
    companyId: ctx.companyId,
    limit: 50,
    where: (g) => !g.parentId,
  });

  // Resolve full ancestry for the first goal
  const targetGoal = await ctx.goals.get(rootGoals[0].id, ctx.companyId);
  console.log("Parent ID:", targetGoal.parentId);

  // Create a sub-goal under the current run's goal context
  const parentId = ctx.run?.goalId ?? targetGoal.id;
  const subGoal = await ctx.goals.create(
    {
      title: "Automate security audit",
      description: "Run nightly vulnerability scans",
      level: "team",
      status: "planned",
      parentId: parentId,
    },
    ctx.companyId
  );

  return subGoal.id;
}

```

On the server side, the context assembly looks conceptually like this simplified implementation:

```typescript
// server/src/services/agent-context.ts
import { GoalService } from "packages/db/src/client";
import { createWorkerRpcHost } from "packages/plugins/sdk/src/worker-rpc-host";

export async function buildAgentContext(runId: string) {
  const run = await runsDao.get(runId);
  const goalService = new GoalService({ companyId: run.companyId });

  const ctx = {
    companyId: run.companyId,
    goalId: run.goalId,
    goals: {
      list: (opts) => goalService.list({ ...opts, companyId: run.companyId }),
      get: (id) => goalService.get(id, run.companyId),
      create: (payload) => goalService.create(payload, run.companyId),
      update: (id, patch) => goalService.update(id, patch, run.companyId),
    },
  };

  return createWorkerRpcHost(ctx);
}

```

## Summary

- **Database layer** – [`packages/db/src/schema/goal.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/db/src/schema/goal.ts) defines the hierarchical structure via the `parentId` foreign key, enforcing referential integrity for the goal tree.
- **Service abstraction** – [`packages/db/src/client.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/db/src/client.ts) implements `GoalService` with company-scoped CRUD operations that respect the parent-child relationships.
- **Runtime injection** – [`packages/plugins/sdk/src/worker-rpc-host.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/sdk/src/worker-rpc-host.ts) instantiates the service and exposes it as `ctx.goals` within the agent sandbox.
- **Security boundary** – Context propagation requires a valid `companyId` from the agent's authentication token, ensuring complete isolation between organizational tenants.
- **Agent API** – Sandboxed agents manipulate the hierarchy through `ctx.goals.list()`, `get()`, `create()`, and `update()`, with all `parentId` relationships resolved automatically by the underlying service.

## Frequently Asked Questions

### How is the goal hierarchy physically stored in Paperclip?

The hierarchy persists in the `goals` table defined in [`packages/db/src/schema/goal.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/db/src/schema/goal.ts), which uses a `parentId` column referencing the same table's primary key. This self-referential foreign key pattern allows arbitrary nesting depth while maintaining referential integrity at the database level.

### What specific methods does ctx.goals expose to agents?

According to [`packages/plugins/sdk/src/types.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/plugins/sdk/src/types.ts), the `ctx.goals` interface exposes four primary asynchronous methods: `list(options)` for paginated retrieval with filtering, `get(goalId, companyId)` for single-node access, `create(payload, companyId)` for inserting new hierarchical nodes, and `update(goalId, patch, companyId)` for modifying existing goals including reparenting operations.

### How does Paperclip prevent agents from accessing other companies' goals?

Every method in the `GoalService` class requires a mandatory `companyId` parameter that must match the company encoded in the agent's API key. The [`packages/db/src/client.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/db/src/client.ts) implementation uses this parameter to scope all SQL queries, effectively adding a tenant filter to every database operation before data reaches the agent sandbox.

### Can an agent modify its position within the goal hierarchy during execution?

Yes. Agents can call `ctx.goals.update()` with a modified `parentId` field to repurpose themselves or create new child goals under different parents. The `GoalService` automatically handles the foreign key validation and invalidates cached descendant data, ensuring the hierarchy remains consistent across concurrent agent executions.