# KOPR R123 — WAnalyzer prepared analytical curve binding hotfix

**Base build:** `KOPR_CLEAN_BUILD_20260729_R122_COBS_VIZIER_REFERENCE_STRETCH_HOTFIX.zip`  
**Date:** 2026-07-29  
**Scope:** WAnalyzer prepared-curve/render consistency; WComparison intentionally unchanged.

## Summary

R123 fixes the WAnalyzer dialog:

```text
Prepared analytical curve is unavailable.
```

The analytical worker and renderer previously generated the formula-result key independently. The worker request was prepared from one snapshot, while `PlotLines()` later reparsed formula widgets and rebuilt the key from render-time range and sampling state. This duplicated contract allowed a successfully calculated curve to become unreachable during rendering, especially after Analyse-generated formulas and return-specific updates.

## Implementation

### Immutable prepared plot request

New Qt-free module `analysis_plot_state.py` provides:

- `PreparedCurveBinding`;
- `PreparedAnalyzerPlotRequest`;
- `PreparedCurveResultsIncomplete`;
- `merge_prepared_curve_results()`.

`_analysis_prepare_curve_jobs()` now creates one binding for each visible combination of:

- comet line;
- apparition/return;
- regular or additional formula;
- formula order;
- exact worker/cache result key.

The same immutable request is carried through cache lookup, asynchronous execution and rendering.

### No render-time key reconstruction

`PlotLines()` no longer calls:

- `GrabRegular()` or `GrabAdditional()`;
- `HalfW()` / `halfwnorm()`;
- `curve_job_key()`.

It obtains exact bindings with `prepared_plot.bindings_for(...)` and reads `binding.result_key` directly. Regular formula lines remain solid and Analyse-generated lines remain dashed.

### Transactional graph protection

Worker rows are merged only after the prepared result set has been validated. The same completeness check runs before `fig.clf()`. If an internal worker result were incomplete, the previous valid graph remains visible and the diagnostic includes comet, return, formula source/index and a key prefix.

## Scientific contract

R123 does not change observation selection, two-body photometric geometry, light-time handling, H0/n equations, dust correction, adaptive sampling, curve styling or axis semantics. WComparison is not modified.

## Production files changed

- `analysis_plot_state.py` — new;
- `wanalyzer.py`;
- `docs/CHANGELOG.md`;
- `docs/developer/KOPR_R123_WANALYZER_PREPARED_CURVE_BINDING.md` — new.

## QA additions

- `tests/test_r123_wanalyzer_prepared_curve_binding.py` — new;
- `RUN_R123_FOCUSED_QA.md` — new.

## Validation

Focused analytical suite before final packaging:

```text
85 passed
1 skipped
0 failed
```

The skipped test requires a live PyQt5 event loop.

Complete QA in non-overlapping groups plus the final manifest validator:

```text
1731 passed
7 skipped
0 failed
```

The seven skips require a complete optional PyQt5 or Astropy FITS/time runtime.

Final manifests:

```text
QA:    505 files
CLEAN: 159 files
```

## Runtime limitation

The build environment does not provide a full PyQt5 graphical runtime or the user’s 10P COBS corpus. Live reproduction of the exact click sequence was therefore unavailable; the duplicated key contract was removed directly and is guarded by deterministic tests.
