How Issues (Work Items) Are Structured in the Plane Database

Plane stores every work item as a rich relational object defined by the TBaseIssue interface in packages/types/src/issues/issue.ts, with core attributes stored in the issues_issue table and auxiliary tables handling many-to-many relationships like labels, assignees, and modules.

Understanding how issues are structured in the Plane database is essential for developers integrating with the open-source project management platform. The schema follows a normalized relational design where the base issue record maintains essential fields, while separate junction tables manage complex relationships. This architecture supports hierarchical work items, sprint cycles, modular grouping, and complete activity auditing.

Core Issue Schema – The TBaseIssue Interface

The foundation of how issues are structured in Plane resides in the TBaseIssue type definition located in packages/types/src/issues/issue.ts. This interface maps directly to columns in the issues_issue table and contains the essential atomic fields every work item requires.

Key properties include:

  • id – UUID primary key for the issue (line 45)
  • sequence_id – Integer used for ordering within a project (line 46)
  • name – The issue title displayed in the UI (line 48)
  • sort_order – UI-side weight for manual ordering (line 49)
  • state_id – Foreign key to states_state.id representing the current workflow state (line 51)
  • priority – Enum value (urgent, high, medium, low, none) (line 52)
  • project_id – Foreign key to projects_project.id indicating ownership (line 61)

The interface also captures scheduling and lifecycle metadata:

  • start_date, target_date, completed_at, archived_at – Optional datetime fields for project planning (lines 69-72)
  • is_draft – Boolean flag indicating visibility status before publication (line 77)
  • is_epic and is_intake – Optional booleans for higher-level categorization (lines 78-79)
  • created_by / updated_by – User IDs tracking the last mutation (lines 74-75)

Extended Issue Model and Relationships

The TIssue type extends TBaseIssue with optional richer data used by the frontend, including description_html for rich-text content and convenience fields like state__group for logical state bucketing.

Many-to-Many Relationships

Plane’s database structure uses junction tables to manage complex associations without denormalizing the core issue table:

  • Labels – Stored via label_ids array referencing issue_labels.id (line 53), implemented through the issues_issue_labels junction table
  • Assignees – Array of user IDs (assignee_ids) linking to users_user.id via the issues_issue_assignees table (line 54)
  • Modules – module_ids array connecting to modules_module.id through issues_issue_modules (line 64)
  • Cycles – Single foreign key cycle_id pointing to cycles_cycle.id via issues_issue_cycles (line 63)

Nested Objects and Attachments

For performance optimization, Plane maintains pre-computed counts and nested object arrays:

  • sub_issues_count – Cached count of child issues for hierarchical views (line 57)
  • attachment_count and link_count – Pre-computed metrics for UI rendering (lines 58-59)
  • issue_attachments, issue_link, issue_relation – Collections of related objects stored in issues_issue_attachments, issues_issue_links, and issues_issue_relations respectively

Parent-Child Hierarchies

The schema supports recursive work item structures through the parent_id field (line 62), a self-referencing foreign key to issues_issue.id. The API returns a shallow parent object containing base fields for quick access without requiring additional queries.

Activity and Audit Trail

Every mutation creates an immutable record defined by the IIssueActivity interface in packages/types/src/issues.ts. This activity log enables full audit trails and history views throughout the application.

Key activity fields include:

  • actor / actor_detail – The user performing the action
  • verb – Action type such as "created", "updated", or "commented"
  • field, old_value, new_value – Delta information tracking specific field changes
  • comment / comment_html – Optional payload for discussion threads

Database Implementation Details

The concrete database schema is established in migration 0074_deploy_board_and_project_issues.py located at apps/api/plane/db/migrations/. This migration introduces the project_id, cycle_id, and module_ids columns that support the relational structure described in the TypeScript interfaces.

The Django ORM layer (implemented in apps/api/plane/models/issue.py) mirrors these type definitions, ensuring type safety between the PostgreSQL backend and the TypeScript frontend.

Practical Code Examples

When working with the Plane API, developers interact with these structures through strongly-typed payloads:

Creating a New Issue

// Payload sent to POST /api/v1/projects/{project_id}/issues/
const newIssue: Partial<TIssue> = {
  name: "Implement OAuth login",
  description_html: "<p>We need OAuth for SSO integration.</p>",
  project_id: "proj_123",
  priority: "high",
  assignee_ids: ["user_45", "user_67"],
  label_ids: ["label_bug", "label_frontend"],
  state_id: "state_todo",
  type_id: "issue_type_feature"
};

Bulk Updating Issues

// Moving multiple issues to a new sprint cycle
const bulkPayload = {
  issue_ids: ["issue_1", "issue_2", "issue_3"],
  properties: { 
    cycle_id: "cycle_2024_q3",
    state_id: "state_in_progress"
  }
};

Parsing Sub-Issue Responses

function getSubIssues(response: ISubIssueResponse): TIssue[] {
  // response.state_distribution provides quick stats
  console.log(response.state_distribution);
  // response.sub_issues contains full TIssue objects
  return response.sub_issues;
}

Summary

  • Base Structure – The TBaseIssue interface in packages/types/src/issues/issue.ts defines the core schema mapping to the issues_issue table with essential fields like sequence_id, state_id, and project_id.
  • Relational Design – Junction tables (issues_issue_labels, issues_issue_assignees, issues_issue_modules) handle many-to-many relationships while keeping the base table normalized.
  • Hierarchy Support – Self-referencing parent_id enables nested work item trees with pre-computed sub_issues_count for performance.
  • Audit Trail – The IIssueActivity interface captures every mutation with actor details and field deltas.
  • Type Safety – Migration 0074_deploy_board_and_project_issues.py ensures the PostgreSQL schema aligns with the TypeScript type definitions.

Frequently Asked Questions

What table stores the primary issue data in Plane?

The primary issue data resides in the issues_issue table, mapped by the TBaseIssue TypeScript interface. This table contains core fields including id, name, sequence_id, state_id, and project_id, while auxiliary tables handle relationships like labels and assignees.

How does Plane handle issue hierarchies and sub-tasks?

Plane implements hierarchies through a self-referencing foreign key. The parent_id field in TBaseIssue links to another record in issues_issue, while the sub_issues_count column maintains a cached count of children for quick UI rendering without recursive queries.

Where is the activity history for issues stored?

Activity history is defined by the IIssueActivity interface and persisted in the issues_issue_activity table. Each record captures the actor, verb (action type), and optional delta fields (old_value, new_value) to provide complete audit trails for compliance and debugging.

What migration created the current issue schema structure?

The migration 0074_deploy_board_and_project_issues.py established the current schema, adding critical columns like project_id, cycle_id, and module_ids that support Plane’s board views and sprint planning features.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →