Activity logging standards
Structured JSON activity log for audit trails. Schema, fields, and weekly retrospective process.
Activity logging standards
Purpose: Defines the schema, standard actions, and implementation patterns for structured activity logging across all Framework applications, providing audit trails, debugging context, and user transparency.
[!NOTE] Implementation status: Schema and patterns defined below. Not yet implemented in any project. When a project requires activity logging, use this as the starting architecture and update with real-world learnings.
A consistent, auditable record of everything that happens in Framework applications.
Why this matters
Activity logs provide:
- User transparency — Users see what happened and when
- Debugging — Trace issues back to specific actions
- Audit trail — Compliance, security, accountability
- Support — Answer "what happened to my X?" instantly
Schema
// activity_logs table
{
id: string // UUID
timestamp: datetime // When it happened (UTC)
// Actor (who did it)
actor_type: string // "user" | "system" | "api_key"
actor_id: string // User ID or system identifier
actor_name: string // Denormalized: "Jane Smith"
actor_email: string // Denormalized: "jane@example.com"
// Action (what they did)
action: string // Verb: "created", "updated", "deleted"
category: string // Grouping: "door", "user", "billing"
description: string // Human-readable: "unlocked the front door"
// Target (what they did it to)
target_type: string // "door", "user", "membership"
target_id: string // Record ID
target_name: string // Denormalized: "Front Door"
// Changes (from/to values)
changes: jsonb // Array of { field, from, to }
// Context
org_id: string // Multi-tenancy
metadata: jsonb // Extra context: IP, user agent, etc.
}
Standard actions
Universal actions
| Action | When to use |
|---|---|
created |
New record created |
updated |
Record modified (include changes) |
deleted |
Record permanently removed |
archived |
Record soft-deleted |
restored |
Record unarchived |
User/team actions
| Action | When to use |
|---|---|
invited |
User invited to org/team |
joined |
User accepted invite |
removed |
User removed from org/team |
role_changed |
User role updated |
Access/permission actions
| Action | When to use |
|---|---|
granted |
Permission granted |
revoked |
Permission revoked |
unlocked |
Physical access (door, etc.) |
locked |
Physical access revoked |
Implementation
Automatic logging (Prisma middleware)
Standard CRUD operations are logged automatically via createActivityClient:
const db = createActivityClient({ userId, orgId });
const door = await db.door.update({...});
// Activity log created automatically!
Manual logging (custom actions)
For non-CRUD actions:
await logActivity({
actor: currentUser,
action: 'unlocked',
category: 'door',
description: `unlocked ${door.name}`,
targetType: 'door',
targetId: door.id,
targetName: door.name,
metadata: { method: 'pin_code' },
});
What NOT to log
Never log:
- Passwords or password hashes
- API keys or tokens
- Credit card numbers
- Full SSN or government IDs
- Session tokens
Retention
- Default — 90 days
- Compliance requirements — Extend as needed
- Archival — Move to cold storage after retention period
Pipeline telemetry
The /go pipeline logs a completion entry to activity-log.json after every issue. Pipeline entries use category: "pipeline" and action: "completed", consistent with the existing schema.
Metadata schema
// metadata for category: "pipeline", action: "completed"
{
issue_id: string // Linear issue ID, e.g. "ORC-332"
branch: string // Git branch name
pr_number: number | null // GitHub PR number
wall_ms: number // Wall-clock duration in milliseconds
tokens_total: number | null // Total tokens across all agents (null until accumulation exists)
agents: string[] // Agent roles invoked, e.g. ["backend-architect", "simplicity-advocate"]
skills: string[] // Skills loaded, e.g. ["shadcn-ui", "video"]
tools: string[] // Tool names used, e.g. ["Grep", "Edit", "Bash"]
plugins: string[] // MCP plugins used, e.g. ["playwright", "fff"]
agents_detail: Array<{ // Per-agent tool/skill/plugin breakdown (self-reported)
role: string // Agent subagent_type, e.g. "backend-architect"
tokens: number | null // Total tokens from <usage> block (null if not reported)
skills: string[] // Skills loaded, e.g. ["shadcn-ui"]
tools: string[] // Tools with counts, e.g. ["Read×4", "Edit×2"]
plugins: string[] // MCP plugins called, e.g. ["playwright"]
}> | null // null if no agents reported
}
Example entry
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2026-03-17T14:32:00Z",
"actor_type": "system",
"actor_id": "claude-orchestrator",
"actor_name": "Claude Orchestrator",
"actor_email": "",
"action": "completed",
"category": "pipeline",
"description": "Completed /go pipeline for ORC-332",
"target_type": "issue",
"target_id": "ORC-332",
"target_name": "Add issue-level telemetry to /go pipeline",
"changes": [],
"org_id": "",
"metadata": {
"issue_id": "ORC-332",
"branch": "feat/pipeline-telemetry",
"pr_number": 117,
"wall_ms": 847200,
"tokens_total": null,
"agents": ["backend-architect", "simplicity-advocate"],
"skills": ["project-context"],
"tools": ["Grep", "Edit", "Read", "Bash"],
"plugins": ["fff"],
"agents_detail": [
{
"role": "backend-architect",
"tokens": 42150,
"skills": ["project-context"],
"tools": ["Read×6", "Grep×4", "Bash×2"],
"plugins": ["fff"]
},
{
"role": "simplicity-advocate",
"tokens": 18340,
"skills": [],
"tools": ["Read×3"],
"plugins": []
}
]
}
}
Example queries
# All pipeline completions
jq '[.[] | select(.category == "pipeline")]' activity-log.json
# Average wall-clock duration across all issues
jq '[.[] | select(.category == "pipeline") | .metadata.wall_ms] | add / length' activity-log.json
# Issues that used a specific agent
jq '[.[] | select(.category == "pipeline" and (.metadata.agents | index("frontend-developer")))]' activity-log.json
# Issues sorted by wall-clock duration (slowest first)
jq '[.[] | select(.category == "pipeline")] | sort_by(-.metadata.wall_ms) | .[:5]' activity-log.json
# Per-agent tool/skill breakdown for a specific issue
jq '.[] | select(.metadata.issue_id == "ORC-334") | .metadata.agents_detail[] | {role, skills, tools}' activity-log.json
Accumulation
The orchestrator populates wall_ms, agents, skills, tools, and plugins directly — these are always known at pipeline end. tokens_total is accumulated by summing per-agent <usage> blocks during the execute loop; it is null if no agents reported usage. agents_detail entries are populated by parsing each agent's ## Work Summary section during the execute loop; each entry carries tokens (from the agent's <usage> block), plus skills, tools, and plugins (from the ## Work Summary prose). agents_detail is null if no agents reported a Work Summary.
Parallel lane accumulation
When /batch runs multiple issues in parallel worktrees, each lane cannot write directly to activity-log.json in the main tree (file conflict risk). Instead, each lane writes its telemetry to a pending changeset file that is merged after all lanes complete.
Detection mechanism: /batch sets the BATCH_LANE environment variable when spawning each claude session. When BATCH_LANE is set, /go step 11b writes to .pending/lane-$BATCH_LANE-log.json instead of activity-log.json. When BATCH_LANE is unset (normal /go run), behavior is unchanged — writes directly to activity-log.json.
Lane log format: .pending/lane-N-log.json is a JSON array using the same schema as activity-log.json. Each entry is a full telemetry object with timestamp, category, action, and all pipeline metadata fields.
Consolidation: After all lanes in a wave merge successfully, /batch calls:
bash tools/scripts/consolidate-activity-log.sh
The script:
- Reads
.pending/lane-*.jsonfiles - Reads existing
activity-log.json(or starts with[]if it doesn't exist) - Merges all entries and sorts by
timestampascending - Writes the result back to
activity-log.json - Removes the
.pending/directory
The .pending/ directory is gitignored — lane logs are transient and never committed.
Dry run: Pass --dry-run to inspect what would be merged without modifying any files:
bash tools/scripts/consolidate-activity-log.sh --dry-run
UI components
Use Shadcnblocks Premium components:
- Activity Feed — Primary pattern for user-facing logs
- Timeline — For sequential processes
- Table — For admin views with filtering/export
Activity Logging Standards v1.2 -- March 2026