タスク コア 仕様書
タスク CRUD・カスタムステータス・担当者・ラベル・マイルストーン・依存関係
ステータス: Draft / 作成日: 2026-05-27 PR #1 — 依存: なし(最初に実装)
1. 概要
タスク管理の基盤となるコア機能。すべての後続 PR がこの PR に依存する。
含む機能:
- タスク CRUD + プロジェクト内連番 ID(
KEY-N形式。例:ENG-42) - カスタムステータス(プロジェクト単位)
- 担当者(Primary / Secondary)
- 優先順位・締切(Soft / Hard)・進捗率
- 親子タスク・ブロッキング依存関係
- ラベル(プロジェクト単位 + export / import)
- マイルストーン
2. データモデル
2.1 project_task_counters
2.2 project_statuses
pub struct Model {
pub id: Uuid,
pub project_id: Uuid,
pub name: String,
pub color: String, // hex (#3b82f6)
pub position: i16, // 表示順
pub is_default: bool, // タスク作成時のデフォルト(プロジェクト内で 1 つのみ)
pub is_done_state: bool, // このステータス = 完了とみなす(複数可)
pub created_at: DateTimeUtc,
}
プロジェクト作成時に
Backlog(is_default=true)/In Progress/In Review/Done(is_done_state=true)を自動挿入する。
2.3 tasks
pub struct Model {
pub id: Uuid,
pub project_id: Uuid,
pub seq_id: i32,
pub title: String,
pub description: Option<String>,
pub status_id: Uuid,
pub priority: TaskPriority,
pub progress_pct: i16,
pub parent_task_id: Option<Uuid>,
pub milestone_id: Option<Uuid>,
pub soft_deadline: Option<DateTimeUtc>,
pub hard_deadline: Option<DateTimeUtc>,
pub estimated_minutes: Option<i32>,
pub is_archived: bool,
pub created_by: Uuid,
pub created_at: DateTimeUtc,
pub updated_at: DateTimeUtc,
pub deleted_at: Option<DateTimeUtc>,
}
2.4 task_assignees
2.5 task_relations
循環依存はリレーション追加時にサーバー側 BFS で検出し
409 Conflict。
2.6 milestones
2.7 labels(既存テーブルに project_id 追加)
ALTER TABLE labels ADD COLUMN project_id UUID REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE labels ADD CONSTRAINT labels_project_name_unique UNIQUE (project_id, name);
2.8 task_labels
3. マイグレーション
-- プロジェクトキー(GitHub 連携での KEY-N 形式に使用)
ALTER TABLE projects
ADD COLUMN key VARCHAR(10) NOT NULL DEFAULT '',
ADD CONSTRAINT projects_key_format CHECK (key ~ '^[A-Z][A-Z0-9]{1,9}$'),
ADD CONSTRAINT projects_key_tenant_unique UNIQUE (tenant_id, key);
keyはテナント内で一意の英大文字識別子(例:ENG、BACK、WEB)。 プロジェクト作成 API でユーザーが指定するか、プロジェクト名から自動生成する(例: "Backend API" →BACK)。
CREATE TABLE project_task_counters (
project_id UUID PRIMARY KEY REFERENCES projects(id) ON DELETE CASCADE,
last_seq INT NOT NULL DEFAULT 0
);
CREATE TABLE project_statuses (
id UUID PRIMARY KEY,
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
name VARCHAR(100) NOT NULL,
color VARCHAR(7) NOT NULL,
position SMALLINT NOT NULL,
is_default BOOLEAN NOT NULL DEFAULT false,
is_done_state BOOLEAN NOT NULL DEFAULT false,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (project_id, name)
);
CREATE TABLE milestones (
id UUID PRIMARY KEY,
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL,
description TEXT,
due_date DATE NOT NULL,
created_by UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE TABLE tasks (
id UUID PRIMARY KEY,
project_id UUID NOT NULL REFERENCES projects(id) ON DELETE CASCADE,
seq_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
description TEXT,
status_id UUID NOT NULL REFERENCES project_statuses(id),
priority VARCHAR NOT NULL DEFAULT 'medium',
progress_pct SMALLINT NOT NULL DEFAULT 0 CHECK (progress_pct BETWEEN 0 AND 100),
parent_task_id UUID REFERENCES tasks(id) ON DELETE SET NULL,
milestone_id UUID REFERENCES milestones(id) ON DELETE SET NULL,
soft_deadline TIMESTAMPTZ,
hard_deadline TIMESTAMPTZ,
estimated_minutes INT CHECK (estimated_minutes > 0),
is_archived BOOLEAN NOT NULL DEFAULT false,
created_by UUID NOT NULL REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
deleted_at TIMESTAMPTZ,
UNIQUE (project_id, seq_id),
CONSTRAINT soft_before_hard CHECK (
soft_deadline IS NULL OR hard_deadline IS NULL OR soft_deadline <= hard_deadline
)
);
CREATE TABLE task_assignees (
id UUID PRIMARY KEY,
task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role VARCHAR NOT NULL DEFAULT 'secondary',
assigned_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (task_id, user_id)
);
CREATE TABLE task_relations (
id UUID PRIMARY KEY,
blocker_task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
blocked_task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE (blocker_task_id, blocked_task_id),
CHECK (blocker_task_id <> blocked_task_id)
);
CREATE TABLE task_labels (
task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE,
label_id UUID NOT NULL REFERENCES labels(id) ON DELETE CASCADE,
PRIMARY KEY (task_id, label_id)
);
ALTER TABLE labels ADD COLUMN project_id UUID REFERENCES projects(id) ON DELETE CASCADE;
ALTER TABLE labels ADD CONSTRAINT labels_project_name_unique UNIQUE (project_id, name);
CREATE INDEX idx_tasks_project ON tasks(project_id) WHERE deleted_at IS NULL;
CREATE INDEX idx_tasks_status ON tasks(status_id);
CREATE INDEX idx_tasks_parent ON tasks(parent_task_id) WHERE parent_task_id IS NOT NULL;
4. API
URL 基底パス: /v1/tenants/{tenant_id}/projects/{project_id}
4.1 タスク
GET /tasks クエリパラメータ
POST /tasks リクエスト
{
"title": "OAuth 対応を実装する",
"description": "## 概要\nGitHub Apps を使って…",
"priority": "high",
"status_id": "uuid",
"soft_deadline": "2026-06-01T00:00:00Z",
"hard_deadline": "2026-06-10T00:00:00Z",
"estimated_minutes": 180,
"assignees": [{ "user_id": "uuid", "role": "primary" }],
"label_ids": ["uuid"],
"milestone_id": "uuid",
"parent_task_id": null
}
GET /tasks レスポンス
{
"tasks": [{
"id": "uuid", "seq_id": 42,
"title": "OAuth 対応を実装する",
"status": { "id": "uuid", "name": "In Progress", "color": "#3b82f6" },
"priority": "high", "progress_pct": 40,
"assignees": [{ "user_id": "uuid", "role": "primary" }],
"soft_deadline": "2026-06-01T00:00:00Z",
"hard_deadline": "2026-06-10T00:00:00Z",
"subtask_count": 3,
"created_at": "2026-05-27T10:00:00Z"
}],
"total": 42
}
4.2 担当者
4.3 依存関係
POST リクエスト — どちらのタスクからでも設定可能:
{ "type": "blocks", "target_task_id": "uuid" }
// 同じ関係を反対側から設定する場合(結果は同一レコード)
{ "type": "blocked_by", "target_task_id": "uuid" }
GET レスポンス:
{
"subtasks": [{ "id": "uuid", "seq_id": 60, "title": "..." }],
"blocks": [{ "id": "uuid", "seq_id": 55, "title": "...", "status": "blocked", "relation_id": "uuid" }],
"blocked_by": [{ "id": "uuid", "seq_id": 38, "title": "...", "status": "done", "relation_id": "uuid" }]
}
4.4 カスタムステータス
4.5 マイルストーン
GET /milestones/{id} レスポンス:
{
"id": "uuid", "name": "v2.0 リリース", "due_date": "2026-07-01",
"progress_pct": 33,
"task_counts": { "total": 12, "done": 4 }
}
4.6 ラベル
エクスポート形式:
{
"version": 1,
"labels": [
{ "name": "bug", "color": "#e11d48", "description": "不具合" }
]
}
5. アクセス制御
6. フロントエンド(Phase B)
ページ
/tenants/{tid}/projects/{pid}/tasks # 一覧(カンバン / リスト / テーブル切替)
/tenants/{tid}/projects/{pid}/tasks/{id} # 詳細
/tenants/{tid}/projects/{pid}/milestones # マイルストーン一覧
/tenants/{tid}/projects/{pid}/labels # ラベル管理
カンバンボード
カスタムステータスが列になる。ドラッグ&ドロップでステータス変更・列の並び替えが可能。
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ Backlog │ │In Progress│ │ In Review│ │ Done │
├──────────┤ ├──────────┤ ├──────────┤ ├──────────┤
│ #42 🔴 │ │ #38 🟡 │ │ #30 🔴 │ │ #25 ✅ │
│ OAuth │ │ DB 設計 │ │ ログイン │ │ 環境構築 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
[+ ステータス追加]