# Paperclip AI Activity Logging and Mutation Tracking: Complete Architecture Guide

> Learn how Paperclip AI logs every data mutation as an immutable activity event. Understand its complete architecture for tracking user activity across database, API, UI, and CLI layers.

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

---

**Paperclip AI captures every data mutation as an immutable activity event, tracking the responsible user or agent across database, API, UI, and CLI layers using a centralized `recordActivity` service.**

The `paperclipai/paperclip` repository implements a comprehensive **activity logging and mutation tracking** system that serves as the single source of truth for all data changes. This infrastructure records every mutation—from user edits to automated agent actions—in an immutable audit trail while simultaneously powering real-time UI updates, policy enforcement, and security auditing.

## Database Schema and Shared Types

Immutable activity entries persist in PostgreSQL via Drizzle ORM. The schema definition in [`packages/db/src/schema/activity_log.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/db/src/schema/activity_log.ts) defines the `activity_log` table with columns for UUID, timestamp, company ID, event type, and a JSON `details` payload.

Cross-layer type safety comes from [`packages/shared/src/types/activity.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/shared/src/types/activity.ts), which exports the `ActivityEvent` interface and enumerates all activity types (e.g., `issue.updated`, `routine.run`). This ensures consistent data structures across server, client, and CLI implementations.

## Server-Side Activity Tracking

The server layer provides both write and read capabilities through dedicated services in `server/src/services/`.

### Recording Mutations with `recordActivity`

The [`server/src/services/activity.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/activity.ts) file exports the core `recordActivity` helper function. Mutation services invoke this function immediately after successful database writes to create audit records.

The function accepts parameters including `companyId`, `type`, `actorId`, and a `details` JSON payload. When a user comments on an issue, the service logs:

```typescript
await recordActivity({
  companyId,
  type: "issue.commented",
  actorId: ctx.user.id,
  details: {
    issueId,
    commentId,
    commentText,
  },
});

```

### Querying and Pagination

Read operations are handled by [`server/src/services/activity-log.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/activity-log.ts), which provides pagination, filtering, and sorting capabilities. This service aggregates data from the `activity_log` table and optimizes queries for timeline rendering and inbox sorting.

### Responsible User Resolution

Every activity record identifies the **responsible user**—whether a human or the user behind an agent action. The resolution logic inspects the request context to map Bearer tokens to users, handling both direct user tokens and agent-invoked operations. The [`activity-log-responsible-user.test.ts`](https://github.com/paperclipai/paperclip/blob/main/activity-log-responsible-user.test.ts) test suite verifies that the correct `userId` is stored even when actions are performed by an automated agent.

## Activity Flow: From Mutation to UI

The mutation tracking pipeline follows six distinct stages:

1. **Mutation Request** – A user POSTs data or an agent invokes `runRoutine`.

2. **Business Logic Execution** – The handling service processes the change and calls `recordActivity`.

3. **Actor Resolution** – The system determines the `responsibleUserId` from the request context (user token or agent owner).

4. **Persistence** – A new row inserts into `activity_log` with UUID, timestamp, event type, and JSON payload.

5. **Event Propagation** – React Query keys (`queryKeys.activity`) and WebSocket pushes distribute the new activity to connected clients.

6. **Presentation** – UI formatters convert raw JSON into human-readable timeline entries.

This flow ensures that [`server/src/services/activity.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/activity.ts) serves as the single write gateway, while [`server/src/routes/activity.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/routes/activity.ts) exposes REST endpoints like `GET /api/company/:companyId/activity` for consumption.

## Policy Enforcement and Auditing

Activity logs drive security and workflow policies beyond simple auditing.

**Activity-Gated Skips** – The system blocks certain routine runs unless recent external activity exists. This check, implemented in [`routine-run-display.ts`](https://github.com/paperclipai/paperclip/blob/main/routine-run-display.ts), uses the "no_external_activity" flag to prevent automated actions on stale issues.

**Write Denial Tracking** – When mutations fail authorization checks, Paperclip still records the attempt via [`issue-write-denial-activity.ts`](https://github.com/paperclipai/paperclip/blob/main/issue-write-denial-activity.ts). This module logs denied writes with the denied user's ID, timestamp, and attempted action, ensuring security teams can audit failed access attempts.

**Inbox Prioritization** – The inbox view in [`inbox.ts`](https://github.com/paperclipai/paperclip/blob/main/inbox.ts) sorts items by the `activityAt` timestamp, bubbling the most active issues to the top based on recent logged events.

## Frontend Presentation

The UI layer transforms raw activity events into interactive timeline components.

[`ui/src/lib/activity-format.ts`](https://github.com/paperclipai/paperclip/blob/main/ui/src/lib/activity-format.ts) converts `ActivityEvent` objects into formatted rows containing icons, labels, and relative timestamps. For issue-specific views, [`ui/src/lib/issue-timeline-events.ts`](https://github.com/paperclipai/paperclip/blob/main/ui/src/lib/issue-timeline-events.ts) extracts and filters events relevant to ticket histories.

Components consume these formatters through React Query hooks keyed to `queryKeys.activity`, ensuring real-time synchronization with the server state.

## CLI Integration

The command-line interface exposes activity streams for automation and debugging. The [`cli/src/commands/client/activity.ts`](https://github.com/paperclipai/paperclip/blob/main/cli/src/commands/client/activity.ts) file implements the `paperclip activity` command, which queries the same REST endpoints used by the web UI.

Fetch recent events using:

```bash
paperclip activity --company=acme --limit=20

```

The CLI maintains parity with the web interface through [`cli/src/__tests__/activity-parity.test.ts`](https://github.com/paperclipai/paperclip/blob/main/cli/src/__tests__/activity-parity.test.ts), ensuring consistent data formatting across all clients.

## Cross-Layer Consistency

Paperclip enforces strict **Core Engineering Rules** for activity system modifications. Any schema change requires synchronized updates to:

- Database schema ([`packages/db/src/schema/activity_log.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/db/src/schema/activity_log.ts))
- Shared TypeScript types ([`packages/shared/src/types/activity.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/shared/src/types/activity.ts))
- Server services ([`server/src/services/activity.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/activity.ts) and [`activity-log.ts`](https://github.com/paperclipai/paperclip/blob/main/activity-log.ts))
- API routes ([`server/src/routes/activity.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/routes/activity.ts))
- UI query keys and formatters ([`ui/src/lib/activity-format.ts`](https://github.com/paperclipai/paperclip/blob/main/ui/src/lib/activity-format.ts))

End-to-end validation occurs in [`server/src/__tests__/activity-service.test.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/__tests__/activity-service.test.ts) and [`activity-routes.test.ts`](https://github.com/paperclipai/paperclip/blob/main/activity-routes.test.ts).

## Summary

- **Immutable Audit Trail** – Every mutation records to [`packages/db/src/schema/activity_log.ts`](https://github.com/paperclipai/paperclip/blob/main/packages/db/src/schema/activity_log.ts) with full actor attribution via `recordActivity`.
- **Centralized Recording** – The `recordActivity` function in [`server/src/services/activity.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/activity.ts) serves as the sole write gateway for all activity events.
- **Responsible User Tracking** – The system resolves human users behind both direct actions and agent invocations for complete accountability.
- **Policy Integration** – Activity data drives security policies (write denials), workflow gates (external activity checks), and UI sorting (inbox prioritization).
- **Universal Consumption** – Events flow to React-based UIs via [`ui/src/lib/activity-format.ts`](https://github.com/paperclipai/paperclip/blob/main/ui/src/lib/activity-format.ts) and to terminals via [`cli/src/commands/client/activity.ts`](https://github.com/paperclipai/paperclip/blob/main/cli/src/commands/client/activity.ts).

## Frequently Asked Questions

### How does Paperclip AI attribute activity to the correct user when agents perform actions?

The responsible user resolution logic in [`server/src/services/activity-log.ts`](https://github.com/paperclipai/paperclip/blob/main/server/src/services/activity-log.ts) inspects the request context to distinguish between direct user tokens and agent invocations. When an agent performs a mutation, the system traces the Bearer token to the agent's owner and records that `userId` as the `responsibleUserId` in the activity log, ensuring human accountability for automated actions.

### What happens when a mutation is denied by authorization policies?

Even failed mutations generate activity records. The [`issue-write-denial-activity.ts`](https://github.com/paperclipai/paperclip/blob/main/issue-write-denial-activity.ts) module captures denied write attempts with the denied user's ID, timestamp, and attempted action. This creates a complete security audit trail showing both successful changes and blocked access attempts.

### Can I query activity logs programmatically outside the web interface?

Yes. The `paperclip activity` CLI command in [`cli/src/commands/client/activity.ts`](https://github.com/paperclipai/paperclip/blob/main/cli/src/commands/client/activity.ts) exposes the same REST endpoints used by the web UI. You can filter by company, limit results, and output JSON for integration with external monitoring or compliance systems.

### How does the activity system affect issue prioritization in the inbox?

The inbox implementation sorts issues by the `activityAt` timestamp, which updates whenever `recordActivity` logs a new event. This ensures issues with recent comments, status changes, or routine runs bubble to the top of the priority queue automatically.