feat(ST-706): task write tools — update_task_status and update_task_plan

- src/access.ts: shared product/story/task access checks via product
  ownership or membership
- update_task_status accepts lowercase API values, converts to DB
  enum, rejects unknown values
- update_task_plan replaces implementation_plan on a task
- Both call requireWriteAccess() so demo accounts get
  PERMISSION_DENIED before any DB write

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Janpeter Visser 2026-04-26 23:05:49 +02:00
parent e9d87dd8ff
commit e3f9476568
4 changed files with 123 additions and 1 deletions

30
src/access.ts Normal file
View file

@ -0,0 +1,30 @@
import { prisma } from './prisma.js'
export async function userCanAccessProduct(productId: string, userId: string): Promise<boolean> {
const hit = await prisma.product.findFirst({
where: {
id: productId,
OR: [{ user_id: userId }, { members: { some: { user_id: userId } } }],
},
select: { id: true },
})
return Boolean(hit)
}
export async function userCanAccessTask(taskId: string, userId: string): Promise<boolean> {
const task = await prisma.task.findUnique({
where: { id: taskId },
select: { story: { select: { product_id: true } } },
})
if (!task) return false
return userCanAccessProduct(task.story.product_id, userId)
}
export async function userCanAccessStory(storyId: string, userId: string): Promise<boolean> {
const story = await prisma.story.findUnique({
where: { id: storyId },
select: { product_id: true },
})
if (!story) return false
return userCanAccessProduct(story.product_id, userId)
}