feat(db): agentic-teams data model — epics, work items, repos, membership

Adds the foundation entities from docs/agentic-teams.md §5/§7: a versioned
vision layer (Epic -> EpicTask -> Task), git repositories attached to projects,
agents as many-to-many project members, and the ephemeral session that IS an
agent's workspace.

9 tables + 5 enums, purely additive — no drops and no column changes on
existing tables, since every new relation is held on the new side.

Shape notes:

- EpicTask chaining uses an explicit EpicTaskEdge table rather than a
  self-referencing array, so the roadmap graph walks in both directions and
  edges can carry a type later.
- TaskVerdict is commit-pinned and immutable: "QA verified 3f2ab" stays
  reproducible, which is what lets later coverage claims be audited rather than
  believed. TaskEvent is the append-only transition log.
- Task survives losing its repo and its assignee (both SetNull) — a work item
  outlives whoever happened to be working it — but cascades from its EpicTask.
- CodeRepo, not Repository: `repository.repository.ts` next to 24 other
  *.repository.ts data-access files would be actively confusing. CLI resource is
  `repos`, with repo/repository/repositories aliased onto it.
- AgentProject.llmId exists so the brain-ownership question
  (docs/agentic-teams.md §15 Q1) can be settled either way without a migration.
- Agent.projectId is retained and coexists with AgentProject for one release so
  findByProjectId / prepareContext keep working. Note this partially reverses
  20260223120000_remove_project_members — deliberately, and only for agents;
  human membership stays RBAC-driven.

No CHECK constraints anywhere, on purpose: deploy/entrypoint.sh provisions with
`prisma db push`, so hand-written migration SQL never runs in production. The
migration file is kept for local `migrate dev` parity and as a readable record,
and says so in its header. Invariants that cannot be expressed in the schema
(EpicTaskEdge cycle rejection) belong to the service layer; a schema test
documents that boundary rather than pretending the database enforces it.

Tests: 23 schema tests covering defaults, per-project name scoping, cascade vs
SetNull on every FK, all 12 lifecycle states, and the GC-sweep predicate.
clearAllTables extended for all 9 models.

Verified locally against postgres:16-alpine on :5433 — db suite 128 passed
(baseline was 105; the same 4 pre-existing Secret_backendId_fkey failures
remain, unrelated). Workspace 2291 passed, build clean, lint unchanged at the
repo's pre-existing 869.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N4wNHWf7xSwnZCWpJcyv9p
This commit is contained in:
Michal
2026-08-08 10:30:02 +01:00
parent a186d95a85
commit fceb3cfb95
5 changed files with 1125 additions and 31 deletions

View File

@@ -2558,9 +2558,9 @@
"testStrategy": "Unit tests in src/mcpd/tests/validation/: verify normalizeResource('task') returns 'tasks', normalizeResource('inference-task') returns 'inference-tasks', normalizeResource('et') returns 'epictasks'. Test that RBAC_RESOURCES includes all new entries.",
"priority": "high",
"dependencies": [],
"status": "pending",
"status": "done",
"subtasks": [],
"updatedAt": "2026-08-08T08:55:58.627Z"
"updatedAt": "2026-08-08T09:22:12.130Z"
},
{
"id": "116",
@@ -2572,9 +2572,9 @@
"dependencies": [
"115"
],
"status": "pending",
"status": "done",
"subtasks": [],
"updatedAt": "2026-08-08T08:55:58.627Z"
"updatedAt": "2026-08-08T09:22:12.140Z"
},
{
"id": "117",
@@ -2584,9 +2584,9 @@
"testStrategy": "Schema tests in src/db/tests/: create Epic, verify unique constraint on (name, projectId), test cascade delete from Project, test EpicTask creation with Epic reference, test EpicTaskEdge uniqueness.",
"priority": "high",
"dependencies": [],
"status": "pending",
"status": "done",
"subtasks": [],
"updatedAt": "2026-08-08T08:55:58.627Z"
"updatedAt": "2026-08-08T09:29:43.358Z"
},
{
"id": "118",
@@ -2598,9 +2598,9 @@
"dependencies": [
"117"
],
"status": "pending",
"status": "done",
"subtasks": [],
"updatedAt": "2026-08-08T08:55:58.627Z"
"updatedAt": "2026-08-08T09:29:43.369Z"
},
{
"id": "119",
@@ -2610,9 +2610,9 @@
"testStrategy": "Schema tests: create CodeRepo linked to Project, verify unique constraint on (name, projectId), test cascade delete from Project, test SetNull behavior when Secret is deleted.",
"priority": "high",
"dependencies": [],
"status": "pending",
"status": "done",
"subtasks": [],
"updatedAt": "2026-08-08T08:55:58.627Z"
"updatedAt": "2026-08-08T09:29:43.377Z"
},
{
"id": "120",
@@ -2622,9 +2622,9 @@
"testStrategy": "Schema tests: create AgentProject junction, verify unique constraint prevents duplicate membership, test cascade on both Agent and Project delete, test SetNull on Llm delete.",
"priority": "high",
"dependencies": [],
"status": "pending",
"status": "done",
"subtasks": [],
"updatedAt": "2026-08-08T08:55:58.627Z"
"updatedAt": "2026-08-08T09:29:43.384Z"
},
{
"id": "121",
@@ -2637,9 +2637,9 @@
"118",
"120"
],
"status": "pending",
"status": "done",
"subtasks": [],
"updatedAt": "2026-08-08T08:55:58.627Z"
"updatedAt": "2026-08-08T09:29:43.392Z"
},
{
"id": "122",
@@ -2655,9 +2655,9 @@
"120",
"121"
],
"status": "pending",
"status": "done",
"subtasks": [],
"updatedAt": "2026-08-08T08:55:58.627Z"
"updatedAt": "2026-08-08T09:29:43.398Z"
},
{
"id": "123",
@@ -3250,9 +3250,13 @@
}
],
"metadata": {
"created": "2026-08-08T08:54:31.861Z",
"updated": "2026-08-08T08:54:31.861Z",
"description": "Tasks for master context"
"version": "1.0.0",
"lastModified": "2026-08-08T09:29:43.398Z",
"taskCount": 162,
"completedCount": 104,
"tags": [
"master"
]
}
}
}
}