Skip to main content
Reference

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:

  1. Reads .pending/lane-*.json files
  2. Reads existing activity-log.json (or starts with [] if it doesn't exist)
  3. Merges all entries and sorts by timestamp ascending
  4. Writes the result back to activity-log.json
  5. 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

Search Framework Explorer

Search agents, skills, and standards