SPEC 022 — Activity Tracking
| Field | Value |
|---|---|
| Status | DRAFT |
| Priority | P1 — Core Product |
| Backend | equa-server/modules/activity/ |
| Frontend | equa-web/src/modules/actions/ |
1. Feature Purpose
Activity Tracking provides a comprehensive audit trail and action management system for organizations on the Equa platform. It captures three categories of activity: discrete user actions (e.g. cap table edits, document uploads), structured event logs for analytics and compliance, and organizational tasks (e.g. stock option exercises, compliance to-dos). Together these give admins visibility into what happened, when, and by whom — and let stakeholders act on pending items.2. Current State (Verified)
2.1 Actions
| Detail | Value |
|---|---|
| Entity | Actions — records discrete user actions within an organization |
| Scope | Organization-level; each action is tied to a user and an action type |
| Usage | Powers the organization activity feed and feature-request tracking |
2.2 Event Logs
| Detail | Value |
|---|---|
| Entity | EventLogs — generic event journal |
| Payload | data JSON column holds arbitrary event-specific details |
| Usage | Audit trail, analytics pipeline input, compliance reporting |
2.3 Tasks
| Detail | Value |
|---|---|
| Entity | Tasks — tracks actionable items (e.g. option exercises, compliance steps) |
| Composite PK | (entity, type, status) — one active task per entity-type combination |
| Types | TaskType enum (e.g. exercise option, sign agreement, file compliance) |
| Statuses | TaskStatus enum (e.g. pending, in_progress, completed, cancelled) |
2.4 Exercise Option Tracking
| Detail | Value |
|---|---|
| Entity | TasksExerciseOption — captures exercise details for stock option tasks |
| Relationship | Linked to a Task row; stores the selected option hash and share count |
2.5 Backend and Frontend
| Component | Path |
|---|---|
| Backend module | equa-server/modules/activity/ |
| Endpoints | equa-server/modules/api/src/endpoints/activity-endpoints.ts |
| Frontend module | equa-web/src/modules/actions/ |
3. Data Model
Actions
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK |
| organization | uuid | FK → Organizations, NOT NULL |
| type | uuid | FK → action type definition, NOT NULL |
| user | uuid | FK → Users, NOT NULL |
| createdAt | timestamp | DEFAULT now() |
EventLogs
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK |
| type | varchar | NOT NULL — event type identifier (e.g. cap_table.updated, document.uploaded) |
| user | uuid | FK → Users, nullable (null for system-generated events) |
| organization | uuid | FK → Organizations, NOT NULL |
| data | json | nullable — event-specific payload |
| createdAt | timestamp | DEFAULT now() |
Tasks
| Column | Type | Constraints |
|---|---|---|
| entity | uuid | Composite PK part — the target entity (e.g. grant ID, agreement ID) |
| type | TaskType | Composite PK part — enum value identifying the task category |
| status | TaskStatus | Composite PK part — enum value for current state |
| createdAt | timestamp | DEFAULT now() |
| updatedAt | timestamp | nullable |
TasksExerciseOption
| Column | Type | Constraints |
|---|---|---|
| id | uuid | PK |
| option | Hash | NOT NULL — identifies the specific option grant |
| shares | numeric | NOT NULL — number of shares being exercised |
4. API Endpoints
| Method | Path | Auth | Description |
|---|---|---|---|
| GET | /api/v1/organizations/:id/actions | Yes | List organization actions (paginated, filterable by type and user) |
| POST | /api/v1/organizations/:id/actions | Yes | Record a new action |
| GET | /api/v1/organizations/:id/actions/:actionId | Yes | Get action details |
| GET | /api/v1/organizations/:id/events | Yes | List event log entries (paginated, filterable by type and date range) |
| GET | /api/v1/organizations/:id/tasks | Yes | List tasks (filterable by type and status) |
| POST | /api/v1/organizations/:id/tasks | Yes | Create a new task |
| PUT | /api/v1/organizations/:id/tasks/:entity/:type | Yes | Update task status |
| GET | /api/v1/organizations/:id/tasks/exercises | Yes | List exercise option tasks with details |
| POST | /api/v1/organizations/:id/tasks/exercises | Yes | Create an exercise option task |
5. Frontend Components
| Component | Path | Description |
|---|---|---|
| ActionsPage | actions/ActionsPage.tsx | Organization activity feed with filters for action type and user |
| ActionItem | actions/components/ActionItem.tsx | Single action row with timestamp, user avatar, and description |
| TaskList | actions/components/TaskList.tsx | Pending tasks list with status badges and action buttons |
| ExerciseOptionForm | actions/components/ExerciseOptionForm.tsx | Form for submitting stock option exercise requests |
| FeatureRequestForm | actions/components/FeatureRequestForm.tsx | Form for submitting feature requests (tracked as actions) |
| ActivityTimeline | actions/components/ActivityTimeline.tsx | Chronological timeline view of all organization activity |
Frontend Behavior
- Activity feed — Displays a reverse-chronological list of actions with infinite scroll pagination.
- Filter bar — Users can filter by action type, user, and date range; filters update the URL query parameters.
- Task status transitions — Status changes trigger optimistic UI updates with rollback on API failure.
- Exercise option flow — The exercise form validates share counts against the grant’s vested/available balance before submission.
- Real-time updates — Activity feed does not auto-refresh; users pull to refresh or navigate away and back.
6. Business Rules
- Immutable actions — Once recorded, actions cannot be edited or deleted; they serve as an audit trail.
- Immutable event logs — EventLog entries are append-only; no updates or deletes are permitted.
- Composite task uniqueness — The
(entity, type, status)composite primary key ensures only one active task of a given type exists per entity. Status transitions create new rows rather than updating in place. - Task status machine — Tasks follow a defined state machine:
pending → in_progress → completedorpending → cancelled. Backward transitions are not allowed. - Exercise validation —
TasksExerciseOption.sharesmust not exceed the vested and unexercised balance of the referenced option grant. - Organization scoping — All activity queries are scoped to the requesting user’s organization; cross-org queries are not supported.
- System events — EventLogs with
user = nullrepresent system-generated events (e.g. scheduled jobs, automated processes). - Event type conventions — Event types follow
resource.actionnaming (e.g.cap_table.updated,document.uploaded,member.invited). - Action type registry — Action types are defined by UUID references; new types must be registered before use.
- Feature requests — Feature request actions are a special action type that collects user feedback; they appear in the admin panel (SPEC 019) for review.
7. Acceptance Criteria
- Organization activity feed displays actions in reverse chronological order
- Actions can be filtered by type, user, and date range
- New actions are recorded with correct user, organization, and type references
- Actions are immutable — no edit or delete endpoints exist
- Event logs capture structured event data with correct type and payload
- Event logs are append-only with no update or delete capability
- Tasks display with correct status badges (pending, in_progress, completed, cancelled)
- Task status transitions follow the defined state machine
- Backward status transitions are rejected by the API
- Exercise option tasks validate share count against available vested balance
- Exercise option form prevents submission when shares exceed available balance
- Feature requests are recorded as actions and visible in admin panel
- All activity queries are scoped to the requesting user’s organization
- Pagination works correctly for large activity feeds (100+ items)
8. Risks
| Risk | Impact | Mitigation |
|---|---|---|
| High-volume event logging on active organizations | Database bloat, slow queries | Add time-based partitioning on EventLogs; archive old entries to cold storage |
| Composite PK on Tasks prevents status history | Cannot audit task state transitions over time | Log status changes to EventLogs before updating Tasks; consider adding a TaskHistory table |
| Exercise validation race condition | Two concurrent exercises exceed available shares | Use database-level locking (SELECT FOR UPDATE) on the option grant during exercise creation |
| No real-time feed updates | Users see stale activity data | Document manual refresh requirement; consider WebSocket or polling in future iteration |
| Action type registry requires pre-registration | Friction when adding new action types | Provide a seed script; validate type UUIDs at startup |
| EventLogs.data is unstructured JSON | Inconsistent payloads, difficult querying | Define JSON schemas per event type; validate payloads before insert |
| Cross-org data leak via malformed query | Sensitive activity visible to wrong organization | Enforce organization scoping in repository layer; add integration tests for isolation |
| Large exercise option numeric values | Precision loss on share counts | Use numeric type (arbitrary precision); validate reasonable bounds on input |