candle-annotator/openspec/specs/span-annotation/spec.md
Marko Djordjevic 925e7284e3 Archive code-review-fix change and sync specs to main
- 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>
2026-02-20 08:54:59 +01:00

5.1 KiB

ADDED Requirements

Requirement: Span annotation JSON export for ML pipeline

The system SHALL provide a GET /api/span-annotations/export endpoint that exports all span annotations for a given chart as JSON in the format expected by the ML pipeline. The output SHALL be a JSON object with an annotations array where each entry has: id, start_time (Unix timestamp), end_time (Unix timestamp), label, confidence (nullable), outcome (nullable), and sub_spans (nullable). The endpoint SHALL accept an optional chartId query parameter.

Scenario: Export span annotations as JSON

  • WHEN GET /api/span-annotations/export?chartId=3 is called
  • THEN the system returns a JSON object with all span annotations for chart 3 in the ML pipeline format

Scenario: Export without chartId

  • WHEN GET /api/span-annotations/export is called without chartId
  • THEN the system exports span annotations for the most recently created chart

Requirement: Prediction-sourced span annotation creation

The system SHALL support creating span annotations with a source field indicating whether the annotation was created by a human ("human"), confirmed from a model prediction ("model_confirmed"), or corrected from a model prediction ("model_corrected"). The existing POST endpoint for span annotations SHALL accept an optional source field (default: "human") and optional model_prediction field (object with label and confidence from the original prediction).

Scenario: Create human annotation

  • WHEN a span annotation is created without a source field
  • THEN the source defaults to "human"

Scenario: Confirm model prediction

  • WHEN a user confirms a model prediction as an annotation
  • THEN the span annotation is created with source "model_confirmed" and model_prediction containing the original predicted label and confidence

Scenario: Correct model prediction

  • WHEN a user changes the label of a model prediction before saving
  • THEN the span annotation is created with source "model_corrected" and model_prediction containing the original predicted label and confidence

Requirement: Negative annotation for dismissed predictions

The system SHALL support saving negative annotations when a user dismisses a model prediction as "not a pattern". A negative annotation SHALL have label "O", source "human_correction", and a model_prediction field recording what the model originally predicted.

Scenario: Save negative annotation

  • WHEN user dismisses a "bull_flag" prediction with confidence 0.72
  • THEN the system creates a span annotation with label "O", source "human_correction", and model_prediction { "label": "bull_flag", "confidence": 0.72 }

Requirement: Keyboard handler uses stable selectedSpanId

The SpanAnnotationManager keyboard handler SHALL use a useRef for selectedSpanId to prevent stale closure reads. The handleDeleteSpan function SHALL be wrapped in useCallback with proper dependencies.

Scenario: Delete correct span via keyboard

  • WHEN user selects span A, then selects span B, then presses Delete
  • THEN span B is deleted (not span A from a stale closure)

Requirement: Preview primitive uses ref instead of state

The SpanAnnotationManager preview primitive (shown during span drawing on mouse move) SHALL use a useRef instead of useState to avoid a state/effect feedback loop and unnecessary re-renders.

Scenario: Mouse move during drawing does not cause re-render cascade

  • WHEN the user moves the mouse while drawing a span annotation
  • THEN the preview primitive updates via ref mutation without triggering a React re-render

Requirement: Preview primitive cleanup on unmount

The SpanAnnotationManager SHALL detach the preview primitive in the useEffect cleanup function when the component unmounts, preventing a leaked canvas primitive.

Scenario: Component unmounts during drawing

  • WHEN the user navigates away while mid-draw
  • THEN the preview primitive is detached from the chart

Requirement: fitContent not called on every span change

The SpanAnnotationManager reconciliation effect SHALL NOT call chart.timeScale().fitContent() on every span annotation change. fitContent() SHALL only be called on initial chart load.

Scenario: Span annotation added preserves zoom

  • WHEN the user adds a new span annotation
  • THEN the chart maintains the current scroll position and zoom level (no fitContent reset)

Requirement: Incremental primitive updates

The SpanAnnotationManager SHALL update only the selection state of existing primitives on selection change instead of detaching all and re-attaching all. Full recreation SHALL only occur when the annotation list itself changes (add/remove/edit).

Scenario: Selection change is O(1)

  • WHEN the user clicks a different span annotation
  • THEN only the previously selected and newly selected primitives are updated (not all N primitives)

Scenario: Annotation add triggers full reconciliation

  • WHEN a new span annotation is added
  • THEN the primitive list is reconciled (new primitive added, existing ones kept)