## ADDED Requirements ### Requirement: Generic error responses All Next.js API routes SHALL return generic error messages to clients for 500-level errors. The response body SHALL be `{ "error": "Internal server error" }`. The full error details SHALL be logged server-side via `console.error` with request context. #### Scenario: Internal error returns generic message - **WHEN** an API route handler throws an unexpected error - **THEN** the client receives HTTP 500 with `{ "error": "Internal server error" }` and the full error is logged server-side #### Scenario: No stack traces in response - **WHEN** a database query fails in any API route - **THEN** the response does NOT contain table names, file paths, connection strings, or stack traces ### Requirement: Scoped bulk annotation delete The `DELETE /api/annotations` endpoint SHALL require a `chartId` query parameter when `all=true` is specified. Unscoped delete-all (without chartId) SHALL be rejected with HTTP 400. #### Scenario: Scoped bulk delete - **WHEN** `DELETE /api/annotations?all=true&chartId=5` is called - **THEN** all annotations for chart 5 are deleted #### Scenario: Unscoped bulk delete rejected - **WHEN** `DELETE /api/annotations?all=true` is called without chartId - **THEN** the route returns HTTP 400 with `{ "error": "chartId is required for bulk delete" }` ### Requirement: Transaction-wrapped chart cascade delete The `DELETE /api/charts/[id]` route SHALL wrap all related deletions (annotations, span annotations, candles, chart) in a single database transaction using `db.transaction()`. #### Scenario: Cascade delete in transaction - **WHEN** `DELETE /api/charts/5` is called - **THEN** span annotations, annotations, candles, and the chart record for chart 5 are all deleted within a single transaction #### Scenario: Partial failure rolls back - **WHEN** the candles deletion fails mid-transaction - **THEN** all deletions are rolled back and the chart remains intact ### Requirement: Span annotations included in chart cascade delete The `DELETE /api/charts/[id]` route SHALL delete span annotations for the chart in addition to annotations and candles. #### Scenario: Span annotations deleted with chart - **WHEN** `DELETE /api/charts/5` is called - **THEN** all `span_annotations` rows with `chart_id=5` are deleted before the chart record ### Requirement: parseInt validation All API routes that parse integer query parameters SHALL use `parseInt(value, 10)` with radix 10 and check for `isNaN()`. Invalid integer parameters SHALL return HTTP 400. #### Scenario: Valid integer parameter - **WHEN** `GET /api/candles?chartId=5` is called - **THEN** chartId is parsed as integer 5 #### Scenario: Invalid integer parameter - **WHEN** `GET /api/candles?chartId=abc` is called - **THEN** the route returns HTTP 400 with `{ "error": "Invalid chartId" }` ### Requirement: CSV injection protection on exports All CSV export routes SHALL prefix cell values starting with `=`, `+`, `-`, or `@` with a single quote (`'`) to prevent spreadsheet formula injection. #### Scenario: Dangerous cell value escaped - **WHEN** an annotation note contains `=CMD("calc")` - **THEN** the exported CSV cell contains `'=CMD("calc")` #### Scenario: Normal values unchanged - **WHEN** an annotation note contains `regular text` - **THEN** the exported CSV cell contains `regular text` (no prefix) ### Requirement: response.ok checks on all fetch calls All `fetch()` calls in frontend components (`page.tsx`, `CandleChart.tsx`) SHALL check `response.ok` before calling `response.json()`. If `!response.ok`, the code SHALL throw an error or handle the failure explicitly. #### Scenario: Successful response parsed - **WHEN** a fetch call returns HTTP 200 - **THEN** `response.json()` is called and the data is used normally #### Scenario: Error response handled - **WHEN** a fetch call returns HTTP 500 - **THEN** the code detects `!response.ok` and shows an error message instead of attempting JSON parse