spectro-kernel 0.6.0__tar.gz → 0.6.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (343) hide show
  1. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/.gitignore +8 -0
  2. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/CHANGELOG.md +394 -0
  3. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/PKG-INFO +37 -5
  4. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/README.md +30 -1
  5. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/concepts/algorithms.md +1 -1
  6. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/contributing.md +3 -3
  7. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/getting-started.md +1 -1
  8. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/index.md +1 -1
  9. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/reference.md +1 -1
  10. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/tutorials/discover-the-catalogue.md +1 -1
  11. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/tutorials/first-spectrum.md +1 -1
  12. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/tutorials/long-slit-reduction.md +1 -1
  13. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/usage/mcp.md +49 -3
  14. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/why.md +1 -1
  15. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/pyproject.toml +10 -4
  16. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/disentangle_sb2.py +63 -13
  17. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/doppler_tomogram.py +32 -9
  18. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/classification/classify_template_chi2.py +74 -14
  19. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/classification/pickles_atlas.py +9 -0
  20. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/compare_normalisations.py +7 -4
  21. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_edges.py +32 -4
  22. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_max.py +13 -1
  23. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_percentile.py +11 -1
  24. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/subtract_continuum.py +12 -1
  25. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/air_vacuum.py +34 -9
  26. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/extinction_correct_easyspec.py +31 -34
  27. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/fit_telluric_scaling.py +9 -3
  28. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/flux_calibrate_easyspec.py +59 -60
  29. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/remove_telluric.py +45 -17
  30. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/response_from_standard.py +36 -20
  31. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/synth_telluric.py +17 -9
  32. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_band_power.py +22 -14
  33. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_continuum_subtracted.py +13 -2
  34. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_lick_indices.py +16 -2
  35. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_log_lambda.py +23 -12
  36. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_pretrained.py +11 -2
  37. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_remote.py +31 -3
  38. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_spectrum.py +16 -5
  39. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_wavelets.py +12 -2
  40. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_hdf5.py +9 -2
  41. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/boxcar.py +92 -31
  42. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/detect_trace.py +9 -4
  43. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/easyspec_extract.py +64 -52
  44. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_fits.py +14 -1
  45. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/kinematics/rotation_curve.py +10 -2
  46. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/_line_flux.py +11 -6
  47. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/detect.py +21 -3
  48. spectro_kernel-0.6.2/src/spectro_kernel/algorithms/lines/equivalent_width.py +190 -0
  49. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/vr_ratio.py +11 -4
  50. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/nebular/bpt_line_ratios.py +26 -6
  51. spectro_kernel-0.6.2/src/spectro_kernel/algorithms/nebular/oiii_electron_temperature.py +324 -0
  52. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/nebular/sii_electron_density.py +27 -5
  53. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/compare_snr_methods.py +6 -3
  54. spectro_kernel-0.6.2/src/spectro_kernel/algorithms/quality/snr_edge.py +102 -0
  55. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/snr_linear_fit.py +38 -5
  56. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/_easyspec_apply.py +16 -0
  57. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/_easyspec_helpers.py +67 -12
  58. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/dark_combine.py +32 -6
  59. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/denoise_2d.py +51 -18
  60. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_bias.py +1 -1
  61. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_cosmic_ray.py +4 -1
  62. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_dark.py +1 -1
  63. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_flat.py +1 -1
  64. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_flat_normalize.py +4 -1
  65. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_bias.py +4 -1
  66. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_dark.py +4 -1
  67. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/extract_spectrum_sum.py +13 -3
  68. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/flat_combine.py +16 -3
  69. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/flat_normalize.py +24 -7
  70. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/subtract_sky_2d.py +37 -19
  71. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/wavelength_calibrate.py +24 -4
  72. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/cross_correlate.py +131 -17
  73. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/fit_keplerian_orbit.py +64 -18
  74. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/redshift_lines.py +28 -7
  75. spectro_kernel-0.6.2/src/spectro_kernel/algorithms/smoothing/_nan_bridge.py +32 -0
  76. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/compare_smoothings.py +6 -4
  77. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/smooth_gaussian.py +25 -3
  78. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/smooth_savgol.py +33 -3
  79. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/stacking/merge_echelle_orders.py +6 -3
  80. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/stacking/stack.py +40 -8
  81. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/timeseries/lomb_scargle.py +20 -4
  82. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/timeseries/phase_fold.py +12 -1
  83. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/clip_sigma.py +8 -3
  84. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/combine_arithmetic.py +7 -2
  85. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/resample.py +4 -2
  86. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/resample_flux_conserving.py +29 -5
  87. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/arc_geometry.py +28 -7
  88. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/easyspec_wavelength.py +20 -25
  89. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/match_lamp.py +2 -1
  90. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/solar.py +18 -8
  91. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/cli.py +8 -3
  92. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/embeddings.py +13 -0
  93. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/io/fits.py +27 -5
  94. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/pipeline.py +20 -3
  95. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/registry.py +61 -17
  96. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/context.py +53 -1
  97. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/image.py +18 -2
  98. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/spectrum.py +46 -4
  99. spectro_kernel-0.6.2/src/spectro_kernel/url_safety.py +301 -0
  100. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/version.py +1 -1
  101. spectro_kernel-0.6.2/src/spectro_mcp/__main__.py +211 -0
  102. spectro_kernel-0.6.2/src/spectro_mcp/auth.py +172 -0
  103. spectro_kernel-0.6.2/src/spectro_mcp/auto_tools.py +888 -0
  104. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_mcp/server.py +21 -3
  105. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_mcp/session.py +77 -22
  106. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_mcp/url_safety.py +11 -5
  107. spectro_kernel-0.6.2/tests/unit/test_aperture_photometry.py +70 -0
  108. spectro_kernel-0.6.2/tests/unit/test_catalog_queries_mocked.py +208 -0
  109. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_classify_template_chi2.py +85 -0
  110. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_cli.py +41 -0
  111. spectro_kernel-0.6.2/tests/unit/test_combine.py +118 -0
  112. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_combine_arithmetic.py +23 -0
  113. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_continuum.py +46 -0
  114. spectro_kernel-0.6.2/tests/unit/test_corrections.py +183 -0
  115. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_cross_correlate_extension.py +110 -0
  116. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_dark_flat_combine.py +50 -0
  117. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_denoise_2d.py +60 -0
  118. spectro_kernel-0.6.2/tests/unit/test_disentangle_sb2.py +91 -0
  119. spectro_kernel-0.6.2/tests/unit/test_doppler_tomogram.py +145 -0
  120. spectro_kernel-0.6.2/tests/unit/test_easyspec_sessions.py +132 -0
  121. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_embedding.py +20 -0
  122. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_embedding_tier1.py +62 -0
  123. spectro_kernel-0.6.2/tests/unit/test_export_hdf5.py +90 -0
  124. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_extract_boxcar.py +97 -0
  125. spectro_kernel-0.6.2/tests/unit/test_extract_spectrum_sum.py +67 -0
  126. spectro_kernel-0.6.2/tests/unit/test_flux_calibrate_easyspec.py +88 -0
  127. spectro_kernel-0.6.2/tests/unit/test_io.py +140 -0
  128. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_line_profiles.py +56 -0
  129. spectro_kernel-0.6.2/tests/unit/test_lines.py +151 -0
  130. spectro_kernel-0.6.2/tests/unit/test_mcp_http_entry.py +204 -0
  131. spectro_kernel-0.6.2/tests/unit/test_mcp_param_guard.py +204 -0
  132. spectro_kernel-0.6.2/tests/unit/test_mcp_production.py +255 -0
  133. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_mcp_security.py +105 -3
  134. spectro_kernel-0.6.2/tests/unit/test_mcp_tools_extra.py +304 -0
  135. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_measure_arc_geometry.py +86 -2
  136. spectro_kernel-0.6.2/tests/unit/test_misc_algorithms.py +245 -0
  137. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_nebular_diagnostics.py +115 -8
  138. spectro_kernel-0.6.2/tests/unit/test_output_produces_contract.py +179 -0
  139. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_pipeline.py +38 -0
  140. spectro_kernel-0.6.2/tests/unit/test_quality.py +96 -0
  141. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_redshift_lines.py +20 -0
  142. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_registry.py +76 -1
  143. spectro_kernel-0.6.2/tests/unit/test_resample_flux_conserving.py +87 -0
  144. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_response_from_standard.py +24 -0
  145. spectro_kernel-0.6.2/tests/unit/test_rv_precision_bouchy.py +94 -0
  146. spectro_kernel-0.6.2/tests/unit/test_smoothing.py +125 -0
  147. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_timeseries.py +42 -0
  148. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_transforms.py +18 -0
  149. spectro_kernel-0.6.2/tests/unit/test_types.py +173 -0
  150. spectro_kernel-0.6.2/tests/unit/test_viz.py +106 -0
  151. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_wavelength_calibration_solar.py +37 -0
  152. spectro_kernel-0.6.2/tools/docs/__init__.py +6 -0
  153. spectro_kernel-0.6.2/tools/docs/build.py +82 -0
  154. spectro_kernel-0.6.2/tools/docs/generate.py +25 -0
  155. spectro_kernel-0.6.2/tools/docs/generate_catalogue.py +482 -0
  156. spectro_kernel-0.6.2/tools/docs/generate_llms.py +134 -0
  157. spectro_kernel-0.6.2/tools/docs/netlify.toml +20 -0
  158. spectro_kernel-0.6.0/docs/gen_catalogue.py +0 -124
  159. spectro_kernel-0.6.0/playground/README.md +0 -99
  160. spectro_kernel-0.6.0/src/spectro_kernel/algorithms/lines/equivalent_width.py +0 -84
  161. spectro_kernel-0.6.0/src/spectro_kernel/algorithms/nebular/oiii_electron_temperature.py +0 -218
  162. spectro_kernel-0.6.0/src/spectro_kernel/algorithms/quality/snr_edge.py +0 -65
  163. spectro_kernel-0.6.0/src/spectro_kernel/url_safety.py +0 -142
  164. spectro_kernel-0.6.0/src/spectro_mcp/__main__.py +0 -127
  165. spectro_kernel-0.6.0/src/spectro_mcp/auth.py +0 -88
  166. spectro_kernel-0.6.0/src/spectro_mcp/auto_tools.py +0 -390
  167. spectro_kernel-0.6.0/tests/reference/data/.gitkeep +0 -1
  168. spectro_kernel-0.6.0/tests/reference/data/README.md +0 -57
  169. spectro_kernel-0.6.0/tests/reference/data/sun/sun_reference_stis_002.fits +0 -3
  170. spectro_kernel-0.6.0/tests/reference/data/vega/alpha_lyr_stis_011.fits +0 -3
  171. spectro_kernel-0.6.0/tests/unit/test_combine.py +0 -61
  172. spectro_kernel-0.6.0/tests/unit/test_corrections.py +0 -60
  173. spectro_kernel-0.6.0/tests/unit/test_doppler_tomogram.py +0 -79
  174. spectro_kernel-0.6.0/tests/unit/test_io.py +0 -54
  175. spectro_kernel-0.6.0/tests/unit/test_lines.py +0 -50
  176. spectro_kernel-0.6.0/tests/unit/test_mcp_production.py +0 -140
  177. spectro_kernel-0.6.0/tests/unit/test_misc_algorithms.py +0 -124
  178. spectro_kernel-0.6.0/tests/unit/test_quality.py +0 -38
  179. spectro_kernel-0.6.0/tests/unit/test_smoothing.py +0 -41
  180. spectro_kernel-0.6.0/tests/unit/test_types.py +0 -64
  181. spectro_kernel-0.6.0/tests/unit/test_viz.py +0 -36
  182. spectro_kernel-0.6.0/website/README.md +0 -71
  183. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/LICENSE +0 -0
  184. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/concepts/architecture.md +0 -0
  185. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/concepts/data-types.md +0 -0
  186. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/concepts/pipelines.md +0 -0
  187. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/cookbook/bess-dashboard.md +0 -0
  188. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/cookbook/multi-star-viewer.md +0 -0
  189. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/cookbook/web-playground.md +0 -0
  190. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_ew_timeseries.png +0 -0
  191. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_halpha_phase_stack.png +0 -0
  192. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_halpha_timeseries.png +0 -0
  193. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_overlay.png +0 -0
  194. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_pc1_vs_phase.png +0 -0
  195. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_pca_2d.png +0 -0
  196. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_pca_3d.png +0 -0
  197. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_periodogram.png +0 -0
  198. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_phase_folded.png +0 -0
  199. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_rv_timeseries.png +0 -0
  200. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_similarity.png +0 -0
  201. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_similarity_date.png +0 -0
  202. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_halpha_phase_stack.png +0 -0
  203. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_pc1_vs_phase.png +0 -0
  204. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_pca_2d.png +0 -0
  205. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_pca_3d.png +0 -0
  206. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_periodogram.png +0 -0
  207. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_phase_folded.png +0 -0
  208. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_rv_timeseries.png +0 -0
  209. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_similarity_date.png +0 -0
  210. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_similarity_phase.png +0 -0
  211. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/alpha-cyg-time-series.md +0 -0
  212. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/alpha-dra-binary-period.md +0 -0
  213. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/aurora-line-monitor.md +0 -0
  214. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/be-star-variability.md +0 -0
  215. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/claude-mcp-end-to-end.md +0 -0
  216. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/exoplanet-transit-rv.md +0 -0
  217. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/full-reduction-walkthrough.md +0 -0
  218. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/native-vs-easyspec-showdown.md +0 -0
  219. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/notebooks/your-first-sb2.md +0 -0
  220. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/tutorials/add-an-algorithm.md +0 -0
  221. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/tutorials/analyse-a-spectrum.md +0 -0
  222. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/usage/cli.md +0 -0
  223. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/docs/usage/library.md +0 -0
  224. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/__init__.py +0 -0
  225. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/adapters/__init__.py +0 -0
  226. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/adapters/easyspec.py +0 -0
  227. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/__init__.py +0 -0
  228. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/_common.py +0 -0
  229. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/__init__.py +0 -0
  230. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/aperture_photometry.py +0 -0
  231. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/__init__.py +0 -0
  232. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/gaia.py +0 -0
  233. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/simbad.py +0 -0
  234. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/vizier.py +0 -0
  235. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/classification/__init__.py +0 -0
  236. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/__init__.py +0 -0
  237. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_polynomial.py +0 -0
  238. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_region.py +0 -0
  239. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/__init__.py +0 -0
  240. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/barycentric.py +0 -0
  241. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/doppler_shift.py +0 -0
  242. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/__init__.py +0 -0
  243. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/__init__.py +0 -0
  244. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/_bess_contract.py +0 -0
  245. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_csv.py +0 -0
  246. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_fits.py +0 -0
  247. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_fits_bess.py +0 -0
  248. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_votable.py +0 -0
  249. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/__init__.py +0 -0
  250. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/optimal.py +0 -0
  251. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/sky_lateral_bands.py +0 -0
  252. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/__init__.py +0 -0
  253. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_ascii.py +0 -0
  254. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_echelle.py +0 -0
  255. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_sdss.py +0 -0
  256. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_votable.py +0 -0
  257. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/kinematics/__init__.py +0 -0
  258. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/__init__.py +0 -0
  259. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/_profiles.py +0 -0
  260. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/catalogs.py +0 -0
  261. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/compare_line_fits.py +0 -0
  262. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/fit_gaussian.py +0 -0
  263. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/fit_lorentzian.py +0 -0
  264. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/fit_voigt.py +0 -0
  265. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/nebular/__init__.py +0 -0
  266. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/__init__.py +0 -0
  267. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/snr_der.py +0 -0
  268. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/validate_bess_header.py +0 -0
  269. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/__init__.py +0 -0
  270. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/bias_combine.py +0 -0
  271. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/clip_cosmic_rays.py +0 -0
  272. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/dark_subtract.py +0 -0
  273. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/geometry.py +0 -0
  274. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/__init__.py +0 -0
  275. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/measure.py +0 -0
  276. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/precision_bouchy.py +0 -0
  277. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/__init__.py +0 -0
  278. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/stacking/__init__.py +0 -0
  279. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/timeseries/__init__.py +0 -0
  280. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/__init__.py +0 -0
  281. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/extract_region.py +0 -0
  282. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/mask_range.py +0 -0
  283. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/__init__.py +0 -0
  284. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_3d_surface.py +0 -0
  285. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_animation.py +0 -0
  286. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_dynamic_spectrum.py +0 -0
  287. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_plotly.py +0 -0
  288. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/__init__.py +0 -0
  289. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/in_situ.py +0 -0
  290. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/lamp_atlas.py +0 -0
  291. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/reidentify.py +0 -0
  292. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/base.py +0 -0
  293. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/errors.py +0 -0
  294. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/io/__init__.py +0 -0
  295. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/io/ascii.py +0 -0
  296. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/io/votable.py +0 -0
  297. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/__init__.py +0 -0
  298. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/balmer_quick.yaml +0 -0
  299. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/embed_quick.yaml +0 -0
  300. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/quality_report.yaml +0 -0
  301. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/rv_quick.yaml +0 -0
  302. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/snr_check.yaml +0 -0
  303. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/time_series_overview.yaml +0 -0
  304. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/reduction/full_reduction_easyspec.yaml +0 -0
  305. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/loader.py +0 -0
  306. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/py.typed +0 -0
  307. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/__init__.py +0 -0
  308. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/catalog.py +0 -0
  309. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/enums.py +0 -0
  310. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/history.py +0 -0
  311. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/line.py +0 -0
  312. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_kernel/types/timeseries.py +0 -0
  313. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_mcp/__init__.py +0 -0
  314. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_mcp/observability.py +0 -0
  315. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/src/spectro_mcp/py.typed +0 -0
  316. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/conftest.py +0 -0
  317. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/reference/conftest.py +0 -0
  318. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/reference/test_known_answers.py +0 -0
  319. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/reference/test_sun.py +0 -0
  320. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/reference/test_vega.py +0 -0
  321. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_barycentric_bjd.py +0 -0
  322. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_base.py +0 -0
  323. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_detect_lines_blind.py +0 -0
  324. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_detect_trace.py +0 -0
  325. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_easyspec_apply_staging.py +0 -0
  326. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_easyspec_wrappers.py +0 -0
  327. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_embedding_pretrained.py +0 -0
  328. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_embedding_remote_lick.py +0 -0
  329. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_export_fits_bess.py +0 -0
  330. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_extract_optimal.py +0 -0
  331. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_geometry_corrections.py +0 -0
  332. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_lamp_atlas_match.py +0 -0
  333. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_mcp.py +0 -0
  334. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_no_circular_imports.py +0 -0
  335. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_normalize_to_region.py +0 -0
  336. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_read_sdss.py +0 -0
  337. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_reidentify.py +0 -0
  338. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_rotation_curve.py +0 -0
  339. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_sky_lateral_bands.py +0 -0
  340. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_url_safety_kernel.py +0 -0
  341. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_validate_bess_header.py +0 -0
  342. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_vr_ratio.py +0 -0
  343. {spectro_kernel-0.6.0 → spectro_kernel-0.6.2}/tests/unit/test_wavelength_calibration_in_situ.py +0 -0
@@ -164,6 +164,11 @@ venv.bak/
164
164
  # Rope project settings
165
165
  .ropeproject
166
166
 
167
+ # documentation - generated by tools/docs/generate.py
168
+ docs/algorithms/
169
+ docs/ai/
170
+ docs/llms.txt
171
+
167
172
  # mkdocs documentation
168
173
  /site
169
174
 
@@ -222,6 +227,9 @@ __marimo__/
222
227
  # GitHub, not in the PyPI sdist, not on the docs site.
223
228
  stuff/
224
229
 
230
+ # Node — the website's dependencies (never committed, never in the sdist).
231
+ node_modules/
232
+
225
233
  # Playground — personal FITS files, generated plots and the local venv.
226
234
  # The example script and README ARE tracked; only user data + outputs are not.
227
235
  playground/datas/
@@ -6,6 +6,400 @@ Until `1.0.0` the public API may change between minor versions.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.6.2] — 2026-09-19
10
+
11
+ A documentation and web release : the docs move to a generated,
12
+ agent-readable site at https://docs.spectrokernel.io/, the landing page is
13
+ rebuilt around the open MCP server, and the server gains CORS so the web
14
+ playground can use it. No algorithm changes.
15
+
16
+ ### MCP server
17
+
18
+ - CORS for browser clients in HTTP mode : `SPECTRO_MCP_CORS_ORIGINS`
19
+ (default : the spectrokernel.io sites ; `*` or `none`), with the
20
+ `Mcp-Session-Id` header exposed, so the web playground on the landing
21
+ page can talk to the public server. Pre-flights are answered before the
22
+ API-key check.
23
+
24
+ ### Documentation — new build pipeline and hosting (no package change)
25
+
26
+ - The site is now built with **Zensical** (the Material for MkDocs team's
27
+ successor engine, reading the unchanged `mkdocs.yml`) and published at
28
+ **https://docs.spectrokernel.io/** by Netlify, which builds it from the
29
+ repository (base directory `tools/docs`, see `tools/docs/netlify.toml`) ;
30
+ pull requests get a deploy preview and GitHub Actions keeps a strict build
31
+ check. `mkdocs build` keeps working as a fallback.
32
+ - The algorithm catalogue is generated *before* the build by
33
+ `tools/docs/generate_catalogue.py` (Zensical runs no MkDocs plugin such as
34
+ `gen-files`) : one page per algorithm under `/algorithms/<category>/<name>/`
35
+ (summary, backend, version, context reads/writes, parameter table, Python /
36
+ CLI / MCP call examples, references, related algorithms), a category index,
37
+ and a navigation tree consumed by `literate-nav`.
38
+ - Machine-readable layer for agents and tooling : `/ai/algorithms.json`
39
+ (validated at build time against `/ai/schemas/algorithms.schema.json`,
40
+ schema version 1, stamped with the package version), a curated `/llms.txt`
41
+ derived from the navigation, and the Markdown source of every page served
42
+ next to its HTML (`<page>/index.md`). Netlify headers set the content types
43
+ and CORS on `/ai/*`.
44
+ - `tools/docs/build.py` reproduces the CI build locally (generators + strict
45
+ build + post-processing) ; `tools/docs/generate.py && zensical serve` for
46
+ live preview. Generated files (`docs/algorithms/`, `docs/ai/`,
47
+ `docs/llms.txt`) are git-ignored. `.readthedocs.yaml` and
48
+ `docs/gen_catalogue.py` are removed ; the `docs` extra now installs
49
+ `zensical`, `mkdocstrings[python]`, `mkdocs-material`,
50
+ `mkdocs-literate-nav`, `jsonschema`.
51
+
52
+ ## [0.6.1] — 2026-09-19
53
+
54
+ A correctness and hardening release driven by a full audit of the
55
+ library (`stuff/spectro_kernel_audit_19_09_26.md`, local) : one
56
+ critical hole in the public MCP server, ten numerical bugs in the
57
+ catalogue, and a long tail of robustness, reference and test fixes.
58
+ No algorithm, parameter, context key or metric key is renamed or
59
+ removed. Bricks whose numerical output changed carry a MAJOR bump of
60
+ their own `version` attribute and the measured impact is listed below,
61
+ so downstream pipelines can decide when to re-run. Catalogue unchanged
62
+ at 115 algorithms ; 399 → 620+ tests.
63
+
64
+ ### Security — MCP server (`spectro-mcp --http`)
65
+
66
+ - **Critical fix : every algorithm tool now goes through the path /
67
+ URL guards.** Until v0.6.0 only `load_spectrum` was guarded ; the
68
+ auto-generated tools ran `run_algorithm` bare, so in HTTP mode
69
+ `read_ascii_spectrum` / `read_fits` / `read_votable_spectrum` /
70
+ `read_echelle_fits` / `read_sdss_spectrum` could read any server
71
+ file, the `export_*` bricks could write anywhere, the EasySpec
72
+ wrappers could stage any directory, and `read_fits` /
73
+ `embed_remote` could reach loopback and metadata addresses.
74
+ `make_tool` now validates every parameter named `path`, `*_path`,
75
+ `*_paths`, `*_dir`, `model_path`, `target_path`, `endpoint`,
76
+ `atlas_dir` : local paths are refused under
77
+ `SPECTRO_MCP_DENY_LOCAL_PATHS` (set by `--http`), URLs go through the
78
+ SSRF guard, and URLs handed to a `read_*` brick are downloaded by the
79
+ MCP layer (no redirects, 256 MiB cap, wall-clock budget, concurrency
80
+ cap, pixel-count check) before the brick sees a temp file. `run_preset`
81
+ only accepts bundled preset names in HTTP mode. Regression tests drive
82
+ the real tool registry with the deny flag set.
83
+ - `embed_remote` v1.0.1 : the server-side `SPECTRO_EMBED_API_KEY` is
84
+ no longer attached to an arbitrary caller-supplied endpoint ; in HTTP
85
+ mode it is sent only to hosts listed in `SPECTRO_EMBED_ENDPOINT_ALLOW`.
86
+ - **HTTP mode now requires an explicit posture** : the server exits
87
+ (code 2) unless `SPECTRO_MCP_API_KEY` is set (closed) or
88
+ `SPECTRO_MCP_ALLOW_ANONYMOUS=1` is set on purpose (open). The public
89
+ demo server is open : `.do/app.yaml` declares
90
+ `SPECTRO_MCP_ALLOW_ANONYMOUS=1`, `SPECTRO_MCP_RATE_PER_MINUTE=60` and
91
+ `SPECTRO_MCP_TRUSTED_PROXY=1` (the closed variant is documented next to
92
+ it). README and `docs/usage/mcp.md` gain the full environment-variable
93
+ table. **Add `SPECTRO_MCP_ALLOW_ANONYMOUS=1` in the DigitalOcean console
94
+ before deploying this version**, otherwise the container refuses to
95
+ start.
96
+ - Key carriers : besides `X-API-Key`, the middleware accepts
97
+ `Authorization: Bearer <key>` and, when `SPECTRO_MCP_ALLOW_KEY_IN_URL=1`,
98
+ `?api_key=…` in the URL, which is what claude.ai / Claude Desktop
99
+ custom connectors need (they cannot send headers). The parameter is
100
+ removed from the request scope after validation so uvicorn's access log
101
+ never records it.
102
+ - Rate limiting : the API-key middleware is now outermost (401 before
103
+ any bucket is allocated), buckets are keyed on the validated key (or
104
+ the client IP when auth is off), idle buckets are evicted and the
105
+ table is capped ; `SPECTRO_MCP_TRUSTED_PROXY=1` makes uvicorn honour
106
+ `X-Forwarded-For` behind a load balancer. Key comparison is
107
+ constant-time (`hmac.compare_digest`).
108
+ - Memory : live sessions are capped (`SPECTRO_MCP_MAX_SESSIONS`, default
109
+ 200, LRU eviction, expired sessions purged on every `create`) ;
110
+ downloaded FITS files are refused above `SPECTRO_MCP_MAX_PIXELS`
111
+ (default 32 M elements, i.e. 256 MB as float64) before any conversion ;
112
+ `get_spectrum_data` returns at most `max_points` samples (default
113
+ 20 000, stride-downsampled, with new fields `downsampled`,
114
+ `npix_total`, `npix_returned`, `stride` ; `max_points=0` returns
115
+ everything).
116
+ - Downloads : per-download wall-clock budget
117
+ (`SPECTRO_MCP_DOWNLOAD_BUDGET_S`, 120 s, enforced inside the read loop
118
+ so a drip-feeding server cannot pin a worker thread) and a concurrency
119
+ cap (`SPECTRO_MCP_MAX_CONCURRENT_DOWNLOADS`, 4).
120
+ - Information disclosure : `FastMCP(mask_error_details=True)` in HTTP
121
+ mode ; `get_algorithm_source` withholds the absolute install path
122
+ (`path: null`, repo-relative path and GitHub URL kept).
123
+ - Every tool response is now strict JSON : `NaN` / `±inf` metrics are
124
+ serialised as `null`, numpy scalars as Python numbers (both channels).
125
+ - `spectro_kernel.url_safety` (library guard) : refuses every
126
+ non-global address (adds CGNAT 100.64/10 and friends), judges NAT64
127
+ (`64:ff9b::/96`) and IPv4-mapped addresses on the embedded IPv4 (a
128
+ public host resolved through a DNS64 resolver was wrongly refused —
129
+ this was the one failing test), and `open_safe_url` pins the
130
+ connection to the validated address (one DNS lookup, closes the
131
+ rebinding window). The public-URL test no longer touches the network.
132
+ - Dockerfile : the dependency layer is built from `pyproject.toml`
133
+ before `COPY src`, so source-only changes no longer reinstall the
134
+ scientific stack. Same specifiers, no lockfile.
135
+
136
+ ### Core
137
+
138
+ - Registry discovery is thread-safe : `ensure_discovered()` runs under a
139
+ lock and flags completion only after the import walk. A thread pool
140
+ hitting the registry cold used to see `AlgorithmNotFoundError` on 5 of
141
+ 6 threads. `clear_registry()` followed by any registry call now
142
+ restores the full catalogue.
143
+ - Provenance hashing is memoised on array identity : a 4096² frame
144
+ hashes in 4 µs after the first 390 ms, so a six-step reduction on ten
145
+ raw frames no longer spends ~20 s re-hashing untouched inputs.
146
+ Digests are byte-identical to v0.6.0 for the same content. In-place
147
+ mutation of `Spectrum1D` / `ImageFrame` arrays after hashing is
148
+ unsupported (documented ; no brick does it).
149
+ - Provenance correctness : `Spectrum1D.content_hash` now covers `mask`
150
+ and `WorkContext.hash_state` covers non-empty `extras`, `periodograms`,
151
+ `light_curves` and `exports`, so `mask_range`, `extract_sky_lateral_bands`,
152
+ `response_from_standard`, `lomb_scargle`, `synth_telluric`, `export_*`
153
+ no longer record `output_hash == input_hash`. Digests change only for
154
+ contexts that carry those slots. The MCP `load_spectrum*` tools append
155
+ a `load_spectrum` step when they replace the spectrum of a session
156
+ that already has history, so the input-hash chain stays valid.
157
+ - `PipelineBuilder.from_config` : `params: null` is accepted ; a string
158
+ `depends_on` or a non-mapping `params` raise `PipelineError` instead of
159
+ a `TypeError` / silent character split. The CLI `pipeline` report is
160
+ built from the audit trail, so after an early stop in DAG mode it no
161
+ longer blames a step that never ran.
162
+ - `Spectrum1D.wavelength_min/max` return `nan` on empty arrays ;
163
+ `preview()` never raises ; the MCP loaders return `{"error": …}` for
164
+ unreadable or empty inputs.
165
+ - Packaging : the sdist no longer ships `website/` (153
166
+ `node_modules` files had leaked in), `playground/` or the Git-LFS
167
+ pointer stubs of `tests/reference/data` ; `node_modules/` is
168
+ git-ignored. CI gains a `test-extras` job installing `[all,dev]`, so
169
+ the EasySpec, plotly, photutils, h5py, astroquery and pywavelets tests
170
+ actually run on GitHub (they were always skipped there).
171
+
172
+ ### Fixed — numerical (MAJOR bump of the brick)
173
+
174
+ - **`oiii_electron_temperature` 1.0.0 → 2.0.0.** The 7.90 prefactor of
175
+ Osterbrock & Ferland 2006 eq. 5.4 applies to
176
+ `[F(4959)+F(5007)]/F(4363)` ; the brick applied it to the 5007-only
177
+ ratio, biasing Te high by 9–16 % across the validity range. λ4959 is
178
+ now fitted too (new optional `lambda_4959`, default 4958.911 Å) with a
179
+ fallback to `F(5007)·(1+1/2.98)` (Storey & Zeippen 2000) when it
180
+ cannot be fitted or its measured 5007/4959 falls outside [2.0, 4.5].
181
+ Measured : spectrum with 5007/4363 = 100 → doublet ratio 133.6 →
182
+ **Te 12 961 K → 11 635 K** ; 5007/4363 = 50 → 17 830 → 15 413 K ;
183
+ 200 → 10 181 → 9 344 K. `metrics.ratio` is now the doublet ratio ;
184
+ additive `oiii_ratio`, `oiii_ratio_doublet`, `ratio_5007_4363`,
185
+ `flux_4959`, `ratio_5007_4959`, `oiii_doublet_from_fit`,
186
+ `extras.oiii_doublet_source`. The acceptance test carries the
187
+ Osterbrock values.
188
+ - **`doppler_tomogram` 1.0.0 → 2.0.0.** Back-projection used
189
+ `v = γ − Vx sin 2πφ + Vy cos 2πφ` ; Marsh & Horne 1988 eq. 1 is
190
+ `v = γ − Vx cos 2πφ + Vy sin 2πφ`. A spot with RV curve γ + K sin 2πφ
191
+ (the donor, MH position (0, +K)) landed at (−K, 0) : v1 maps are the
192
+ v2 maps rotated `(Vx, Vy) → (Vy, −Vx)`. Test generator switched to the
193
+ MH convention with an explicit (0, +K) assertion.
194
+
195
+ - **`measure_arc_geometry` 1.0.0 → 2.0.0.** The returned `slant_deg`
196
+ had the opposite sign of what `correct_slant_affine` expects, so the
197
+ documented measure → correct chain doubled the slant instead of
198
+ removing it (synthetic +0.05 px/row arc : residual +0.10 px/row after
199
+ correction, now 0.0001). The sign is negated in the measuring brick
200
+ only ; `correct_slant_affine` is untouched, so hand-tuned preset angles
201
+ keep their meaning. Round-trip test added (the smile half of the chain
202
+ was already exact).
203
+ - **`extract_spectrum_easyspec` 1.0.0 → 2.0.0.** The uint16
204
+ `BZERO`/`BSCALE` leak fixed in v0.2.1 for the `_easyspec_apply`
205
+ wrappers was still present in the extraction wrapper's own staging :
206
+ a uint16 science frame came back +32768. Structural and scaling
207
+ keywords are now filtered with the same `_filter_structural` helper.
208
+ - **`extract_spectrum_sum` 1.0.0 → 2.0.0.** Its pixel axis was 1-based
209
+ (`arange(n) + 1`) while every other extractor, the lamp identifiers
210
+ and `wavelength_calibrate_polynomial` are 0-based : a sum-extracted
211
+ lamp calibrated through `match_lamp_lines` was off by one pixel
212
+ (+0.9 Å at 0.9 Å/px) with a clean RMS. Now 0-based ; the convention is
213
+ stated in both bricks.
214
+ - **`extract_spectrum_boxcar` 1.0.0 → 2.0.0.** The sky estimate was
215
+ the mean of the two flanking windows with an empty window counted as
216
+ zero, and the windows are bounded by the *median* trace, so a tilted
217
+ trace halved the sky on many columns (audit scenario : 163 of 400
218
+ columns at sky = 50 instead of 100 ; now 0). Only non-empty windows
219
+ are averaged ; `metrics.boxcar_columns_without_sky` counts the columns
220
+ with neither. `shift_y_pixels` must now exceed `trace_half_width`
221
+ (it silently produced a zero sky otherwise) and negative values are
222
+ refused ; the two contradictory docstrings agree. Untilted traces are
223
+ bit-identical to v1.
224
+ - **`stack_spectra` 1.0.0 → 2.0.0.** `np.interp` extrapolated each
225
+ spectrum's edge value across the whole reference grid : two spectra
226
+ covering 4000–5000 Å (flux 1) and 4500–5500 Å (flux 3) averaged to 2.0
227
+ at 4000 Å. Samples outside a spectrum's coverage are now NaN and
228
+ ignored by the combiners (also for `sum`) ; `metrics.n_nan_samples`
229
+ reports how many reference samples had no contributor. The brick also
230
+ gains a `backend` and a reference (Tody 1993, IRAF `scombine`).
231
+ - **`dark_combine` 1.0.0 → 2.0.0, `flat_combine` 1.0.0 → 2.0.0.** The
232
+ sigma-clipped mean floored the MAD scale at machine epsilon, so any
233
+ stack with MAD = 0 (integer-ADU darks with ~1 ADU read noise) rejected
234
+ every value that differed from the median and degenerated to it
235
+ (`[10,10,10,11,12]` → 10.0). When MAD is zero the scale now falls back
236
+ to the mean absolute deviation about the median (Gaussian-scaled),
237
+ which still rejects a cosmic ray in that regime (`[10,10,10,11,1000]`
238
+ → 10.25, whereas a `nanstd` fallback would have kept it). Result :
239
+ 10.6. Stacks with MAD > 0 are unchanged.
240
+ - **`wavelength_calibration_solar` 1.0.0 → 2.0.0.** When several peaks
241
+ matched the same Fraunhofer line, the tie-break compared the catalogue
242
+ wavelength with itself and always kept the first peak ; it now keeps
243
+ the closest (a spurious dip 6 Å blueward of Hα no longer steals the
244
+ identification). New artifact `matched_pixels`.
245
+ - **`read_fits` 1.0.0 → 2.0.0 (`io/fits.py`).** A `CTYPE1` ending in
246
+ `-LOG` (`WAVE-LOG`, `AWAV-LOG`) was decoded as IRAF log10, giving
247
+ `inf` everywhere ; it now follows WCS Paper III (Greisen et al. 2006)
248
+ λ = CRVAL1·exp((p − CRPIX1)·CDELT1/CRVAL1), verified against
249
+ `astropy.wcs` to 1e-9. `10**` applies only to `DC-FLAG = 1`. A `-LOG`
250
+ axis with `CRVAL1 = 0` raises `IOReadError`.
251
+
252
+ ### Changed — analysis-side bricks (additive, no numerical change for valid input)
253
+
254
+ - `cross_correlate_rv` 2.0.0 → 2.1.0 : `n_grid=None` derives the
255
+ log-λ grid from the finest native sampling of the two spectra (capped
256
+ at 2^18). The default stays 4096, which is badly under-sampled for
257
+ high-resolution input : on a 30 000-pixel synthetic (σ_line 0.15 Å,
258
+ S/N 100) an injected +10 km/s comes back as +0.9 ± 0.15 km/s at the
259
+ default 41 km/s step and +9.97 ± 0.02 km/s with `n_grid=None`. σ_v
260
+ (Tonry & Davis) is documented as a lower bound at coarse grids. New
261
+ `metrics.n_grid_used`, `metrics.velocity_step_kms`,
262
+ `extras.template_source` ; the `ctx.spectra[0]` template fallback is
263
+ deprecated (`DeprecationWarning` + message) because it contradicts the
264
+ "second spectrum lives in `ctx.extras`" rule.
265
+ - `equivalent_width` 1.0.0 → 1.1.0 : σ(EW) per Vollmann & Eversberg
266
+ 2006 (AN 327, 862) eq. 7 as `metrics.equivalent_width_error_angstrom`
267
+ when the spectrum carries uncertainties ; new optional `label` with
268
+ per-label keys next to the flat ones ; references added.
269
+ - `fit_keplerian_orbit` 1.0.0 → 1.1.0 : an explicit `0.0` initial guess
270
+ is honoured (`or` → `is None`) ; additive `reduced_chi_squared` when
271
+ RV uncertainties are present ; ω bounds and the unweighted
272
+ `chi_squared` documented as implemented.
273
+ - `disentangle_sb2` 1.0.0 → 1.1.0 : cites Bagnuolo & Gies 1991 (ApJ
274
+ 376, 266) as the implemented scheme, documents the additive continuum
275
+ degeneracy, adds `metrics.disentangle_residual_rms` ; first numerical
276
+ test.
277
+ - `classify_template_chi2` 1.0.0 → 1.1.0 : `atlas_dir` (path) as an
278
+ alternative to the Python `atlas` object, so the brick is CLI/MCP
279
+ drivable ; `required_params` is now empty (one of the two is validated
280
+ at run time) ; per-template normalisation cached on the atlas
281
+ (identical numbers) ; `grid_lo/grid_hi/grid_step` documented as
282
+ reserved.
283
+ - `sii_electron_density` 1.0.0 → 1.1.0, `bpt_line_ratios` 1.0.0 →
284
+ 1.1.0 : prefixed keys (`sii_ratio`, `sii_regime`, `bpt_method`, …)
285
+ next to the generic `ratio` / `regime` / `method`, which the three
286
+ nebular bricks used to overwrite in a pipeline.
287
+ - `lomb_scargle` 1.1.0 → 1.1.1 : the false-alarm probability is
288
+ evaluated on the frequency range actually searched (unchanged with
289
+ default bounds ; with `maximum_frequency=1.0` on the test series
290
+ 1.85e-210 → 2.95e-211).
291
+ - `redshift_lines` 1.0.0 → 1.0.1 : `z_error` documented as the
292
+ population std of the per-line z ; descending axes and non-finite
293
+ samples handled (v1 answered "0 lines matched") ; needs ≥ 6 finite
294
+ samples.
295
+
296
+ ### Changed — reduction-side bricks (additive / robustness)
297
+
298
+ - `resample_flux_conserving` 1.0.0 → 1.0.1 : failed for the kernel's
299
+ own default flux unit (`ADU`) and for `counts` / `transmission`
300
+ because `astropy.units.Unit(..., parse_strict="silent")` returns an
301
+ `UnrecognizedUnit` instead of raising ; units are now mapped
302
+ (`adu`, `count`, else dimensionless). First tests for the brick,
303
+ including flux conservation on a top-hat to 1e-6.
304
+ - `remove_telluric_division` 1.0.0 → 1.1.0 : when the default
305
+ `telluric_spectrum` key is absent it falls back to
306
+ `telluric_template_scaled` then `telluric_template`, so the documented
307
+ `synth_telluric → fit_telluric_scaling → remove_telluric_division`
308
+ chain works with defaults (in-band residual 0.40 → 0.03 on the test
309
+ case). Records `meta["telluric_template_key"]`.
310
+ - EasySpec wrappers (all PATCH) : sessions are serialised behind one
311
+ lock, user paths are resolved to absolute before the working
312
+ directory switch, `stage_frames` tolerates duplicate basenames, and
313
+ the four wrappers that did their own `os.chdir` now use the shared
314
+ context manager. Cheap parameter validation in
315
+ `flux_calibrate_easyspec` / `extinction_correct_easyspec` /
316
+ `extract_spectrum_boxcar` runs before the EasySpec import, so
317
+ refusals are reachable without it.
318
+ - `subtract_sky_2d` 1.0.0 → 1.0.1 : one 2-D `polyfit` instead of a
319
+ per-column loop (200×2048 : 0.21 s → 0.007 s, max |Δ| 6e-14, tested
320
+ against the verbatim v1 loop for orders 0–3).
321
+ - `outlier_rejection_mad_adaptive` 1.0.0 → 1.0.1 : rows are processed
322
+ in chunks sized from a 64 MB budget (a 2048², k = 5 frame needed
323
+ ~0.8 GB transient) ; bit-identical output, tested.
324
+ - `export_hdf5` 1.0.1 (file-descriptor leak), `resample_linear` 1.0.1
325
+ (own header dict), `response_from_standard` / `flat_normalize` /
326
+ `flat_combine` docstrings aligned with the code (no masking, global
327
+ median normalisation imprints the lamp continuum, `spline_knots` is a
328
+ smoothing factor, exposure-time normalisation is the caller's job).
329
+
330
+ ### Fixed — robustness (PATCH bumps)
331
+
332
+ - NaN no longer yields `success=True` with garbage : `snr_edge`,
333
+ `snr_linear_fit` (SNR = inf) and `normalize_edges` (all-NaN output)
334
+ mask non-finite samples and fail when too few remain ; the
335
+ `embed_*` family fails cleanly (or drops non-finite samples where the
336
+ recipe is sampling-independent : `embed_band_power`,
337
+ `embed_lick_indices`, `embed_log_lambda`) instead of returning a zero
338
+ vector ; `smooth_gaussian` / `smooth_savgol` bridge NaN before
339
+ filtering and restore it afterwards (one NaN used to spread over 34
340
+ pixels, Savitzky-Golay used to raise). `embed_flux` raises
341
+ `ValueError` on non-finite input.
342
+ - Descending wavelength axes (negative `CDELT1`, reversed ASCII tables)
343
+ are sorted locally in `equivalent_width` (returned −0.005 instead of
344
+ +1.88 Å), `redshift_lines`, `embed_band_power`, `embed_lick_indices`,
345
+ `embed_log_lambda`, `cross_correlate_rv`, `disentangle_sb2`,
346
+ `doppler_tomogram`, `classify_template_chi2`
347
+ and, on the reduction side, in the reference read by
348
+ `combine_spectra_arithmetic` (which silently returned the science flux
349
+ unchanged with a reversed reference), `remove_telluric_division` and
350
+ `response_from_standard`. Ascending input is unchanged.
351
+ - `compare_normalisations` / `compare_smoothings` / `compare_snr_methods`
352
+ 1.0.1 : work on a slim copy of the spectrum instead of deep-copying
353
+ the whole context (gigabytes in a reduction session).
354
+ - `output_produces` declarations completed for `detect_lines`,
355
+ `vr_ratio`, `rotation_curve`, `redshift_lines`, `doppler_tomogram`,
356
+ `fit_keplerian_orbit`, `cross_correlate_rv`, `lomb_scargle`,
357
+ `disentangle_sb2`, the nebular trio and, on the reduction side, `match_lamp_lines`,
358
+ `wavelength_calibration_solar`, `fit_telluric_scaling`, `detect_trace`,
359
+ `extract_spectrum_boxcar`, `stack_spectra` ; a contract
360
+ test now checks that every written key is declared.
361
+
362
+ ### References
363
+
364
+ - Corrected : `vr_ratio` Okazaki 1991 is PASJ 43, 75 (not A&A 246, 415) ;
365
+ `embed_spectrum` no longer credits Soubiran et al. 2016 (the PASTEL
366
+ catalogue) for the DCT ; `detect_lines` cites Press et al. 2007 §10.2
367
+ for three-point parabolic centroiding instead of "standard technique"
368
+ ; the air → vacuum
369
+ coefficients of `air_to_vacuum` are attributed to the Piskunov / VALD3
370
+ inverse fit (vacuum → air stays Morton 2000, with the Hα 6562.79 →
371
+ 6564.603 Å acceptance value now tested) ; `detect_trace` cites
372
+ `scipy.signal.find_peaks`, which it actually calls, instead of
373
+ `center_of_mass` ; "standard technique" hand-waving replaced by Tody
374
+ 1986 / 1993 in `wavelength_calibrate_polynomial`, Horne 1986 §2 in
375
+ `subtract_sky_2d`, Hinkle, Wallace & Livingston 2003 + Hardie 1962 in
376
+ `synth_telluric`, Bevington & Robinson 2003 in `clip_sigma`, Tody 1993 +
377
+ Horne 1986 in `merge_echelle_orders` ; `response_from_standard` drops
378
+ the unused Hayes & Latham citation and its docstring no longer promises
379
+ an airmass step that does not exist.
380
+ - Added to bricks that had none : `equivalent_width`, `normalize_edges`,
381
+ `normalize_max`, `normalize_percentile`, `subtract_continuum`,
382
+ `snr_edge`, `snr_linear_fit`, `phase_fold`.
383
+ - Still to verify (left in place, flagged in the audit) : Baldry 1999
384
+ (`embed_log_lambda`), Bu 2014 and Sánchez-Sáez 2021 (`embed_spectrum`),
385
+ Brault 1987 (`rv_precision_bouchy`), Teyssier 2015
386
+ (`validate_bess_header`).
387
+
388
+ ### Tests
389
+
390
+ - First tests for the ten orphan bricks : `aperture_photometry`,
391
+ `simbad_query` / `gaia_query` / `vizier_query` (astroquery mocked),
392
+ `plot_dynamic_spectrum` / `plot_3d_surface_plotly` /
393
+ `plot_animation_plotly`, `export_hdf5`, `flux_calibrate_easyspec`,
394
+ `resample_flux_conserving`.
395
+ - Literature acceptance values added : Bouchy 2001 eq. 6 closed form
396
+ (89.18 m/s), Lorentzian γ / FWHM / area, Voigt Olivero-Longbothum
397
+ FWHM, Lomb-Scargle FAP vs astropy, Morton 2000 Hα 6562.79 → 6564.603 Å,
398
+ Osterbrock [O III] table, Marsh-Horne spot position, slant round trip,
399
+ partial-overlap stacking, WCS Paper III log axis.
400
+ - New MCP suites : parameter guard (12), tool extras (17), HTTP entry
401
+ point (6), rebinding / NAT64 / CGNAT cases, session cap and LRU.
402
+
9
403
  ## [0.6.0] — 2026-07-30
10
404
 
11
405
  Contributions from the SpecRelay / LineMill ecosystem audit
@@ -1,9 +1,9 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: spectro-kernel
3
- Version: 0.6.0
3
+ Version: 0.6.2
4
4
  Summary: A shared catalogue of astronomical spectroscopy algorithms, composable into reproducible pipelines, usable as a Python library, a CLI, or an MCP server.
5
5
  Project-URL: Homepage, https://github.com/matthieulel/spectro-kernel
6
- Project-URL: Documentation, https://matthieulel.github.io/spectro-kernel/
6
+ Project-URL: Documentation, https://docs.spectrokernel.io/
7
7
  Project-URL: Source, https://github.com/matthieulel/spectro-kernel
8
8
  Project-URL: Issues, https://github.com/matthieulel/spectro-kernel/issues
9
9
  Project-URL: Changelog, https://github.com/matthieulel/spectro-kernel/blob/main/CHANGELOG.md
@@ -54,9 +54,12 @@ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
54
54
  Requires-Dist: pytest>=8.0; extra == 'dev'
55
55
  Requires-Dist: ruff>=0.5; extra == 'dev'
56
56
  Provides-Extra: docs
57
- Requires-Dist: mkdocs-gen-files>=0.5; extra == 'docs'
57
+ Requires-Dist: jsonschema>=4.0; extra == 'docs'
58
+ Requires-Dist: mkdocs-literate-nav>=0.6; extra == 'docs'
58
59
  Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
59
60
  Requires-Dist: mkdocstrings[python]>=0.26; extra == 'docs'
61
+ Requires-Dist: pyyaml>=6.0; extra == 'docs'
62
+ Requires-Dist: zensical>=0.0.62; extra == 'docs'
60
63
  Provides-Extra: embedding-ml
61
64
  Requires-Dist: torch>=2.2; extra == 'embedding-ml'
62
65
  Provides-Extra: embedding-remote
@@ -178,6 +181,35 @@ transverse ones (`list_algorithms`, `describe_algorithm`, `get_algorithm_source`
178
181
  deploy the same `spectro-mcp` in HTTP mode on any container host (DigitalOcean
179
182
  App Platform, Fly.io, etc.). Local-stdio remains the recommended default.
180
183
 
184
+ In `--http` mode the server refuses local filesystem paths, fetches URLs
185
+ through an SSRF guard (no private addresses, no redirects, size and time
186
+ caps), caps sessions and memory, and rate-limits clients. Two postures :
187
+
188
+ - **Open** (the public demo server) : `SPECTRO_MCP_ALLOW_ANONYMOUS=1`. No
189
+ accounts, no key ; anyone adds `https://<host>/mcp` as a connector.
190
+ - **Closed** : `SPECTRO_MCP_API_KEY=<secret>` ; clients send it in the
191
+ `X-API-Key` header (or `?api_key=` with `SPECTRO_MCP_ALLOW_KEY_IN_URL=1`).
192
+
193
+ The server refuses to start in `--http` mode with neither. Configuration is
194
+ by environment variable (none of these affect stdio mode) :
195
+
196
+ | Variable | Default | Meaning |
197
+ |----------|---------|---------|
198
+ | `SPECTRO_MCP_API_KEY` | unset | Closed posture : shared secret clients send in the `X-API-Key` header. |
199
+ | `SPECTRO_MCP_ALLOW_ANONYMOUS` | unset | Open posture : `1` to start without a key (explicit opt-in). |
200
+ | `SPECTRO_MCP_ALLOW_KEY_IN_URL` | unset | `1` also accepts the key as `?api_key=…` in the URL (for claude.ai / Claude Desktop connectors, which cannot set headers); the parameter is scrubbed before access logging. `Authorization: Bearer <key>` is always accepted. |
201
+ | `SPECTRO_MCP_RATE_PER_MINUTE` | `0` (off) | Sliding-window request limit per API key (per client IP when auth is off). |
202
+ | `SPECTRO_MCP_TRUSTED_PROXY` | unset | `1` behind a load balancer: trust `X-Forwarded-For` for the client IP. |
203
+ | `SPECTRO_MCP_CORS_ORIGINS` | spectrokernel.io sites | Comma-separated browser origins allowed to call the server (`*` for any, `none` to disable). The `Mcp-Session-Id` header is exposed to browsers. |
204
+ | `SPECTRO_MCP_MAX_SESSIONS` | `200` | Cap on live in-memory sessions; the least recently used one is evicted. |
205
+ | `SPECTRO_MCP_MAX_PIXELS` | `32000000` | Refuse downloaded FITS files with more data elements than this (256 MB as float64); `0` disables. |
206
+ | `SPECTRO_MCP_DOWNLOAD_BUDGET_S` | `120` | Wall-clock budget for one URL download (on top of the 256 MiB cap). |
207
+ | `SPECTRO_MCP_MAX_CONCURRENT_DOWNLOADS` | `4` | Downloads allowed in flight at once. |
208
+ | `SPECTRO_EMBED_ENDPOINT_ALLOW` | unset | Comma-separated hosts `embed_remote` may call with the server-side `SPECTRO_EMBED_API_KEY` (and bypass the SSRF guard for, e.g. an internal inference service). |
209
+ | `SPECTRO_MCP_REDIS_URL`, `SPECTRO_MCP_SESSION_SECRET` | unset | Redis-backed sessions for multi-instance deployments (payloads are HMAC-signed with the secret). |
210
+ | `SPECTRO_MCP_S3_BUCKET`, `SPECTRO_MCP_S3_ENDPOINT`, … | unset | Presigned-URL uploads (`request_upload_url`). |
211
+ | `SENTRY_DSN` | unset | Error reporting. |
212
+
181
213
  ## Architecture
182
214
 
183
215
  ```
@@ -206,7 +238,7 @@ from the registry. Build it locally with:
206
238
 
207
239
  ```bash
208
240
  uv pip install -e ".[docs,all]"
209
- mkdocs serve # live preview at http://127.0.0.1:8000
241
+ python tools/docs/generate.py && zensical serve # live preview at http://127.0.0.1:8000
210
242
  ```
211
243
 
212
244
  Every algorithm declares its **provenance** — a `backend` (the library it leans on) and
@@ -92,6 +92,35 @@ transverse ones (`list_algorithms`, `describe_algorithm`, `get_algorithm_source`
92
92
  deploy the same `spectro-mcp` in HTTP mode on any container host (DigitalOcean
93
93
  App Platform, Fly.io, etc.). Local-stdio remains the recommended default.
94
94
 
95
+ In `--http` mode the server refuses local filesystem paths, fetches URLs
96
+ through an SSRF guard (no private addresses, no redirects, size and time
97
+ caps), caps sessions and memory, and rate-limits clients. Two postures :
98
+
99
+ - **Open** (the public demo server) : `SPECTRO_MCP_ALLOW_ANONYMOUS=1`. No
100
+ accounts, no key ; anyone adds `https://<host>/mcp` as a connector.
101
+ - **Closed** : `SPECTRO_MCP_API_KEY=<secret>` ; clients send it in the
102
+ `X-API-Key` header (or `?api_key=` with `SPECTRO_MCP_ALLOW_KEY_IN_URL=1`).
103
+
104
+ The server refuses to start in `--http` mode with neither. Configuration is
105
+ by environment variable (none of these affect stdio mode) :
106
+
107
+ | Variable | Default | Meaning |
108
+ |----------|---------|---------|
109
+ | `SPECTRO_MCP_API_KEY` | unset | Closed posture : shared secret clients send in the `X-API-Key` header. |
110
+ | `SPECTRO_MCP_ALLOW_ANONYMOUS` | unset | Open posture : `1` to start without a key (explicit opt-in). |
111
+ | `SPECTRO_MCP_ALLOW_KEY_IN_URL` | unset | `1` also accepts the key as `?api_key=…` in the URL (for claude.ai / Claude Desktop connectors, which cannot set headers); the parameter is scrubbed before access logging. `Authorization: Bearer <key>` is always accepted. |
112
+ | `SPECTRO_MCP_RATE_PER_MINUTE` | `0` (off) | Sliding-window request limit per API key (per client IP when auth is off). |
113
+ | `SPECTRO_MCP_TRUSTED_PROXY` | unset | `1` behind a load balancer: trust `X-Forwarded-For` for the client IP. |
114
+ | `SPECTRO_MCP_CORS_ORIGINS` | spectrokernel.io sites | Comma-separated browser origins allowed to call the server (`*` for any, `none` to disable). The `Mcp-Session-Id` header is exposed to browsers. |
115
+ | `SPECTRO_MCP_MAX_SESSIONS` | `200` | Cap on live in-memory sessions; the least recently used one is evicted. |
116
+ | `SPECTRO_MCP_MAX_PIXELS` | `32000000` | Refuse downloaded FITS files with more data elements than this (256 MB as float64); `0` disables. |
117
+ | `SPECTRO_MCP_DOWNLOAD_BUDGET_S` | `120` | Wall-clock budget for one URL download (on top of the 256 MiB cap). |
118
+ | `SPECTRO_MCP_MAX_CONCURRENT_DOWNLOADS` | `4` | Downloads allowed in flight at once. |
119
+ | `SPECTRO_EMBED_ENDPOINT_ALLOW` | unset | Comma-separated hosts `embed_remote` may call with the server-side `SPECTRO_EMBED_API_KEY` (and bypass the SSRF guard for, e.g. an internal inference service). |
120
+ | `SPECTRO_MCP_REDIS_URL`, `SPECTRO_MCP_SESSION_SECRET` | unset | Redis-backed sessions for multi-instance deployments (payloads are HMAC-signed with the secret). |
121
+ | `SPECTRO_MCP_S3_BUCKET`, `SPECTRO_MCP_S3_ENDPOINT`, … | unset | Presigned-URL uploads (`request_upload_url`). |
122
+ | `SENTRY_DSN` | unset | Error reporting. |
123
+
95
124
  ## Architecture
96
125
 
97
126
  ```
@@ -120,7 +149,7 @@ from the registry. Build it locally with:
120
149
 
121
150
  ```bash
122
151
  uv pip install -e ".[docs,all]"
123
- mkdocs serve # live preview at http://127.0.0.1:8000
152
+ python tools/docs/generate.py && zensical serve # live preview at http://127.0.0.1:8000
124
153
  ```
125
154
 
126
155
  Every algorithm declares its **provenance** — a `backend` (the library it leans on) and
@@ -103,7 +103,7 @@ principle](../why.md#the-principle-we-do-not-reinvent). Every algorithm declares
103
103
  - its **`references`** - the literature for the method, or the wrapped library.
104
104
 
105
105
  Both are visible in `spectro describe <name>`, in the
106
- [catalogue](../catalogue.md), and in every MCP tool description. Nothing is a black box:
106
+ [catalogue](../algorithms/index.md), and in every MCP tool description. Nothing is a black box:
107
107
  you can always see what an algorithm stands on and where the method comes from.
108
108
 
109
109
  ## Naming conventions
@@ -57,10 +57,10 @@ CI runs the same checks on every pull request; keep `main` green.
57
57
 
58
58
  ```bash
59
59
  uv pip install -e ".[docs,all]"
60
- mkdocs serve # live preview at http://127.0.0.1:8000
61
- mkdocs build # static build into site/
60
+ python tools/docs/generate.py && zensical serve # live preview at http://127.0.0.1:8000
61
+ python tools/docs/build.py # what CI runs: generators + strict build into site/
62
62
  ```
63
63
 
64
- The [algorithm catalogue](catalogue.md) page is *generated* from the registry at build
64
+ The [algorithm catalogue](algorithms/index.md) page is *generated* from the registry at build
65
65
  time - you never edit it by hand. Add a docstring and metadata to your algorithm and the
66
66
  catalogue page documents it automatically.
@@ -114,4 +114,4 @@ The agent now sees every algorithm as a tool. See [As an MCP server](usage/mcp.m
114
114
  catalogue](tutorials/discover-the-catalogue.md).
115
115
  - Understand the moving parts → [Concepts: architecture](concepts/architecture.md).
116
116
  - A guided, end-to-end analysis → [Tutorial: analyse a spectrum](tutorials/analyse-a-spectrum.md).
117
- - Browse everything available → [Algorithm catalogue](catalogue.md).
117
+ - Browse everything available → [Algorithm catalogue](algorithms/index.md).
@@ -49,7 +49,7 @@ through MCP, you can do from a terminal, and vice-versa.
49
49
  your first analysis in five minutes.
50
50
  - :material-sitemap: **[Concepts](concepts/architecture.md)** - the architecture,
51
51
  the data types, the registry, the pipelines.
52
- - :material-book-open-variant: **[Algorithm catalogue](catalogue.md)** - every
52
+ - :material-book-open-variant: **[Algorithm catalogue](algorithms/index.md)** - every
53
53
  algorithm, auto-generated from the code.
54
54
  - :material-school: **[Tutorials](tutorials/first-spectrum.md)** - from *"I captured a
55
55
  spectrum of a star, now what?"* all the way to writing your own algorithm.
@@ -2,7 +2,7 @@
2
2
 
3
3
  This page is generated from the source docstrings by
4
4
  [mkdocstrings](https://mkdocstrings.github.io/). It documents the public Python API; for
5
- the *algorithms* themselves see the [catalogue](catalogue.md).
5
+ the *algorithms* themselves see the [catalogue](algorithms/index.md).
6
6
 
7
7
  ## Data types
8
8
 
@@ -106,7 +106,7 @@ Over [MCP](../usage/mcp.md), the discovery tools are `list_algorithms` and
106
106
 
107
107
  ## The full reference
108
108
 
109
- The [Algorithm catalogue](../catalogue.md) page lists **every** algorithm with its
109
+ The [Algorithm catalogue](../algorithms/index.md) page lists **every** algorithm with its
110
110
  parameters, provenance and references, grouped by category. It is generated directly
111
111
  from the registry, so it is always in sync with the version you have installed.
112
112
 
@@ -149,4 +149,4 @@ checked, continuum normalised, lines identified. That is a genuine scientific re
149
149
  operation available, and how to inspect each one.
150
150
  - **A fuller, guided analysis** → [Analyse a spectrum](analyse-a-spectrum.md).
151
151
  - **Periodic targets** (Be stars, eclipsing binaries) → the `lomb_scargle` and
152
- `phase_fold` algorithms in the [catalogue](../catalogue.md).
152
+ `phase_fold` algorithms in the [catalogue](../algorithms/index.md).
@@ -167,5 +167,5 @@ Every algorithm above follows the same kernel contract:
167
167
  `ctx.image.meta` (for 2-D) or `ctx.spectrum.meta` (for 1-D), and the
168
168
  per-step `metrics` are JSON-serialisable for later auditing.
169
169
 
170
- See [Algorithm catalogue](../catalogue.md) for the full parameter and
170
+ See [Algorithm catalogue](../algorithms/index.md) for the full parameter and
171
171
  reference list of each brick.