- Synced 14 capability delta specs to main specs - Created 6 new main specs: api-authentication, error-boundary, input-validation, security-headers, shared-types - Updated 8 existing specs with security, validation, and performance requirements - Archived change to openspec/changes/archive/2026-02-20-code-review-fix/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.9 KiB
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=5is called - THEN all annotations for chart 5 are deleted
Scenario: Unscoped bulk delete rejected
- WHEN
DELETE /api/annotations?all=trueis 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/5is 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/5is called - THEN all
span_annotationsrows withchart_id=5are 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=5is called - THEN chartId is parsed as integer 5
Scenario: Invalid integer parameter
- WHEN
GET /api/candles?chartId=abcis 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.okand shows an error message instead of attempting JSON parse