Paperclip AI Activity Logging and Mutation Tracking: Complete Architecture Guide
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 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, 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 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:
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, 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 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:
-
Mutation Request – A user POSTs data or an agent invokes
runRoutine. -
Business Logic Execution – The handling service processes the change and calls
recordActivity. -
Actor Resolution – The system determines the
responsibleUserIdfrom the request context (user token or agent owner). -
Persistence – A new row inserts into
activity_logwith UUID, timestamp, event type, and JSON payload. -
Event Propagation – React Query keys (
queryKeys.activity) and WebSocket pushes distribute the new activity to connected clients. -
Presentation – UI formatters convert raw JSON into human-readable timeline entries.
This flow ensures that server/src/services/activity.ts serves as the single write gateway, while 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, 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. 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 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 converts ActivityEvent objects into formatted rows containing icons, labels, and relative timestamps. For issue-specific views, 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 file implements the paperclip activity command, which queries the same REST endpoints used by the web UI.
Fetch recent events using:
paperclip activity --company=acme --limit=20
The CLI maintains parity with the web interface through 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) - Shared TypeScript types (
packages/shared/src/types/activity.ts) - Server services (
server/src/services/activity.tsandactivity-log.ts) - API routes (
server/src/routes/activity.ts) - UI query keys and formatters (
ui/src/lib/activity-format.ts)
End-to-end validation occurs in server/src/__tests__/activity-service.test.ts and activity-routes.test.ts.
Summary
- Immutable Audit Trail – Every mutation records to
packages/db/src/schema/activity_log.tswith full actor attribution viarecordActivity. - Centralized Recording – The
recordActivityfunction inserver/src/services/activity.tsserves 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.tsand to terminals viacli/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 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 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 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.
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 →