# SciQLopPlots Backlog — 2026-03-27 Deep Review

## P0 — Fix Now (crashes, UB, data races)

### ~~BUG-01: Division by zero in `SciQLopNDProjectionCurves::set_data`~~ ✅ FIXED 2026-03-27
- Early return when `curves_count < 2` (projections need ≥2 curves)
- Reproducer: `tests/integration/test_p0_bugs.py::TestBug01`

### ~~BUG-02: Unsigned integer underflow in `VPlotsAlign::_recompute_margins`~~ ✅ FIXED 2026-03-27
- Switched from `std::size_t` to `int`, added `std::max(0, ...)` clamping, added size guard in second loop
- Reproducer: `tests/integration/test_p0_bugs.py::TestBug02`

### ~~BUG-03: `nullptr` crash in `SciQLopNDProjectionCurves` constructor~~ ✅ FIXED 2026-03-27
- Skip nullptr from `qobject_cast` instead of appending
- Reproducer: `tests/integration/test_p0_bugs.py::TestBug03`

### ~~RACE-01: `set_callable` races with worker-thread `get_data`~~ ✅ FIXED 2026-03-27
- Added `QMutex m_callable_mutex` to `SimplePyCallablePWrapper`; copy-under-lock pattern
- Reproducer: `tests/integration/test_p0_bugs.py::TestRace01`

## P1 — Fix Soon (latent deadlocks, silent failures, correctness)

### THREAD-01: Signal emitted while `_plot_info_mutex` held in `_resample()`
- **File:** `include/SciQLopPlots/Plotables/Resamplers/AbstractResampler.hpp:131-135`
- **Status:** Low risk — uses QRecursiveMutex + QueuedConnection (safe in practice)
- **Fix:** Copy range under lock, release, then call `resample(range)`

### ~~THREAD-02: `Q_EMIT _state_changed()` while `m_mutex` held~~ ✅ FIXED 2026-03-27
- Set flag under lock, emit after unlock in all 3 `set_data` overloads
- Reproducer: `tests/integration/test_p1_bugs.py::TestThread02`

### ~~THREAD-03: `_resample_impl` runs under `_data_mutex` (long hold + signal emission)~~ ✅ FIXED 2026-03-27
- Copy data snapshot under lock, release lock, then call `_resample_impl` outside
- Reproducer: `tests/integration/test_p1_bugs.py::TestThread03`

### ~~BUG-04: Views boundary clipping — `upper_bound`/`lower_bound` inversion~~ ✅ FIXED 2026-03-27
- Swapped to `lower_bound` for start and `upper_bound` for stop in XYView/XYZView
- Reproducer: `tests/integration/test_p1_bugs.py::TestBug04`

### ~~BUG-05: Python `plot()` silently returns `None` for unhandled dispatch paths~~ ✅ FIXED 2026-03-27
- Made dispatch exhaustive with `raise ValueError`; removed exception-swallowing `except` blocks
- Reproducer: `tests/integration/test_p1_bugs.py::TestBug05`

### ~~BUG-06: `_nanoseconds` overflows `int` after year 2038~~ ✅ FIXED 2026-03-27
- Changed `static_cast<int>(epoch)` to `static_cast<int64_t>(epoch)` in both functions
- Reproducer: `tests/integration/test_p1_bugs.py::TestBug06`

### ~~BUG-07: Unchecked `std::get` on variant in `_range_based_update`~~ ✅ FIXED 2026-03-27
- Added `std::holds_alternative` guard before `std::get`
- Reproducer: `tests/integration/test_p1_bugs.py::TestBug07`

## P2 — Dead Code Cleanup

### ~~DEAD-01: `_icon_update_timer` allocated but never started/connected~~ ✅ CLEANED 2026-03-27
- Removed member, constructor init, and interval/singleShot setup from ColorMap & Histogram2D

### ~~DEAD-02: `Q_ENUMS(FractionStyle)` in classes that don't define it~~ ✅ CLEANED 2026-03-27
- Removed from LineGraph, Curve, ColorMap

### ~~DEAD-03: `SciQLopLineGraph(QCustomPlot*)` single-arg constructor~~ ✅ CLEANED 2026-03-27
- Removed declaration and definition

### DEAD-04: `minimal_axis_margins()` and `calculateAutoMargin` stub — NOT DEAD
- Actually in use; kept as-is

### ~~DEAD-05: `enable_legend()` virtual, never overridden or called~~ ✅ CLEANED 2026-03-27
- Removed from SciQLopPlotInterface

### ~~DEAD-06: `create_curves()` private alias, never called~~ ✅ CLEANED 2026-03-27
- Removed from SciQLopCurve

### ~~DEAD-07: `_microseconds()` defined, never called~~ ✅ CLEANED 2026-03-27
- Removed from SciQLopTracer.cpp

### ~~DEAD-08: `SciQLopMultiTimeSeriesPlotPanel.hpp` empty file~~ ✅ CLEANED 2026-03-27
- Deleted empty file

### ~~DEAD-09: Duplicate entries in `bindings.xml`~~ ✅ CLEANED 2026-03-27
- Removed duplicate `<system-include memory>` and duplicate `<rename to="marker">`

### ~~DEAD-10: Unused includes in `SciQLopPlot.cpp`~~ ✅ CLEANED 2026-03-27
- Removed `<QGestureRecognizer>` and `<QSharedPointer>`

### ~~DEAD-11: Commented-out build config blocks~~ ✅ CLEANED 2026-03-27
- Removed fast-math and QCUSTOMPLOT_USE_OPENGL commented blocks

## P3 — Refactoring Opportunities (address when touching nearby code)

### REFAC-01: Deduplicate `SciQLopColorMap` / `SciQLopHistogram2D`
- ~80% identical: same members, same `set_selected`, `set_x_axis`, `gradient`, `set_name`
- Extract shared intermediate base class

### REFAC-02: Deduplicate span implementations (VSpan/HSpan/RSpan)
- Identical `replot()`, `set_color()`, `set_borders_color()`, etc.
- CRTP `SciQLopSpanWrapper<ImplT>` for forwarding boilerplate

### REFAC-03: Extract `set_x_axis`/`set_y_axis` into shared base
- Same body in 5 plotable classes

### REFAC-04: Extract `_got_first_data` + `request_rescale()` into base helper
- Same pattern in 4 plotable classes

### REFAC-05: Naming consistency for `MultiPlotsVerticalSpan`
- Uses `get_*/is_*` while all other types use bare accessors
- Align to `color()`, `selected()`, `visible()` convention

### REFAC-06: Python `plot()` dispatch table
- Replace cascading if/elif with dict keyed on `(len(args), graph_type)`

### REFAC-07: `SciQLopMultiPlotPanel` delegation shell
- ~30 single-line forwarding methods to `_container`
- Consider exposing container directly or using `using` declarations
