spectro-kernel 0.7.0__tar.gz → 0.8.0__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 (379) hide show
  1. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/.gitignore +3 -0
  2. spectro_kernel-0.8.0/AGENTS.md +419 -0
  3. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/CHANGELOG.md +165 -0
  4. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/PKG-INFO +4 -2
  5. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/README.md +3 -1
  6. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/concepts/data-types.md +1 -1
  7. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/getting-started.md +11 -0
  8. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/index.md +4 -0
  9. spectro_kernel-0.7.0/docs/reference.md → spectro_kernel-0.8.0/docs/reference/python.md +3 -2
  10. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/usage/cli.md +4 -0
  11. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/usage/mcp.md +7 -0
  12. spectro_kernel-0.8.0/docs/validation/.nav.md +2 -0
  13. spectro_kernel-0.8.0/docs/validation/index.md +96 -0
  14. spectro_kernel-0.8.0/examples/mcp_client.py +57 -0
  15. spectro_kernel-0.8.0/examples/quickstart.py +37 -0
  16. spectro_kernel-0.8.0/examples/recipe_with_profile.py +58 -0
  17. spectro_kernel-0.8.0/examples/wavelength_calibration.py +53 -0
  18. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/pyproject.toml +1 -1
  19. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/activity/activity_index_caii_hk.py +10 -3
  20. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/activity/activity_index_halpha.py +10 -3
  21. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/activity/ccf_bisector.py +10 -4
  22. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/compare_normalisations.py +8 -3
  23. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/normalize_edges.py +9 -3
  24. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/normalize_max.py +13 -3
  25. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/normalize_percentile.py +12 -3
  26. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/normalize_polynomial.py +13 -5
  27. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/normalize_region.py +16 -6
  28. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/normalize_spline.py +14 -6
  29. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/subtract_continuum.py +15 -3
  30. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_band_power.py +5 -3
  31. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_continuum_subtracted.py +10 -7
  32. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_lick_indices.py +5 -3
  33. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_log_lambda.py +6 -3
  34. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_pretrained.py +12 -7
  35. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_remote.py +17 -3
  36. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_spectrum.py +10 -6
  37. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_wavelets.py +10 -5
  38. spectro_kernel-0.8.0/src/spectro_kernel/algorithms/extraction/_aperture.py +58 -0
  39. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/extraction/boxcar.py +71 -51
  40. spectro_kernel-0.8.0/src/spectro_kernel/algorithms/extraction/optimal.py +861 -0
  41. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/kinematics/vsini_fourier.py +13 -8
  42. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/_line_flux.py +6 -3
  43. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/_profiles.py +10 -4
  44. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/detect.py +19 -4
  45. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/equivalent_width.py +22 -10
  46. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/fit_gaussian.py +10 -2
  47. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/fit_lorentzian.py +8 -2
  48. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/fit_voigt.py +10 -2
  49. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/vr_ratio.py +6 -3
  50. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/nebular/balmer_decrement_extinction.py +5 -2
  51. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/nebular/bpt_line_ratios.py +6 -2
  52. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/nebular/oiii_electron_temperature.py +6 -2
  53. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/nebular/sii_electron_density.py +5 -2
  54. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/quality/measure_resolving_power.py +6 -3
  55. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/quality/snr_der.py +22 -8
  56. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/quality/snr_edge.py +5 -3
  57. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/quality/snr_linear_fit.py +5 -3
  58. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/rv/cross_correlate.py +50 -28
  59. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/rv/measure.py +6 -2
  60. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/rv/precision_bouchy.py +11 -5
  61. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/rv/redshift_lines.py +5 -3
  62. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/timeseries/temporal_variance_spectrum.py +61 -15
  63. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/embeddings.py +10 -7
  64. spectro_kernel-0.8.0/src/spectro_kernel/registry_export.py +334 -0
  65. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/spectrum.py +36 -0
  66. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/version.py +1 -1
  67. spectro_kernel-0.8.0/src/spectro_mcp/resources.py +184 -0
  68. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/server.py +11 -7
  69. spectro_kernel-0.8.0/tests/reference/cases.py +832 -0
  70. spectro_kernel-0.8.0/tests/reference/test_validation_cases.py +65 -0
  71. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_cross_correlate_extension.py +22 -8
  72. spectro_kernel-0.8.0/tests/unit/test_docs_generators.py +184 -0
  73. spectro_kernel-0.8.0/tests/unit/test_examples_run.py +63 -0
  74. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_extract_boxcar.py +99 -29
  75. spectro_kernel-0.8.0/tests/unit/test_extract_optimal.py +437 -0
  76. spectro_kernel-0.8.0/tests/unit/test_extraction_centring.py +108 -0
  77. spectro_kernel-0.8.0/tests/unit/test_mask_honoured.py +393 -0
  78. spectro_kernel-0.8.0/tests/unit/test_mcp_resources.py +159 -0
  79. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tools/docs/generate.py +7 -1
  80. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tools/docs/generate_catalogue.py +18 -199
  81. spectro_kernel-0.8.0/tools/docs/generate_cli.py +275 -0
  82. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tools/docs/generate_llms.py +4 -0
  83. spectro_kernel-0.8.0/tools/docs/generate_mcp.py +362 -0
  84. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tools/docs/generate_recipes.py +4 -28
  85. spectro_kernel-0.8.0/tools/docs/generate_validation.py +225 -0
  86. spectro_kernel-0.7.0/src/spectro_kernel/algorithms/extraction/optimal.py +0 -403
  87. spectro_kernel-0.7.0/tests/unit/test_extract_optimal.py +0 -178
  88. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/LICENSE +0 -0
  89. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/.drafts/README.md +0 -0
  90. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/.drafts/notebooks/aurora-line-monitor.md +0 -0
  91. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/concepts/algorithms.md +0 -0
  92. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/concepts/architecture.md +0 -0
  93. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/concepts/pipelines.md +0 -0
  94. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/contributing.md +0 -0
  95. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/cookbook/multi-star-viewer.md +0 -0
  96. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/cookbook/web-playground.md +0 -0
  97. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_ew_timeseries.png +0 -0
  98. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_halpha_phase_stack.png +0 -0
  99. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_halpha_timeseries.png +0 -0
  100. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_overlay.png +0 -0
  101. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_pc1_vs_phase.png +0 -0
  102. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_pca_2d.png +0 -0
  103. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_pca_3d.png +0 -0
  104. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_periodogram.png +0 -0
  105. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_phase_folded.png +0 -0
  106. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_rv_timeseries.png +0 -0
  107. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_similarity.png +0 -0
  108. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_similarity_date.png +0 -0
  109. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_halpha_phase_stack.png +0 -0
  110. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_pc1_vs_phase.png +0 -0
  111. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_pca_2d.png +0 -0
  112. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_pca_3d.png +0 -0
  113. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_periodogram.png +0 -0
  114. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_phase_folded.png +0 -0
  115. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_rv_timeseries.png +0 -0
  116. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_similarity_date.png +0 -0
  117. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_similarity_phase.png +0 -0
  118. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/alpha-cyg-time-series.md +0 -0
  119. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/alpha-dra-binary-period.md +0 -0
  120. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/be-star-variability.md +0 -0
  121. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/claude-mcp-end-to-end.md +0 -0
  122. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/exoplanet-transit-rv.md +0 -0
  123. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/full-reduction-walkthrough.md +0 -0
  124. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/native-vs-easyspec-showdown.md +0 -0
  125. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/your-first-sb2.md +0 -0
  126. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/recipes/format.md +0 -0
  127. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/recipes/index.md +0 -0
  128. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/recipes/share.md +0 -0
  129. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/tutorials/add-an-algorithm.md +0 -0
  130. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/tutorials/analyse-a-spectrum.md +0 -0
  131. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/tutorials/discover-the-catalogue.md +0 -0
  132. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/tutorials/first-spectrum.md +0 -0
  133. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/tutorials/long-slit-reduction.md +0 -0
  134. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/usage/library.md +0 -0
  135. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/why.md +0 -0
  136. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/__init__.py +0 -0
  137. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/adapters/__init__.py +0 -0
  138. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/adapters/easyspec.py +0 -0
  139. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/__init__.py +0 -0
  140. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/_common.py +0 -0
  141. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/activity/__init__.py +0 -0
  142. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/activity/_bands.py +0 -0
  143. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/advanced/__init__.py +0 -0
  144. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/advanced/aperture_photometry.py +0 -0
  145. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/advanced/disentangle_sb2.py +0 -0
  146. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/advanced/doppler_tomogram.py +0 -0
  147. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/catalogs/__init__.py +0 -0
  148. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/catalogs/gaia.py +0 -0
  149. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/catalogs/simbad.py +0 -0
  150. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/catalogs/standard_star.py +0 -0
  151. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/catalogs/vizier.py +0 -0
  152. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/classification/__init__.py +0 -0
  153. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/classification/classify_template_chi2.py +0 -0
  154. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/classification/pickles_atlas.py +0 -0
  155. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/__init__.py +0 -0
  156. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/__init__.py +0 -0
  157. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/air_vacuum.py +0 -0
  158. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/atmospheric_extinction.py +0 -0
  159. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/barycentric.py +0 -0
  160. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/deredden_interstellar.py +0 -0
  161. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/doppler_shift.py +0 -0
  162. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/extinction_correct_easyspec.py +0 -0
  163. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/fit_telluric_scaling.py +0 -0
  164. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/flux_calibrate_easyspec.py +0 -0
  165. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/remove_telluric.py +0 -0
  166. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/response_from_standard.py +0 -0
  167. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/synth_telluric.py +0 -0
  168. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/__init__.py +0 -0
  169. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/exports/__init__.py +0 -0
  170. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/exports/_bess_contract.py +0 -0
  171. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/exports/export_csv.py +0 -0
  172. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/exports/export_fits.py +0 -0
  173. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/exports/export_fits_bess.py +0 -0
  174. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/exports/export_hdf5.py +0 -0
  175. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/exports/export_votable.py +0 -0
  176. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/extraction/__init__.py +0 -0
  177. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/extraction/detect_trace.py +0 -0
  178. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/extraction/easyspec_extract.py +0 -0
  179. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/extraction/sky_lateral_bands.py +0 -0
  180. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/io/__init__.py +0 -0
  181. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/io/read_ascii.py +0 -0
  182. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/io/read_echelle.py +0 -0
  183. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/io/read_fits.py +0 -0
  184. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/io/read_sdss.py +0 -0
  185. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/io/read_votable.py +0 -0
  186. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/kinematics/__init__.py +0 -0
  187. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/kinematics/rotation_curve.py +0 -0
  188. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/__init__.py +0 -0
  189. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/catalogs.py +0 -0
  190. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/compare_line_fits.py +0 -0
  191. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/nebular/__init__.py +0 -0
  192. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/quality/__init__.py +0 -0
  193. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/quality/compare_snr_methods.py +0 -0
  194. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/quality/validate_bess_header.py +0 -0
  195. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/__init__.py +0 -0
  196. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/_easyspec_apply.py +0 -0
  197. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/_easyspec_helpers.py +0 -0
  198. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/bias_combine.py +0 -0
  199. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/clip_cosmic_rays.py +0 -0
  200. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/dark_combine.py +0 -0
  201. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/dark_subtract.py +0 -0
  202. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/denoise_2d.py +0 -0
  203. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/easyspec_bias.py +0 -0
  204. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/easyspec_cosmic_ray.py +0 -0
  205. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/easyspec_dark.py +0 -0
  206. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/easyspec_flat.py +0 -0
  207. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/easyspec_flat_normalize.py +0 -0
  208. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_bias.py +0 -0
  209. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_dark.py +0 -0
  210. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/extract_spectrum_sum.py +0 -0
  211. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/flat_combine.py +0 -0
  212. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/flat_normalize.py +0 -0
  213. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/geometry.py +0 -0
  214. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/subtract_sky_2d.py +0 -0
  215. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/wavelength_calibrate.py +0 -0
  216. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/rv/__init__.py +0 -0
  217. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/rv/fit_keplerian_orbit.py +0 -0
  218. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/smoothing/__init__.py +0 -0
  219. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/smoothing/_kernel_uncertainty.py +0 -0
  220. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/smoothing/_nan_bridge.py +0 -0
  221. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/smoothing/compare_smoothings.py +0 -0
  222. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/smoothing/smooth_gaussian.py +0 -0
  223. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/smoothing/smooth_savgol.py +0 -0
  224. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/stacking/__init__.py +0 -0
  225. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/stacking/merge_echelle_orders.py +0 -0
  226. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/stacking/stack.py +0 -0
  227. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/timeseries/__init__.py +0 -0
  228. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/timeseries/_light_curve.py +0 -0
  229. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/timeseries/box_least_squares.py +0 -0
  230. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/timeseries/lomb_scargle.py +0 -0
  231. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/timeseries/phase_dispersion_minimization.py +0 -0
  232. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/timeseries/phase_fold.py +0 -0
  233. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/transforms/__init__.py +0 -0
  234. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/transforms/clip_sigma.py +0 -0
  235. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/transforms/combine_arithmetic.py +0 -0
  236. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/transforms/extract_region.py +0 -0
  237. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/transforms/mask_range.py +0 -0
  238. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/transforms/resample.py +0 -0
  239. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/transforms/resample_flux_conserving.py +0 -0
  240. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/viz/__init__.py +0 -0
  241. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/viz/plot_3d_surface.py +0 -0
  242. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/viz/plot_animation.py +0 -0
  243. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/viz/plot_dynamic_spectrum.py +0 -0
  244. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/viz/plot_plotly.py +0 -0
  245. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/__init__.py +0 -0
  246. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/arc_geometry.py +0 -0
  247. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/easyspec_wavelength.py +0 -0
  248. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/in_situ.py +0 -0
  249. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/lamp_atlas.py +0 -0
  250. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/match_lamp.py +0 -0
  251. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/reidentify.py +0 -0
  252. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/solar.py +0 -0
  253. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/base.py +0 -0
  254. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/cli.py +0 -0
  255. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/errors.py +0 -0
  256. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/io/__init__.py +0 -0
  257. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/io/ascii.py +0 -0
  258. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/io/fits.py +0 -0
  259. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/io/votable.py +0 -0
  260. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/pipeline.py +0 -0
  261. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/__init__.py +0 -0
  262. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/catalog/analysis/balmer_quick.yaml +0 -0
  263. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/catalog/analysis/embed_quick.yaml +0 -0
  264. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/catalog/analysis/quality_report.yaml +0 -0
  265. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/catalog/analysis/rv_quick.yaml +0 -0
  266. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/catalog/analysis/snr_check.yaml +0 -0
  267. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/catalog/analysis/time_series_overview.yaml +0 -0
  268. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/catalog/reduction/full_reduction_easyspec.yaml +0 -0
  269. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/loader.py +0 -0
  270. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/preset.schema.json +0 -0
  271. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/variables.py +0 -0
  272. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/py.typed +0 -0
  273. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/registry.py +0 -0
  274. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/__init__.py +0 -0
  275. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/catalog.py +0 -0
  276. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/context.py +0 -0
  277. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/enums.py +0 -0
  278. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/history.py +0 -0
  279. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/image.py +0 -0
  280. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/line.py +0 -0
  281. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/timeseries.py +0 -0
  282. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/url_safety.py +0 -0
  283. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/__init__.py +0 -0
  284. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/__main__.py +0 -0
  285. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/auth.py +0 -0
  286. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/auto_tools.py +0 -0
  287. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/observability.py +0 -0
  288. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/py.typed +0 -0
  289. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/session.py +0 -0
  290. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/url_safety.py +0 -0
  291. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/conftest.py +0 -0
  292. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/reference/conftest.py +0 -0
  293. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/reference/test_known_answers.py +0 -0
  294. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/reference/test_sun.py +0 -0
  295. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/reference/test_vega.py +0 -0
  296. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_activity_bisector.py +0 -0
  297. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_activity_caii_hk.py +0 -0
  298. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_activity_halpha.py +0 -0
  299. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_aperture_photometry.py +0 -0
  300. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_barycentric_bjd.py +0 -0
  301. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_base.py +0 -0
  302. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_catalog_queries_mocked.py +0 -0
  303. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_classify_template_chi2.py +0 -0
  304. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_cli.py +0 -0
  305. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_combine.py +0 -0
  306. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_combine_arithmetic.py +0 -0
  307. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_continuum.py +0 -0
  308. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_corrections.py +0 -0
  309. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_dark_flat_combine.py +0 -0
  310. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_denoise_2d.py +0 -0
  311. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_detect_lines_blind.py +0 -0
  312. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_detect_trace.py +0 -0
  313. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_disentangle_sb2.py +0 -0
  314. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_doppler_tomogram.py +0 -0
  315. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_easyspec_apply_staging.py +0 -0
  316. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_easyspec_sessions.py +0 -0
  317. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_easyspec_wrappers.py +0 -0
  318. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_embedding.py +0 -0
  319. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_embedding_pretrained.py +0 -0
  320. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_embedding_remote_lick.py +0 -0
  321. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_embedding_tier1.py +0 -0
  322. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_export_fits_bess.py +0 -0
  323. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_export_hdf5.py +0 -0
  324. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_extract_spectrum_sum.py +0 -0
  325. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_flux_atmospheric_extinction.py +0 -0
  326. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_flux_balmer_decrement.py +0 -0
  327. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_flux_calibrate_easyspec.py +0 -0
  328. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_flux_deredden_interstellar.py +0 -0
  329. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_flux_standard_star_reference.py +0 -0
  330. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_geometry_corrections.py +0 -0
  331. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_io.py +0 -0
  332. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_io_fits_zoo.py +0 -0
  333. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_lamp_atlas_match.py +0 -0
  334. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_line_profiles.py +0 -0
  335. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_lines.py +0 -0
  336. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_mcp.py +0 -0
  337. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_mcp_http_entry.py +0 -0
  338. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_mcp_param_guard.py +0 -0
  339. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_mcp_production.py +0 -0
  340. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_mcp_security.py +0 -0
  341. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_mcp_tools_extra.py +0 -0
  342. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_measure_arc_geometry.py +0 -0
  343. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_misc_algorithms.py +0 -0
  344. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_nebular_diagnostics.py +0 -0
  345. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_no_circular_imports.py +0 -0
  346. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_normalize_spline.py +0 -0
  347. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_normalize_to_region.py +0 -0
  348. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_output_produces_contract.py +0 -0
  349. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_pipeline.py +0 -0
  350. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_presets.py +0 -0
  351. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_quality.py +0 -0
  352. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_read_sdss.py +0 -0
  353. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_redshift_lines.py +0 -0
  354. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_registry.py +0 -0
  355. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_reidentify.py +0 -0
  356. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_resample_flux_conserving.py +0 -0
  357. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_resolving_power.py +0 -0
  358. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_response_from_standard.py +0 -0
  359. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_rotation_curve.py +0 -0
  360. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_rv_precision_bouchy.py +0 -0
  361. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_sky_lateral_bands.py +0 -0
  362. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_smoothing.py +0 -0
  363. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_timeseries.py +0 -0
  364. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_timeseries_bls.py +0 -0
  365. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_timeseries_pdm.py +0 -0
  366. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_timeseries_tvs.py +0 -0
  367. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_transforms.py +0 -0
  368. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_types.py +0 -0
  369. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_uncertainty_propagation.py +0 -0
  370. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_url_safety_kernel.py +0 -0
  371. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_validate_bess_header.py +0 -0
  372. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_viz.py +0 -0
  373. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_vr_ratio.py +0 -0
  374. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_vsini_fourier.py +0 -0
  375. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_wavelength_calibration_in_situ.py +0 -0
  376. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_wavelength_calibration_solar.py +0 -0
  377. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tools/docs/__init__.py +0 -0
  378. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tools/docs/build.py +0 -0
  379. {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tools/docs/netlify.toml +0 -0
@@ -167,8 +167,11 @@ venv.bak/
167
167
  # documentation - generated by tools/docs/generate.py
168
168
  docs/algorithms/
169
169
  docs/recipes/catalogue/
170
+ docs/validation/catalogue/
170
171
  docs/ai/
171
172
  docs/llms.txt
173
+ docs/reference/cli.md
174
+ docs/reference/mcp.md
172
175
 
173
176
  # mkdocs documentation
174
177
  /site
@@ -0,0 +1,419 @@
1
+ # AGENTS.md — guidance for AI-assisted contributors
2
+
3
+ > Read this file end-to-end before adding code or proposing changes.
4
+ > It is the standing brief for **coding agents that modify spectro-kernel**
5
+ > (Claude Code loads it through `CLAUDE.md`; other agents read it directly).
6
+ > It supplements [CONTRIBUTING.md](CONTRIBUTING.md) (the human-oriented
7
+ > contributor guide) with the guard-rails that matter most when an AI is at
8
+ > the keyboard. The complementary file for agents that *use* the library is
9
+ > `docs/llms.txt` (generated, published at https://docs.spectrokernel.io/llms.txt):
10
+ > `AGENTS.md` says how an AI modifies spectro-kernel, `llms.txt` how an AI
11
+ > understands and uses it.
12
+
13
+ ---
14
+
15
+ ## 1. What the project is
16
+
17
+ `spectro-kernel` is a shared catalogue of **astronomical spectroscopy
18
+ algorithms grounded in the published literature**. The whole library
19
+ exists so that the *N*-th project that needs continuum normalisation
20
+ doesn't write a slightly-different *N*-th version. Every contribution
21
+ must keep that invariant true.
22
+
23
+ One catalogue, three doors: the Python library, the `spectro` CLI and the
24
+ `spectro-mcp` MCP server all read the same registry. **Functional parity
25
+ between the three is an invariant**, and the documentation for all three is
26
+ generated from the code (see §7).
27
+
28
+ ---
29
+
30
+ ## 2. The non-negotiables
31
+
32
+ **Before** writing or proposing an algorithm :
33
+
34
+ 1. **Is it already in the catalogue?** Run :
35
+ ```bash
36
+ .venv/bin/python -c "from spectro_kernel.registry import ensure_discovered, list_algorithms; \
37
+ ensure_discovered(); \
38
+ print('\n'.join(sorted(a['name'] for a in list_algorithms())))"
39
+ ```
40
+ Search the printed list with grep for related keywords *and* for the
41
+ nearest astronomical idiom (`bpt`, `nebular`, `redshift`,
42
+ `template`, `tomogr`, etc.). When in doubt, ask. **Duplicates are
43
+ rejected.**
44
+
45
+ 2. **Is it in the literature?** Every algorithm must cite at least
46
+ **one peer-reviewed reference** (paper, textbook, IRAF / specutils
47
+ / pPXF documentation) in its `references = […]` class attribute.
48
+ Reproduce the published formula / coefficients **verbatim** when
49
+ possible — `stuff/spectro_kernel_spec_25_06_26.md` is a worked
50
+ example. If you have to derive a coefficient yourself, that's a
51
+ research project, not a kernel contribution.
52
+
53
+ 3. **Are the references real?** Verify each citation against the
54
+ paper's abstract (arXiv / ADS / publisher DOI). A reference that
55
+ doesn't exist (or that doesn't say what you claim it says) is an
56
+ immediate revert.
57
+
58
+ 4. **Is it overlapping with astropy / specutils / pPXF / pypeit?** If
59
+ one of those tools already does the job in importable form, prefer
60
+ to **wrap** rather than re-implement. Set `backend = "astropy"` /
61
+ `"specutils"` / `"easyspec"` and forward — the catalogue layer
62
+ makes the wrap the *one* canonical entry point.
63
+
64
+ 5. **Will the implementation fit on a shelf?** The library's contract
65
+ is "one algorithm = one Python file = one `BaseAlgorithm`
66
+ subclass". If the design needs new context fields, a new pipeline
67
+ primitive, or new MCP plumbing, stop and discuss — those changes
68
+ ripple through every other algorithm and need separate review.
69
+
70
+ If you cannot answer **yes** to all five questions, do not push code.
71
+
72
+ ### Anti-patterns (rejected on sight)
73
+
74
+ - **Fabricated references.** "Smith et al. 2021" that nobody can find
75
+ in ADS is an automatic revert. Verify before citing.
76
+ - **Domain hand-waving.** "Standard astronomy practice" is not a
77
+ reference. Cite the paper that establishes the formula.
78
+ - **Duplicates.** "It's slightly different" is rarely true. If the
79
+ result is genuinely different, *why* — and is that documented as a
80
+ caveat in both bricks' `long_description`? (Example of valid
81
+ coexistence : `extract_spectrum_boxcar` vs `extract_spectrum_optimal`
82
+ — both are Horne-flavoured but the optimal variant adds variance +
83
+ cosmic rejection. The two `long_description`s cross-reference each
84
+ other so users can pick.)
85
+ - **API drift.** Don't introduce a new `ctx.something` field, a new
86
+ return-type shape, or a new registration mechanism. The triple
87
+ surface (Python library, CLI, MCP) is generated from the registry —
88
+ any drift breaks one of them silently.
89
+ - **ML-flashy reinvention.** A "Bayesian-deep-something" wrapper of a
90
+ classical algorithm is not a new algorithm if it doesn't change the
91
+ published method. Keep the kernel boring on purpose.
92
+ - **Inventing parameter names that don't match the literature.** If
93
+ the paper says ``R = F(6716)/F(6731)``, your variable is `R` and
94
+ your param is `lambda_6716` — not `lhs_line_a`.
95
+ - **Hardcoded paths to home directories, network fetches at import
96
+ time, secret tokens.** None of those belong in a kernel algorithm.
97
+
98
+ ### The kernel philosophy (the "why")
99
+
100
+ If you have to pick between two options, the right one is usually the
101
+ one closest to these principles :
102
+
103
+ - **Literature over invention.** The kernel is a curated archive of
104
+ *published* recipes. We do not invent algorithms here.
105
+ - **One algorithm = one shelf brick.** Composability comes from
106
+ pipelines composing bricks, not from monolithic super-algorithms.
107
+ - **No surprise.** Every algorithm declares its `backend`, its
108
+ `references`, its `default_params` and its `output_produces`. The
109
+ registry is a contract — code without metadata is rejected.
110
+ - **Provenance is a feature.** Each `BaseAlgorithm.execute` writes a
111
+ `ProcessingStep` into `ctx.history` (parameters, input hash, output
112
+ hash, duration, success). Don't bypass it.
113
+ - **Determinism by default.** Random sources are explicit (seeded
114
+ from `params`), file I/O is explicit, network calls are explicit
115
+ and lazy (inside `run()`, never at module import).
116
+ - **Triple surface.** Every algorithm becomes a Python function, a
117
+ `spectro run` CLI command **and** an MCP tool — for free. Don't write
118
+ per-surface code.
119
+ - **No data in the wheel.** Catalogues / atlases / templates are
120
+ loaded on demand from disk (see
121
+ [`algorithms/wavelength_calibration/lamp_atlas.py`](src/spectro_kernel/algorithms/wavelength_calibration/lamp_atlas.py)
122
+ for a small bundled one, and
123
+ [`algorithms/classification/pickles_atlas.py`](src/spectro_kernel/algorithms/classification/pickles_atlas.py)
124
+ for the externally-fetched variant). Keep the install size honest.
125
+
126
+ ---
127
+
128
+ ## 3. Architecture (memorise this)
129
+
130
+ ```
131
+ src/spectro_kernel/ the importable library — the catalogue
132
+ types/ Spectrum1D, WorkContext, ProcessingStep, LineFitResult, enums
133
+ base.py BaseAlgorithm + AlgorithmOutput (the algorithm contract)
134
+ registry.py @register_algorithm, discovery, list/describe/run_algorithm
135
+ registry_export.py the ONE builder of the catalogue JSON (algorithms + recipes)
136
+ pipeline.py Pipeline, PipelineBuilder (from_preset), PipelineResult
137
+ presets/ recipe loader, variables, validation, bundled catalog/*.yaml
138
+ io/ FITS / ASCII / VOTable readers and writers
139
+ algorithms/<category>/ one file per algorithm — auto-discovered
140
+ cli.py the `spectro` command (argparse, build_parser())
141
+ src/spectro_mcp/ the MCP server over the same registry
142
+ server.py build_server(): FastMCP + tools + resources + /health, /metrics
143
+ auto_tools.py 13 session/discovery tools + one tool per algorithm, path/URL guards
144
+ resources.py spectro://algorithms, spectro://recipes, spectro://llms.txt
145
+ tools/docs/ documentation generators (§7), build.py = what CI runs
146
+ docs/ the documentation site source (Markdown, Zensical / MkDocs)
147
+ examples/ executable, deterministic examples run by CI
148
+ tests/unit/ the test suite (one test file per brick or feature)
149
+ website/ the public landing page (React) — separate from docs/
150
+ ```
151
+
152
+ ### The algorithm pattern
153
+
154
+ Every algorithm follows this pattern (see e.g.
155
+ [`src/spectro_kernel/algorithms/continuum/normalize_polynomial.py`](src/spectro_kernel/algorithms/continuum/normalize_polynomial.py),
156
+ [`src/spectro_kernel/algorithms/nebular/oiii_electron_temperature.py`](src/spectro_kernel/algorithms/nebular/oiii_electron_temperature.py)) :
157
+
158
+ ```python
159
+ from __future__ import annotations
160
+ from typing import Any
161
+
162
+ import numpy as np
163
+
164
+ from ...base import AlgorithmOutput, BaseAlgorithm
165
+ from ...errors import InvalidParameterError
166
+ from ...registry import register_algorithm
167
+ from ...types import AlgorithmCategory, WorkContext
168
+
169
+
170
+ @register_algorithm("name", category=AlgorithmCategory.SOMETHING, version="1.0.0")
171
+ class MyAlgorithm(BaseAlgorithm):
172
+ """One-line summary (becomes the registry description)."""
173
+
174
+ backend = "numpy" # numpy | scipy | astropy | specutils | …
175
+ references = ["Author YYYY, Journal vol, page — short note."]
176
+ long_description = "Optional richer paragraph with caveats / regime of validity."
177
+ default_params = {"threshold": 3.0}
178
+ required_params = ["threshold"] # only when there is no sensible default
179
+ param_descriptions = {"threshold": "…"}
180
+ input_requirements = ["spectrum"] # "image" | "spectrum" | "spectra" | "images"
181
+ output_produces = ["metrics.x"] # documentation only
182
+
183
+ def run(self, ctx: WorkContext, params: dict[str, Any]) -> AlgorithmOutput:
184
+ spec = ctx.spectrum
185
+ if spec is None:
186
+ return AlgorithmOutput.fail("…")
187
+ # …
188
+ return AlgorithmOutput.ok(metrics={...}, message="…")
189
+ ```
190
+
191
+ **Auto-discovery** picks the file up from its category directory at
192
+ import time — no registry edit required. The metadata is the contract:
193
+ it feeds `spectro describe`, the MCP tool description, the catalogue
194
+ page and the machine-readable JSON, all generated.
195
+
196
+ ### Context conventions (do NOT add new fields)
197
+
198
+ | Data | Slot | Type |
199
+ |---|---|---|
200
+ | current 2-D frame | `ctx.image` | `ImageFrame` |
201
+ | current 1-D spectrum | `ctx.spectrum` | `Spectrum1D` |
202
+ | list of 2-D frames | `ctx.images` | `list[ImageFrame]` |
203
+ | list of 1-D spectra | `ctx.spectra` | `list[Spectrum1D]` |
204
+ | **second spectrum** (template, reference, sky, std) | `ctx.extras["<key>"]` | `Spectrum1D` |
205
+ | scalar metrics (JSON-safe) | `ctx.metrics` | `dict[str, float]` |
206
+ | line fits | `ctx.line_fits` | `dict[str, LineFitResult]` |
207
+ | anything else (artefacts, intermediate state) | `ctx.extras["<key>"]` | any JSON-safe |
208
+
209
+ **The "second spectrum lives in `ctx.extras`" rule is invariant.**
210
+ Examples: `remove_telluric_division` reads
211
+ `ctx.extras["telluric_spectrum"]`,
212
+ `combine_spectra_arithmetic` reads `ctx.extras["reference_spectrum"]`,
213
+ `response_from_standard` writes `ctx.extras["reference_spectrum"]`,
214
+ `extract_sky_lateral_bands` writes `ctx.extras["sky_spectrum"]`. If
215
+ your algorithm needs another spectrum, follow the same convention with
216
+ a self-documenting key.
217
+
218
+ ### Categories
219
+
220
+ `AlgorithmCategory` is the curated taxonomy
221
+ ([`src/spectro_kernel/types/enums.py`](src/spectro_kernel/types/enums.py)).
222
+ Use an existing member when possible. New categories require :
223
+
224
+ 1. a real use case (3+ algorithms or a clear domain gap),
225
+ 2. an enum entry,
226
+ 3. a friendly title in `CATEGORY_TITLES` of
227
+ [`src/spectro_kernel/registry_export.py`](src/spectro_kernel/registry_export.py)
228
+ (shared by the docs generators and the MCP resources),
229
+ 4. an update to the website catalogue group if the brick is
230
+ user-facing (see
231
+ [`website/components/sections.jsx`](website/components/sections.jsx)).
232
+
233
+ Adding a category is fine — the v0.4.0 release added `nebular`,
234
+ `kinematics` and `classification`. Don't add a category for one algo
235
+ unless the domain really is new.
236
+
237
+ ### The MCP layer
238
+
239
+ `spectro_mcp` depends on `spectro_kernel`, never the reverse. Tools are
240
+ generated from the registry in
241
+ [`src/spectro_mcp/auto_tools.py`](src/spectro_mcp/auto_tools.py) (one tool
242
+ per algorithm, plus 13 session / discovery tools); the catalogue is also
243
+ readable as resources in
244
+ [`src/spectro_mcp/resources.py`](src/spectro_mcp/resources.py)
245
+ (`spectro://algorithms`, `spectro://algorithms/{name}`, `spectro://recipes`,
246
+ `spectro://recipes/{name}`, `spectro://llms.txt`), backed by
247
+ `registry_export.py`. Safety (path / URL guards, masking, deny mode) lives
248
+ at the MCP layer; kernel algorithms stay policy-free. In HTTP deny mode
249
+ nothing may disclose an install path.
250
+
251
+ ---
252
+
253
+ ## 4. Recipes (presets) and profiles
254
+
255
+ A **recipe** (`src/spectro_kernel/presets/catalog/*.yaml`, or an installed
256
+ collection) composes bricks with the scientific conventions fixed;
257
+ everything that depends on an instrument or an observer is a declared
258
+ **variable**, bound at run time by a **profile** (`--profile`, `--set`,
259
+ `variables=`). The loader lives in
260
+ [`src/spectro_kernel/presets/`](src/spectro_kernel/presets/) (variables,
261
+ discovery through `SPECTRO_PRESET_PATH` and the `spectro_kernel.presets`
262
+ entry-point group, validation, `requires` range), the engine records a
263
+ `pipeline:<name>` provenance step at the end of every named recipe run.
264
+ Rules: a recipe never hard-codes an instrument value, never introduces a
265
+ brick, and campaign recipes cite references. The kernel bundles only
266
+ generic recipes; campaign recipes belong to
267
+ [spectro-kernel-recipes](https://github.com/matthieulel/spectro-kernel-recipes).
268
+ `spectro preset validate` is the gate.
269
+
270
+ ---
271
+
272
+ ## 5. Commands and environment
273
+
274
+ - Project root: `/Users/matthieu/lab/meta-spectro/spectro-kernel`
275
+ - Python : 3.12 (managed via `pyenv` ; project venv at `.venv/`).
276
+ - Run any tool via `.venv/bin/<tool>` (e.g. `.venv/bin/pytest`).
277
+ - Install for development: `uv pip install -e ".[dev,mcp]"` (add `docs`
278
+ to build the site, `all` for every optional extra).
279
+ - Lint / format: `.venv/bin/python -m ruff check src tests tools examples`
280
+ and `.venv/bin/python -m ruff format <files>` (line length 100, rules
281
+ `E F I UP B W`, see `pyproject.toml`). CI runs `ruff check src tests`.
282
+ - Docs : `.venv/bin/python tools/docs/generate.py && .venv/bin/zensical serve`
283
+ then open http://127.0.0.1:8000. Published at https://docs.spectrokernel.io/.
284
+ - MCP server stdio mode : `.venv/bin/spectro-mcp` (cloud HTTP variant
285
+ deployed on DigitalOcean).
286
+ - Examples : `.venv/bin/python examples/<file>.py` (each is
287
+ self-contained, deterministic, offline).
288
+
289
+ If you are inside Claude Code, your tools are : `Read`, `Edit`,
290
+ `Write`, `Bash`, `Agent`. Stay inside the project working directory ;
291
+ never run commands that touch `~` or `/etc`.
292
+
293
+ ---
294
+
295
+ ## 6. Tests (required for every contribution)
296
+
297
+ Each new algorithm ships with **at least one** test in `tests/unit/`.
298
+ The test must :
299
+
300
+ 1. Build a synthetic input where the right answer is **known
301
+ analytically from the cited literature** (e.g.
302
+ `tests/unit/test_nebular_diagnostics.py::test_oiii_inversion_acceptance_values`
303
+ exercises Eq. 1 from Aller 1984 directly).
304
+ 2. Assert on the numerical result with a tolerance that reflects the
305
+ method's known precision — not blindly tight, not generously loose.
306
+ 3. Cover at least one failure path (parameter validation, missing
307
+ input, regime out of validity).
308
+
309
+ When the spec we are porting from already lists *acceptance values*
310
+ (STAROS-style spec docs), put them in the test docstring verbatim.
311
+
312
+ Scientific validation against real data lives in `tests/reference/`
313
+ (known-answer tests on reference spectra); a brick that claims a published
314
+ precision should be checked there when reference data exists.
315
+
316
+ Run before pushing :
317
+
318
+ ```bash
319
+ .venv/bin/python -m pytest tests/ # the whole suite must pass
320
+ .venv/bin/python -m ruff check src tests tools examples # clean
321
+ .venv/bin/python tools/docs/build.py # generators + zensical build --strict, clean
322
+ .venv/bin/python -m build && .venv/bin/python -m twine check --strict dist/*
323
+ ```
324
+
325
+ ---
326
+
327
+ ## 7. Documentation requirements and how the generated docs work
328
+
329
+ The documentation is generated from the code wherever a source of truth
330
+ exists; **never edit a generated file**, edit the generator or the metadata
331
+ it reads. Generated outputs are git-ignored (`.gitignore`, "documentation"
332
+ block) and rebuilt by `tools/docs/generate.py` (called by `build.py`, by
333
+ CI and by Netlify):
334
+
335
+ | Generator | Reads | Writes |
336
+ |---|---|---|
337
+ | `tools/docs/generate_catalogue.py` | the registry | `docs/algorithms/**`, `docs/ai/algorithms.json` + schema |
338
+ | `tools/docs/generate_recipes.py` | the presets loader | `docs/recipes/catalogue/**`, `docs/ai/presets.json` |
339
+ | `tools/docs/generate_cli.py` | `spectro_kernel.cli.build_parser()` | `docs/reference/cli.md` |
340
+ | `tools/docs/generate_mcp.py` | `spectro_mcp.server.build_server()` | `docs/reference/mcp.md`, `docs/ai/mcp-tools.json` |
341
+ | `tools/docs/generate_llms.py` | `mkdocs.yml` nav + page descriptions | `docs/llms.txt` |
342
+
343
+ The JSON builders live in `src/spectro_kernel/registry_export.py` so the
344
+ published JSON and the MCP resources are one implementation. Hand-written
345
+ pages (`docs/concepts`, `docs/usage`, `docs/tutorials`, `docs/reference/python.md`)
346
+ link to the generated ones; the nav is in `mkdocs.yml`. `build.py` runs
347
+ Zensical in `--strict` mode: a broken link or a missing page fails CI.
348
+
349
+ A new algorithm needs **no** documentation edit: its docstring and
350
+ metadata become its page, its MCP tool description and its JSON entry. A
351
+ new CLI flag or MCP tool is documented on the next build. A new
352
+ user-facing feature gets a paragraph in the relevant `docs/usage` or
353
+ `docs/concepts` page and, when it can be shown in under 60 lines, an
354
+ example in `examples/` (deterministic, offline, registered in
355
+ `tests/unit/test_examples_run.py`, which CI runs).
356
+
357
+ ---
358
+
359
+ ## 8. Versioning
360
+
361
+ - **PATCH** (`0.x.y → 0.x.(y+1)`) : bug fix + at most one small
362
+ incremental addition (cf. v0.2.1 — uint16 BZERO leak fix + one new
363
+ `extract_spectrum_boxcar` brick).
364
+ - **MINOR** (`0.x → 0.(x+1)`) : substantive feature add (cf. v0.2.0 —
365
+ 13 long-slit bricks ; v0.4.0 — 8 science bricks + 3 new categories).
366
+ - **MAJOR** (`0.x → 1.0+`) : only after a `1.0.0` API freeze decision.
367
+
368
+ Bump the **algorithm's own** `version` attribute on any behaviour
369
+ change so old results stay reproducible. Numerical change → MAJOR
370
+ attribute bump (`1.0.0 → 2.0.0`). Performance / docstring → MINOR /
371
+ PATCH.
372
+
373
+ ---
374
+
375
+ ## 9. Release flow (when asked to ship)
376
+
377
+ 1. Update [CHANGELOG.md](CHANGELOG.md) with full per-algorithm notes
378
+ (every new brick named, every reference cited, numeric impact
379
+ measured when relevant — see the v0.2.1 / v0.3.0 / v0.4.0 entries
380
+ for the expected level of detail).
381
+ 2. Bump [`src/spectro_kernel/version.py`](src/spectro_kernel/version.py).
382
+ 3. Update the website counters / catalogue group entries in
383
+ [`website/app.jsx`](website/app.jsx) and
384
+ [`website/components/sections.jsx`](website/components/sections.jsx).
385
+ 4. Run the pre-flight quartet from section 6.
386
+ 5. Commit, push, tag (`git tag -a vX.Y.Z -m "…"`), push the tag.
387
+ 6. The
388
+ [`release.yml`](.github/workflows/release.yml)
389
+ GitHub Action runs the test matrix, builds the wheel + sdist, and
390
+ publishes to PyPI via Trusted Publisher OIDC (no token in the
391
+ repo). Watch the run ; PyPI propagation usually completes within
392
+ one minute of workflow success.
393
+
394
+ ---
395
+
396
+ ## 10. When to escalate to the user
397
+
398
+ Surface a decision to the user (rather than guessing) when :
399
+
400
+ - A new external dependency would land in the wheel (changes the
401
+ install footprint).
402
+ - A change would affect the public registry contract (renaming an
403
+ algorithm, removing a parameter, changing a return shape).
404
+ - A pipeline's deterministic output would shift numerically (algorithm
405
+ MAJOR bump scenario).
406
+ - A release would skip a version on PyPI (cf. the v0.1.5 re-tag
407
+ scenario).
408
+ - The proposed addition is genuinely on the boundary between "shelf
409
+ brick" and "downstream application" — when in doubt, the kernel
410
+ errs on the side of being smaller.
411
+
412
+ For everything routine — implementing a brick whose spec is unambiguous,
413
+ fixing a clear bug, refreshing docs — proceed and report.
414
+
415
+ ---
416
+
417
+ *This file is the standing brief for AI contributors. Keep it short —
418
+ human contributors read [CONTRIBUTING.md](CONTRIBUTING.md), AI
419
+ contributors read both.*
@@ -6,6 +6,171 @@ Until `1.0.0` the public API may change between minor versions.
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.8.0] — 2026-09-26
10
+
11
+ The "exactness" release : the four method corrections the September audit
12
+ had left for discussion are applied (each shifts numbers, hence MAJOR
13
+ bumps), a validation layer compares the kernel against published values
14
+ and real CALSPEC data on every documentation build, the agent-facing
15
+ documentation is completed (generated CLI and MCP references, MCP
16
+ resources, `AGENTS.md`, executable examples run in CI), and the recipe
17
+ collection grows to ten recipes on the 0.7.0 bricks.
18
+
19
+ ### Changed — method corrections (MAJOR bumps ; results shift)
20
+
21
+ - **`extract_spectrum_optimal` 1.0.0 → 2.0.0 : Horne 1986 to the letter.**
22
+ Checked against the paper's scan (PASP 98, 609, pp. 610–615) : eq. 8 flux
23
+ and eq. 9 variance `1/Σ P²/V` (the audit's "missing Σ M P numerator" was
24
+ not in the printed equation and is not applied), eq. 13 variance from the
25
+ **model** `V = (RON/gain)² + |f P + S|/gain` in ADU² (v1 used the data and
26
+ added RON² in e⁻² to ADU²), eq. 14 spatial profile from low-order
27
+ polynomials **along the dispersion** of `(D − S)/f` with inverse-variance
28
+ weights, σ-clipping, positivity and per-column normalisation (v1 used a
29
+ 21-column running median of the raw data), §II.E rejection of the single
30
+ worst pixel per column per iteration with f and V recomputed, §II.F
31
+ two-pass iteration. The sky S is now part of the model : from
32
+ `ctx.extras["sky_spectrum"]` (`extract_sky_lateral_bands`), else from
33
+ the rows flanking the aperture (`sky_offset`, `sky_half_width`,
34
+ `sky_poly_order`, unweighted : the 1/V weights of eq. 12 bias a Poisson
35
+ sky by ≈ −1 count), else none ; `sky_source` (`auto` skips the sky when
36
+ the frame is marked `sky_subtracted`). New params `sky_source`,
37
+ `sky_extras_key`, `sky_offset`, `sky_half_width`, `sky_poly_order`,
38
+ `profile_poly_order` ; `profile_smooth_columns` now smooths the
39
+ normalising spectrum (default 21 → 401 ; Horne's literal per-column
40
+ normaliser biases faint columns by +17 % through errors-in-variables
41
+ attenuation, the paper's own caveat). **The output flux is now
42
+ sky-subtracted** (v1 returned object + sky) ; `metrics.sky_median` added.
43
+ Measured on identical frames (gain 1, RON 3 e⁻, σ_trace 1.5 px, 65 536
44
+ columns) : bias at 5 / 20 / 100 / 1000 e⁻ per column **−23.1 / −13.7 /
45
+ −4.8 / −0.8 % → −0.3 / +0.6 / +0.06 / −0.02 %** (the boxcar control
46
+ gives the same to 0.01 % from 20 e⁻) ; reported σ over the empirical
47
+ scatter at gain 2.5, RON 6 e⁻ : **×2.40 / ×2.05 / ×1.38 → 0.995 / 1.00 /
48
+ 1.03** at 20 / 100 / 1000 e⁻ ; a 10⁵ e⁻ cosmic ray on the peak or a wing
49
+ is rejected as exactly one pixel with the column flux within 0.35 σ of
50
+ the clean extraction. 25 tests.
51
+ - **Aperture centring, `extract_spectrum_boxcar` 2.0.0 → 3.0.0 and
52
+ `extract_spectrum_optimal` (same 2.0.0)** (audit A9) : the aperture is
53
+ `round(trace) ± hw` (2·hw + 1 rows) instead of `int(trace)` with a
54
+ half-open window ; the boxcar sky windows follow the same rule (each
55
+ exactly `shift − hw` rows). Captured fraction for a Gaussian profile
56
+ σ = 1.5 px, hw = 3, at trace rows 20.0 / 20.3 / 20.5 / 20.9 : **0.947 /
57
+ 0.928 / 0.910 / 0.860 → 0.983 / 0.980 / 0.976 / 0.982** (ripple 8.7 % →
58
+ 0.7 %), identical for tophat, Gaussian and optimal weights. On a
59
+ well-sampled trace the sum has one more row (+1 × sky per column when
60
+ `shift_y_pixels = 0`) and up to +9 % flux where the old window was
61
+ off-centre.
62
+
63
+ - **`cross_correlate_rv` 2.1.0 → 3.0.0 : the default grid follows the
64
+ native sampling.** `n_grid` now defaults to `None` (the finest Δlnλ of
65
+ the two spectra over the overlap, capped at 2^18) instead of a fixed
66
+ 4096. On the audit's synthetic (30 000 pixels, σ_line 0.15 Å, S/N 100) an
67
+ injected +10 km/s came back as +0.8 ± 0.15 km/s with the old default
68
+ (41 km/s step) and comes back as +9.98 ± 0.02 km/s now (4.3 km/s step).
69
+ An explicit `n_grid=4096` still gives the old behaviour.
70
+ - **`Spectrum1D.mask` is honoured by every analysis brick** (audit C9).
71
+ New `Spectrum1D.masked_flux()` / `masked_uncertainty()` return the arrays
72
+ with NaN where the mask is True ; the analysis bricks read those, so a
73
+ masked cosmic ray or telluric no longer enters a continuum fit, a
74
+ signal-to-noise estimate, a line fit, an equivalent width, a
75
+ cross-correlation, an activity index or an embedding. Output bricks keep
76
+ the (corrected) flux at masked samples and carry the mask through.
77
+ Bumped : `normalize_polynomial`, `normalize_to_region`, `normalize_max`,
78
+ `normalize_percentile`, `normalize_edges` (→ 3.0.0), `normalize_spline`,
79
+ `subtract_continuum`, `compare_normalisations`, `snr_der`, `snr_edge`,
80
+ `snr_linear_fit`, `measure_resolving_power`, `detect_lines`,
81
+ `fit_gaussian_line`, `fit_lorentzian_line`, `fit_voigt_line`,
82
+ `equivalent_width`, `vr_ratio`, `measure_radial_velocity`,
83
+ `rv_precision_bouchy`, `redshift_lines`, `activity_index_caii_hk`,
84
+ `activity_index_halpha`, `ccf_bisector`, `vsini_fourier`, the eight
85
+ `embed_*` bricks, `oiii_electron_temperature`, `sii_electron_density`,
86
+ `bpt_line_ratios`, `balmer_decrement_extinction`,
87
+ `temporal_variance_spectrum` (→ 2.0.0 or 3.0.0 each ; every
88
+ `long_description` says so). Spectra read with a mask column or
89
+ extension (`read_fits`, `read_sdss_spectrum`) therefore give different
90
+ numbers when the mask is non-empty. Sampling-dependent embeddings
91
+ (`embed_spectrum`, `embed_continuum_subtracted`, `embed_wavelets`,
92
+ `embed_pretrained`, `embed_remote`) refuse a masked spectrum with a
93
+ clear message, as they refuse NaN ; the line fitters, the nebular flux
94
+ primitive, `equivalent_width` and `detect_lines` gained the NaN handling
95
+ this required ; the temporal variance spectrum leaves a pixel undefined
96
+ (`None`) when any epoch lacks it instead of failing. 15 tests compare
97
+ clean, spiked and masked spectra brick by brick.
98
+
99
+ ### Added — validation layer (published values and real CALSPEC data)
100
+
101
+ - `tests/reference/cases.py` is a single registry of "published value vs
102
+ measured value" cases ; `tests/reference/test_validation_cases.py` runs
103
+ them in the suite and `tools/docs/generate_validation.py` renders them at
104
+ every documentation build as [Validation](https://docs.spectrokernel.io/validation/)
105
+ with `docs/ai/validation.json` (a failing case shows ✗ with the numbers,
106
+ it never breaks the docs build ; the tests are where it blocks CI).
107
+ - 14 literature cases on synthetic inputs, all passing : Morton 2000 Hα
108
+ air → vacuum (6564.603 Å), Stoehr 2008 DER_SNR, Olivero & Longbothum
109
+ 1977 Voigt FWHM (6.5160 Å), Bouchy 2001 (89.18 m/s), Osterbrock &
110
+ Ferland 2006 [O III] Te (12 961 K at R = 100), Proxauf 2014 [S II]
111
+ density (449 cm⁻³ at R = 1), Kauffmann 2003 and Kewley 2001 BPT
112
+ demarcations at x = −0.5, Kasten & Young 1989 airmass (1.994293 at
113
+ 60°), the KPNO extinction node at 5504 Å (0.150), CCM89 dereddening
114
+ factor (1.330), Vollmann & Eversberg 2006 EW error, the first Fourier
115
+ zero q₁(0.6) = 0.660, Greisen 2006 WAVE-LOG axis.
116
+ - 7 real-data cases on the CALSPEC fixtures (Git LFS, fetched by the docs
117
+ workflow) : the four Balmer line centres of Vega recovered within 0.04 to
118
+ 0.26 Å of the NIST rest wavelengths (RV −13.9 km/s ; tolerance set by
119
+ the STIS FWHM column, 2.75 to 4.9 Å) and the Hα−Hβ separation on the
120
+ solar reference (1701.44 Å, Δ +1.7 Å at 10–20 Å sampling) pass ; the
121
+ Vega A0V and Sun G2V classifications run when a Pickles atlas directory
122
+ is given (`SPECTRO_PICKLES_ATLAS_DIR`) and are shown as "not run"
123
+ otherwise. Recorded as not measurable at these resolutions : the Na I D
124
+ separation, Hγ/Hδ on the Sun, the solar Ca II H&K S-index.
125
+ - Reference corrected while writing the cases : the q₁(ε) zeros of the
126
+ rotational profile's transform are from Dravins, Lindegren & Torkelsson
127
+ 1990, A&A 237, 137 (the 0.7.0 notes and the `vsini_fourier` brick named
128
+ the wrong authors and volume).
129
+
130
+ ### Documentation for agents and developers (spec phases 5–7)
131
+
132
+ - **Generated references.** `docs/reference/cli.md` is rendered from the
133
+ argparse tree (every command, its usage line, an options table with kinds,
134
+ defaults and requirements, the exit codes) and `docs/reference/mcp.md`
135
+ from the running server (the 13 session and discovery tools with their
136
+ JSON input schemas, the 128 algorithm tools by category linked to their
137
+ pages, the response shape, the resources) ; `docs/ai/mcp-tools.json` is
138
+ the machine twin. The Python API reference moves to
139
+ `docs/reference/python.md`. Neither page can drift from the code.
140
+ - **One implementation of the catalogue export.** The JSON builders move
141
+ from the docs tooling into the package, `spectro_kernel/registry_export.py`
142
+ (`algorithm_catalogue`, `algorithm_export`, `recipe_catalogue`,
143
+ `recipe_export`, the JSON Schema) ; the docs generators and the MCP
144
+ resources consume the same functions.
145
+ - **MCP resources** (`spectro_mcp/resources.py`) : `spectro://algorithms`,
146
+ `spectro://algorithms/{name}`, `spectro://recipes`,
147
+ `spectro://recipes/{name}` (the same JSON as `docs/ai/*.json`, never a
148
+ file path, source labels reduced in shared mode) and `spectro://llms.txt`
149
+ (a text map of the server and the documentation). An unknown name
150
+ answers with a clear `ResourceError` that survives error masking.
151
+ - **`AGENTS.md`** at the repository root now holds the standing brief for
152
+ AI contributors (architecture, non-negotiables, recipes, commands,
153
+ tests, versioning, release flow, escalation) ; `CLAUDE.md` imports it.
154
+ - **Executable examples** in `examples/` (`quickstart.py`,
155
+ `recipe_with_profile.py`, `wavelength_calibration.py`, `mcp_client.py`,
156
+ each deterministic, offline and under 60 lines), run by the test suite
157
+ and by CI, referenced from the getting-started page.
158
+
159
+ ### Recipes — collection 0.2.0
160
+
161
+ Six recipes added to
162
+ [spectro-kernel-recipes](https://github.com/matthieulel/spectro-kernel-recipes)
163
+ (all `draft`, validated against this kernel, three of them exercised end to
164
+ end on synthetic data) : `nova_balmer_decrement` (Osterbrock & Ferland 2006
165
+ Case B, CCM89), `be_line_profile_variability` (temporal variance spectrum,
166
+ Fullerton, Gies & Bolton 1996, on the same continuum band as
167
+ `be_halpha_ew`), `star_activity_caii_hk` (Mount Wilson S, `b_v` and the
168
+ instrumental calibration as variables), `star_activity_halpha` (Gomes da
169
+ Silva 2011), `star_vsini_fourier` (Carroll 1933 / Gray 2005, line, window
170
+ and limb darkening as variables), `lamp_resolving_power` (quality control).
171
+ `be_halpha_ew` now starts with the barycentric correction, the observer's
172
+ site being its profile.
173
+
9
174
  ## [0.7.0] — 2026-09-26
10
175
 
11
176
  The release that turns presets into **recipes** : a measurement written once,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: spectro-kernel
3
- Version: 0.7.0
3
+ Version: 0.8.0
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
6
  Project-URL: Documentation, https://docs.spectrokernel.io/
@@ -237,7 +237,9 @@ spectro_mcp/ MCP server wrapping the same catalogue
237
237
  ```
238
238
 
239
239
  Add an algorithm = one Python file + one test. No change to the core. See
240
- [CONTRIBUTING.md](CONTRIBUTING.md).
240
+ [CONTRIBUTING.md](CONTRIBUTING.md) (humans) and [AGENTS.md](AGENTS.md) (coding
241
+ agents). Runnable, deterministic examples of the three doors live in
242
+ [examples/](examples/) and are executed by CI.
241
243
 
242
244
  ## Documentation
243
245
 
@@ -148,7 +148,9 @@ spectro_mcp/ MCP server wrapping the same catalogue
148
148
  ```
149
149
 
150
150
  Add an algorithm = one Python file + one test. No change to the core. See
151
- [CONTRIBUTING.md](CONTRIBUTING.md).
151
+ [CONTRIBUTING.md](CONTRIBUTING.md) (humans) and [AGENTS.md](AGENTS.md) (coding
152
+ agents). Runnable, deterministic examples of the three doors live in
153
+ [examples/](examples/) and are executed by CI.
152
154
 
153
155
  ## Documentation
154
156
 
@@ -112,5 +112,5 @@ Together with per-algorithm versions, this is what makes a spectro-kernel result
112
112
  **traceable and replayable** - see [Pipelines → Reproducibility](pipelines.md#reproducibility).
113
113
 
114
114
  ??? note "Full field-by-field API"
115
- See the [API reference](../reference.md) for the complete, generated documentation
115
+ See the [Python API reference](../reference/python.md) for the complete, generated documentation
116
116
  of every type.
@@ -106,6 +106,17 @@ Point Claude Desktop at it (`~/Library/Application Support/Claude/claude_desktop
106
106
 
107
107
  The agent now sees every algorithm as a tool. See [As an MCP server](usage/mcp.md).
108
108
 
109
+ ## Executable examples
110
+
111
+ The `examples/` directory of the repository holds four self-contained, deterministic
112
+ scripts that CI runs on every change, so they can never go stale:
113
+ `quickstart.py` (the analysis above, on a synthetic spectrum), `recipe_with_profile.py`
114
+ (a recipe with variables bound by a profile, and the `pipeline:` provenance record it
115
+ leaves), `wavelength_calibration.py` (arc-lamp identification against the bundled NIST
116
+ atlas, then a polynomial dispersion solution) and `mcp_client.py` (an in-process MCP
117
+ client calling the server's tools and reading its resources, no network). Run one with
118
+ `python examples/quickstart.py`.
119
+
109
120
  ## Where next
110
121
 
111
122
  - Just captured a spectrum of a star? → [Tutorial: your first