Todo description, entity codes, REST API extensions and Claude Code hardening (ST-509/511/512/513) (#2)
* docs(ST-511): add backlog entry for entity codes feature Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ST-511): add createWithCodeRetry helper to handle P2002 race on auto codes Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ST-511): retry on auto-code unique conflict in story and pbi create Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ST-511): surface field errors for code and title in PBI dialog Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ST-511): read create-state errors in Story dialog fieldError Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(ST-512): add backlog entry for REST API code/description/implementation_plan extensions; mark ST-511 done Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(ST-512): extend REST API with code, description and implementation_plan - GET /api/products returns code, description and definition_of_done - GET /api/products/:id/next-story returns story.code and per-task code + implementation_plan - GET /api/sprints/:id/tasks returns description, implementation_plan, story_code and derived per-task code - POST /api/todos accepts and returns optional description (max 2000) All changes are additive — existing clients ignore unknown keys. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(ST-512): mark ST-512 as done Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(ST-513): add backlog entry for API hardening for Claude Code Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(ST-513): add task and story status mappers for API boundary Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(ST-513): expose lowercase status on API and accept lowercase in PATCH /api/tasks Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(ST-513): add metadata JSONB column to StoryLog Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(ST-513): accept optional metadata in story log and switch validation errors to 422 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(ST-513): add GET /api/health endpoint with optional db ping Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(ST-513): add GET /api/products/:id/claude-context bundled endpoint Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(ST-513): add docs/API.md and link from CLAUDE.md specs table Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs(ST-513): mark ST-513 as done Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(ST-513): split 400 (malformed JSON) from 422 (validation), reject 'review' Codex review on PR #2: - P2.1: routes treated JSON parse failures as 422 instead of 400, breaking the contract in docs/API.md. Replace `request.json().catch(() => null)` with try/catch in 4 routes (tasks, reorder, todos, story-log) so a malformed body returns 400 and only well-formed-but-invalid bodies return 422. - P2.2: PATCH /api/tasks/:id accepted `status: "review"`, but the sprint task list UI does not render REVIEW (no label/color, the cycle helper falls back to TO_DO). Reject `review` at the API until the sprint UI is extended; document the subset in docs/API.md. Tests in __tests__/api updated for the new contract (29 assertions: zod-failures now expect 422, status payloads use lowercase API values, sprint-tasks mocks include the new story relation). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a8adac127f
commit
43a4294424
29 changed files with 809 additions and 116 deletions
|
|
@ -1,5 +1,43 @@
|
|||
import { Prisma } from '@prisma/client'
|
||||
import { prisma } from '@/lib/prisma'
|
||||
|
||||
const MAX_AUTO_CODE_ATTEMPTS = 3
|
||||
|
||||
function isCodeUniqueConflict(error: unknown): boolean {
|
||||
if (!(error instanceof Prisma.PrismaClientKnownRequestError)) return false
|
||||
if (error.code !== 'P2002') return false
|
||||
const target = (error.meta as { target?: string[] | string } | undefined)?.target
|
||||
if (!target) return false
|
||||
if (Array.isArray(target)) return target.includes('code')
|
||||
return target.includes('code')
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an auto code, then run the create. If the insert collides with an
|
||||
* existing code (P2002 on the code column), regenerate and retry up to a small
|
||||
* number of attempts. Protects against the SELECT-MAX → INSERT race when two
|
||||
* concurrent requests pick the same next number.
|
||||
*/
|
||||
export async function createWithCodeRetry<T>(
|
||||
generate: () => Promise<string>,
|
||||
create: (code: string) => Promise<T>,
|
||||
): Promise<T> {
|
||||
let lastError: unknown
|
||||
for (let attempt = 0; attempt < MAX_AUTO_CODE_ATTEMPTS; attempt++) {
|
||||
const code = await generate()
|
||||
try {
|
||||
return await create(code)
|
||||
} catch (e) {
|
||||
if (isCodeUniqueConflict(e)) {
|
||||
lastError = e
|
||||
continue
|
||||
}
|
||||
throw e
|
||||
}
|
||||
}
|
||||
throw lastError ?? new Error('Kon geen unieke code genereren')
|
||||
}
|
||||
|
||||
const STORY_AUTO_RE = /^ST-(\d+)$/
|
||||
const PBI_AUTO_RE = /^PBI-(\d+)$/
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue