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
285
docs/API.md
Normal file
285
docs/API.md
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
# Scrum4Me REST API
|
||||
|
||||
REST-API contract voor Claude Code en andere clients.
|
||||
|
||||
## Authenticatie
|
||||
|
||||
Alle endpoints behalve `GET /api/health` vereisen een Bearer-token:
|
||||
|
||||
```
|
||||
Authorization: Bearer <token>
|
||||
```
|
||||
|
||||
Tokens beheer je via Instellingen → Tokens (`/settings/tokens`). Een token is gekoppeld aan één gebruiker; een demo-account-token kan lezen maar niet schrijven (`403`).
|
||||
|
||||
## Status-enums
|
||||
|
||||
De API gebruikt **lowercase** statussen. De database gebruikt UPPER_SNAKE; de vertaling gebeurt op de boundary.
|
||||
|
||||
| Entiteit | Waarden |
|
||||
|---|---|
|
||||
| Task status | `todo`, `in_progress`, `review`, `done` |
|
||||
| Story status | `open`, `in_sprint`, `done` |
|
||||
|
||||
## Foutcodes
|
||||
|
||||
| Code | Betekenis |
|
||||
|---|---|
|
||||
| `200` | OK |
|
||||
| `201` | Created |
|
||||
| `400` | Malformed body (bv. ongeldige JSON) |
|
||||
| `401` | Token ontbreekt of ongeldig |
|
||||
| `403` | Token heeft geen toegang (demo-account, geen lid van product) |
|
||||
| `404` | Resource niet gevonden |
|
||||
| `422` | Validatiefout — body is wel-gevormd maar niet acceptabel |
|
||||
| `500` | Onverwachte serverfout |
|
||||
|
||||
---
|
||||
|
||||
## Endpoints
|
||||
|
||||
### `GET /api/health`
|
||||
|
||||
Health-probe. Geen authenticatie vereist.
|
||||
|
||||
**Query params:** `?db=1` voegt een DB-ping toe.
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
{ "status": "ok", "version": "0.3.x", "time": "2026-04-26T20:00:00Z" }
|
||||
```
|
||||
|
||||
Met `?db=1`:
|
||||
```json
|
||||
{ "status": "ok", "version": "0.3.x", "time": "...", "database": "ok" }
|
||||
```
|
||||
|
||||
`database` is `"ok"` of `"down"`. De endpoint zelf retourneert altijd `200`.
|
||||
|
||||
```bash
|
||||
curl https://scrum4me.app/api/health?db=1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `GET /api/products`
|
||||
|
||||
Lijst van actieve producten waar de tokengebruiker eigenaar of lid van is.
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "cmofu...",
|
||||
"code": "SCRUM4ME",
|
||||
"name": "Scrum4Me",
|
||||
"description": "...",
|
||||
"repo_url": "https://github.com/...",
|
||||
"definition_of_done": "..."
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer $TOKEN" https://scrum4me.app/api/products
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `GET /api/products/:id/claude-context`
|
||||
|
||||
Bundled context voor Claude Code: product, actieve sprint, volgende story (met tasks) en open todos van de tokengebruiker — in één call.
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
{
|
||||
"product": { "id", "code", "name", "description", "repo_url", "definition_of_done" },
|
||||
"active_sprint": { "id": "...", "sprint_goal": "...", "status": "ACTIVE" } | null,
|
||||
"next_story": {
|
||||
"id", "code", "title", "description", "acceptance_criteria",
|
||||
"priority", "status",
|
||||
"tasks": [
|
||||
{ "id", "code", "title", "description", "implementation_plan",
|
||||
"priority", "sort_order", "status" }
|
||||
]
|
||||
} | null,
|
||||
"open_todos": [
|
||||
{ "id", "title", "description", "created_at" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
`open_todos` is gelimiteerd op 50 items, gesorteerd op `created_at` asc. Demo-tokens kunnen dit endpoint lezen.
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Bearer $TOKEN" \
|
||||
https://scrum4me.app/api/products/$PRODUCT_ID/claude-context
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `GET /api/products/:id/next-story`
|
||||
|
||||
Hoogst geprioriteerde open story in de actieve sprint.
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
{
|
||||
"id": "...",
|
||||
"code": "ST-356",
|
||||
"title": "Solo Kanban-bord met DnD en Zustand",
|
||||
"description": "...",
|
||||
"acceptance_criteria": "...",
|
||||
"status": "in_sprint",
|
||||
"tasks": [
|
||||
{
|
||||
"id": "...",
|
||||
"code": "ST-356.1",
|
||||
"title": "Store stores/solo-store.ts",
|
||||
"description": "...",
|
||||
"implementation_plan": null,
|
||||
"priority": 2,
|
||||
"sort_order": 1,
|
||||
"status": "todo"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Foutcodes:** `404` als geen actieve sprint of geen open stories.
|
||||
|
||||
---
|
||||
|
||||
### `GET /api/sprints/:id/tasks`
|
||||
|
||||
Lijst taken van de sprint, geordend op `(story.sort_order, task.priority, task.sort_order)`.
|
||||
|
||||
**Query params:** `?limit=N` (default 10, max 50)
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "...",
|
||||
"code": "ST-356.1",
|
||||
"title": "...",
|
||||
"description": "...",
|
||||
"implementation_plan": null,
|
||||
"story_id": "...",
|
||||
"story_code": "ST-356",
|
||||
"priority": 2,
|
||||
"sort_order": 1,
|
||||
"status": "todo"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `PATCH /api/stories/:id/tasks/reorder`
|
||||
|
||||
Volgorde van taken binnen een story aanpassen.
|
||||
|
||||
**Body:**
|
||||
```json
|
||||
{ "task_ids": ["task-id-a", "task-id-b", "task-id-c"] }
|
||||
```
|
||||
|
||||
Alle IDs moeten bij de story horen. **Foutcodes:** `422` bij Zod-fouten of als een task_id niet tot de story behoort.
|
||||
|
||||
---
|
||||
|
||||
### `PATCH /api/tasks/:id`
|
||||
|
||||
Status of implementation_plan bijwerken. Minstens één van beide is verplicht.
|
||||
Toegestane status-waarden zijn `todo`, `in_progress` en `done`. `review`
|
||||
wordt door deze endpoint geweigerd zolang de sprint-UI die state niet
|
||||
rendert — gebruik de Kanban-board voor REVIEW-overgangen.
|
||||
|
||||
**Body:**
|
||||
```json
|
||||
{ "status": "in_progress", "implementation_plan": "..." }
|
||||
```
|
||||
|
||||
**Response (200):**
|
||||
```json
|
||||
{
|
||||
"id": "...",
|
||||
"status": "in_progress",
|
||||
"implementation_plan": "..."
|
||||
}
|
||||
```
|
||||
|
||||
**Foutcodes:** `422` bij ongeldige body of onbekende status. `403` bij demo-token.
|
||||
|
||||
---
|
||||
|
||||
### `POST /api/stories/:id/log`
|
||||
|
||||
Activiteit vastleggen op een story.
|
||||
|
||||
**Body — IMPLEMENTATION_PLAN:**
|
||||
```json
|
||||
{
|
||||
"type": "IMPLEMENTATION_PLAN",
|
||||
"content": "Plan: ...",
|
||||
"metadata": { "branch": "feat/x" }
|
||||
}
|
||||
```
|
||||
|
||||
**Body — TEST_RESULT:**
|
||||
```json
|
||||
{
|
||||
"type": "TEST_RESULT",
|
||||
"content": "Alle tests groen",
|
||||
"status": "PASSED",
|
||||
"metadata": { "ci_run": "..." }
|
||||
}
|
||||
```
|
||||
|
||||
**Body — COMMIT:**
|
||||
```json
|
||||
{
|
||||
"type": "COMMIT",
|
||||
"content": "Werk afgerond",
|
||||
"commit_hash": "abc123",
|
||||
"commit_message": "feat(ST-XXX): ...",
|
||||
"metadata": { "branch": "feat/x" }
|
||||
}
|
||||
```
|
||||
|
||||
`metadata` is optioneel, vrij JSON-object. **Response (201):**
|
||||
```json
|
||||
{ "id": "...", "created_at": "..." }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `POST /api/todos`
|
||||
|
||||
Nieuwe todo voor de tokengebruiker.
|
||||
|
||||
**Body:**
|
||||
```json
|
||||
{
|
||||
"title": "Een ding doen",
|
||||
"description": "Optionele uitleg, max 2000 tekens",
|
||||
"product_id": "cmof..."
|
||||
}
|
||||
```
|
||||
|
||||
**Response (201):**
|
||||
```json
|
||||
{ "id": "...", "title": "...", "description": "...", "created_at": "..." }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Voorbeeldworkflow voor Claude Code
|
||||
|
||||
1. **Probe:** `GET /api/health?db=1` — bevestig dat de service en DB bereikbaar zijn.
|
||||
2. **Context:** `GET /api/products/$ID/claude-context` — haal product, sprint, volgende story en todos op in één call.
|
||||
3. **Plan vastleggen:** `POST /api/stories/$STORY_ID/log` met `type: IMPLEMENTATION_PLAN`.
|
||||
4. **Per task:** `PATCH /api/tasks/$TASK_ID` met `status: "in_progress"`, daarna met `status: "done"` plus eventueel `implementation_plan`.
|
||||
5. **Test:** `POST /api/stories/$STORY_ID/log` met `type: TEST_RESULT` en `status: PASSED|FAILED`.
|
||||
6. **Commit:** `POST /api/stories/$STORY_ID/log` met `type: COMMIT`, `commit_hash`, `commit_message`, optioneel `metadata: { branch }`.
|
||||
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 334 KiB After Width: | Height: | Size: 337 KiB |
|
|
@ -382,6 +382,38 @@ De MVP is klaar wanneer Lars — de primaire persona — de volledige cyclus kan
|
|||
- Gecombineerde lijst op `/settings` van eigen producten (badge "Eigenaar") en team-lidmaatschappen (badge "Developer" + eigenaarsnaam); klikbaar naar product; "Verlaten"-knop met bevestiging voor lidmaatschappen; lege staat met CTA
|
||||
- Done when: eigenaar-producten en team-producten zichtbaar in één lijst; verlaten werkt en verwijdert rij
|
||||
|
||||
- [x] **ST-511** Entity codes voor Product, PBI en Story (buiten originele backlog toegevoegd)
|
||||
- **Schema:** `code String? @db.VarChar(30)` op `Product`, `Pbi` en `Story`; unique per parent (`user_id` voor Product, `product_id` voor Pbi/Story); `Task` heeft geen DB-veld — code wordt afgeleid als `${story.code}.${index_in_story}`
|
||||
- **Auto-generatie:** `lib/code-server.ts` met `generateNextStoryCode` (`ST-001`, `ST-002`, … 3-cijferig per product) en `generateNextPbiCode` (`PBI-1`, `PBI-2`, … per product); `createWithCodeRetry`-helper vangt P2002 op het code-veld op en probeert max 3× opnieuw zodat gelijktijdige inserts niet crashen
|
||||
- **Validatie:** Zod max 30 tekens, regex `^[A-Za-z0-9._-]+$`; handmatige override mag elk format dat aan de basis-regex voldoet (geen format-enforcement op `ST-NNN`)
|
||||
- **Forms:** code-input op Product/Pbi/Story dialogen; auto-default zichtbaar als placeholder `auto`; field-level error-rendering onder code-input voor zowel create- als edit-mode (uniciteits-conflict, ongeldig format)
|
||||
- **Display:** `CodeBadge` (`components/shared/code-badge.tsx`) consistent op dashboard product-list, PBI-list, story-blocks (Product Backlog), sprint board (alle drie panelen incl. PBI-headers), solo-bord task-cards, task-detail-dialoog, sprint-afronden-dialoog en de story-dialoog title; task-card toont derived `${story.code}.${index}`-badge rechtsboven uitgelijnd
|
||||
- **Seed:** parser strip `ST-XXX:`-prefix uit titles, vult `code` apart; product `Scrum4Me` krijgt `code: 'SCRUM4ME'`, milestones krijgen `M0`/`M3.5`/etc., stories krijgen `ST-001…ST-612`
|
||||
- Done when: auto-toegekende codes per product oplopend en uniek; race-conflict wordt opgevangen door retry-helper i.p.v. te crashen; handmatige duplicate code toont inline error onder de input in zowel create- als edit-mode; codes zichtbaar als badge in alle lijsten/cards/dialogen; seed verdeelt codes correct (8 PBI's met `M*`, 84 stories met `ST-NNN`)
|
||||
|
||||
- [x] **ST-512** REST API uitbreidingen voor codes, todo-description en task implementation_plan (buiten originele backlog toegevoegd)
|
||||
- **`GET /api/products`:** voeg `code` toe (naast `id`, `name`, `repo_url`); optioneel `description` en `definition_of_done`
|
||||
- **`GET /api/products/:id/next-story`:** voeg `code` toe op story; voeg per task `code` (derived `${story.code}.${index_in_story}`) en `implementation_plan` toe
|
||||
- **`GET /api/sprints/:id/tasks`:** voeg `description`, `implementation_plan` en `story_code` toe per task; voeg een derived `code`-veld per task toe (`${story.code}.${index_in_story}`)
|
||||
- **`POST /api/todos`:** accepteer optionele `description` (max 2000 tekens); valideer en sla op; retourneer `description` in response
|
||||
- Done when: alle vier endpoints retourneren / accepteren de nieuwe velden zoals beschreven; curl-test toont `code` op products, story en tasks; todo aanmaken via API met `description` slaat op
|
||||
|
||||
- [x] **ST-513** REST API hardening voor Claude Code (buiten originele backlog toegevoegd)
|
||||
- **Health:** nieuwe `GET /api/health` zonder auth; retourneert `{ status, version, time }`; optioneel `?db=1` voor DB-ping (`{ database: 'ok' | 'down' }`)
|
||||
- **Claude-context:** nieuwe `GET /api/products/:id/claude-context` (auth) die in één call `product`, `active_sprint`, `next_story` (met tasks), en `open_todos` van de gebruiker terugbrengt — voorkomt round-trips
|
||||
- **Status-case op API-boundary:** nieuwe `lib/task-status.ts` mapper; API exposeert lowercase (`todo`/`in_progress`/`review`/`done` voor tasks; `open`/`in_sprint`/`done` voor stories); DB blijft UPPER_SNAKE; UI ongewijzigd
|
||||
- **`PATCH /api/tasks/:id`:** accepteert lowercase `status` via mapper; retourneert lowercase
|
||||
- **Story-log metadata:** nieuwe optionele `metadata Json?` kolom op `StoryLog`; `POST /api/stories/:id/log` accepteert per type een optioneel `metadata`-veld (bv. `{ branch: 'feat/x' }`); bestaande velden ongewijzigd → backwards-compatible
|
||||
- **Foutcodes:** Zod-validatie geeft `422` (was `400`); `400` blijft voor malformed body; `401`/`403`/`404`/`500` ongewijzigd
|
||||
- **API-documentatie:** nieuwe `docs/API.md` met endpoints, request/response, foutcodes, status-enums en curl-voorbeelden; `CLAUDE.md` verwijst ernaar
|
||||
- Done when: `curl /api/health` werkt zonder auth; `curl /api/products/:id/claude-context` retourneert bundled JSON; PATCH/PUT routes accepteren lowercase status en geven 422 bij ongeldige body; story-log POST bewaart `metadata`; `docs/API.md` is gepubliceerd
|
||||
- **`GET /api/products`:** voeg `code` toe (naast `id`, `name`, `repo_url`); optioneel `description` en `definition_of_done`
|
||||
- **`GET /api/products/:id/next-story`:** voeg `code` toe op story; voeg per task `code` (derived `${story.code}.${index_in_story}`) en `implementation_plan` toe
|
||||
- **`GET /api/sprints/:id/tasks`:** voeg `description`, `implementation_plan` en `story_code` toe per task; voeg een derived `code`-veld per task toe (`${story.code}.${index_in_story}`)
|
||||
- **`POST /api/todos`:** accepteer optionele `description` (max 2000 tekens); valideer en sla op; retourneer `description` in response
|
||||
- **Backwards-compat:** alle wijzigingen zijn additief — bestaande clients negeren onbekende keys; nieuwe input-velden zijn optioneel
|
||||
- Done when: alle vier endpoints retourneren / accepteren de nieuwe velden zoals beschreven; curl-test toont `code` op products, story en tasks; todo aanmaken via API met `description` slaat op
|
||||
|
||||
---
|
||||
|
||||
### M6: Polish & Launch-ready
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue