## 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
