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.
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/.gitignore +3 -0
- spectro_kernel-0.8.0/AGENTS.md +419 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/CHANGELOG.md +165 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/PKG-INFO +4 -2
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/README.md +3 -1
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/concepts/data-types.md +1 -1
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/getting-started.md +11 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/index.md +4 -0
- spectro_kernel-0.7.0/docs/reference.md → spectro_kernel-0.8.0/docs/reference/python.md +3 -2
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/usage/cli.md +4 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/usage/mcp.md +7 -0
- spectro_kernel-0.8.0/docs/validation/.nav.md +2 -0
- spectro_kernel-0.8.0/docs/validation/index.md +96 -0
- spectro_kernel-0.8.0/examples/mcp_client.py +57 -0
- spectro_kernel-0.8.0/examples/quickstart.py +37 -0
- spectro_kernel-0.8.0/examples/recipe_with_profile.py +58 -0
- spectro_kernel-0.8.0/examples/wavelength_calibration.py +53 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/pyproject.toml +1 -1
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/activity/activity_index_caii_hk.py +10 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/activity/activity_index_halpha.py +10 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/activity/ccf_bisector.py +10 -4
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/compare_normalisations.py +8 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/normalize_edges.py +9 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/normalize_max.py +13 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/normalize_percentile.py +12 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/normalize_polynomial.py +13 -5
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/normalize_region.py +16 -6
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/normalize_spline.py +14 -6
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/subtract_continuum.py +15 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_band_power.py +5 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_continuum_subtracted.py +10 -7
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_lick_indices.py +5 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_log_lambda.py +6 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_pretrained.py +12 -7
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_remote.py +17 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_spectrum.py +10 -6
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/embed_wavelets.py +10 -5
- spectro_kernel-0.8.0/src/spectro_kernel/algorithms/extraction/_aperture.py +58 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/extraction/boxcar.py +71 -51
- spectro_kernel-0.8.0/src/spectro_kernel/algorithms/extraction/optimal.py +861 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/kinematics/vsini_fourier.py +13 -8
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/_line_flux.py +6 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/_profiles.py +10 -4
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/detect.py +19 -4
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/equivalent_width.py +22 -10
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/fit_gaussian.py +10 -2
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/fit_lorentzian.py +8 -2
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/fit_voigt.py +10 -2
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/vr_ratio.py +6 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/nebular/balmer_decrement_extinction.py +5 -2
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/nebular/bpt_line_ratios.py +6 -2
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/nebular/oiii_electron_temperature.py +6 -2
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/nebular/sii_electron_density.py +5 -2
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/quality/measure_resolving_power.py +6 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/quality/snr_der.py +22 -8
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/quality/snr_edge.py +5 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/quality/snr_linear_fit.py +5 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/rv/cross_correlate.py +50 -28
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/rv/measure.py +6 -2
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/rv/precision_bouchy.py +11 -5
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/rv/redshift_lines.py +5 -3
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/timeseries/temporal_variance_spectrum.py +61 -15
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/embeddings.py +10 -7
- spectro_kernel-0.8.0/src/spectro_kernel/registry_export.py +334 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/spectrum.py +36 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/version.py +1 -1
- spectro_kernel-0.8.0/src/spectro_mcp/resources.py +184 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/server.py +11 -7
- spectro_kernel-0.8.0/tests/reference/cases.py +832 -0
- spectro_kernel-0.8.0/tests/reference/test_validation_cases.py +65 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_cross_correlate_extension.py +22 -8
- spectro_kernel-0.8.0/tests/unit/test_docs_generators.py +184 -0
- spectro_kernel-0.8.0/tests/unit/test_examples_run.py +63 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_extract_boxcar.py +99 -29
- spectro_kernel-0.8.0/tests/unit/test_extract_optimal.py +437 -0
- spectro_kernel-0.8.0/tests/unit/test_extraction_centring.py +108 -0
- spectro_kernel-0.8.0/tests/unit/test_mask_honoured.py +393 -0
- spectro_kernel-0.8.0/tests/unit/test_mcp_resources.py +159 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tools/docs/generate.py +7 -1
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tools/docs/generate_catalogue.py +18 -199
- spectro_kernel-0.8.0/tools/docs/generate_cli.py +275 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tools/docs/generate_llms.py +4 -0
- spectro_kernel-0.8.0/tools/docs/generate_mcp.py +362 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tools/docs/generate_recipes.py +4 -28
- spectro_kernel-0.8.0/tools/docs/generate_validation.py +225 -0
- spectro_kernel-0.7.0/src/spectro_kernel/algorithms/extraction/optimal.py +0 -403
- spectro_kernel-0.7.0/tests/unit/test_extract_optimal.py +0 -178
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/LICENSE +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/.drafts/README.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/.drafts/notebooks/aurora-line-monitor.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/concepts/algorithms.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/concepts/architecture.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/concepts/pipelines.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/contributing.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/cookbook/multi-star-viewer.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/cookbook/web-playground.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_ew_timeseries.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_halpha_phase_stack.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_halpha_timeseries.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_overlay.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_pc1_vs_phase.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_pca_2d.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_pca_3d.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_periodogram.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_phase_folded.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_rv_timeseries.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_similarity.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_cyg_similarity_date.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_halpha_phase_stack.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_pc1_vs_phase.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_pca_2d.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_pca_3d.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_periodogram.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_phase_folded.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_rv_timeseries.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_similarity_date.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/images/notebooks/alpha_dra_similarity_phase.png +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/alpha-cyg-time-series.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/alpha-dra-binary-period.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/be-star-variability.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/claude-mcp-end-to-end.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/exoplanet-transit-rv.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/full-reduction-walkthrough.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/native-vs-easyspec-showdown.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/notebooks/your-first-sb2.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/recipes/format.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/recipes/index.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/recipes/share.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/tutorials/add-an-algorithm.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/tutorials/analyse-a-spectrum.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/tutorials/discover-the-catalogue.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/tutorials/first-spectrum.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/tutorials/long-slit-reduction.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/usage/library.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/docs/why.md +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/adapters/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/adapters/easyspec.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/_common.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/activity/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/activity/_bands.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/advanced/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/advanced/aperture_photometry.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/advanced/disentangle_sb2.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/advanced/doppler_tomogram.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/catalogs/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/catalogs/gaia.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/catalogs/simbad.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/catalogs/standard_star.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/catalogs/vizier.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/classification/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/classification/classify_template_chi2.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/classification/pickles_atlas.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/continuum/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/air_vacuum.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/atmospheric_extinction.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/barycentric.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/deredden_interstellar.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/doppler_shift.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/extinction_correct_easyspec.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/fit_telluric_scaling.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/flux_calibrate_easyspec.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/remove_telluric.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/response_from_standard.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/corrections/synth_telluric.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/embedding/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/exports/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/exports/_bess_contract.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/exports/export_csv.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/exports/export_fits.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/exports/export_fits_bess.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/exports/export_hdf5.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/exports/export_votable.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/extraction/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/extraction/detect_trace.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/extraction/easyspec_extract.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/extraction/sky_lateral_bands.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/io/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/io/read_ascii.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/io/read_echelle.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/io/read_fits.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/io/read_sdss.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/io/read_votable.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/kinematics/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/kinematics/rotation_curve.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/catalogs.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/lines/compare_line_fits.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/nebular/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/quality/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/quality/compare_snr_methods.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/quality/validate_bess_header.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/_easyspec_apply.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/_easyspec_helpers.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/bias_combine.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/clip_cosmic_rays.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/dark_combine.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/dark_subtract.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/denoise_2d.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/easyspec_bias.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/easyspec_cosmic_ray.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/easyspec_dark.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/easyspec_flat.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/easyspec_flat_normalize.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_bias.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_dark.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/extract_spectrum_sum.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/flat_combine.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/flat_normalize.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/geometry.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/subtract_sky_2d.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/reduction/wavelength_calibrate.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/rv/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/rv/fit_keplerian_orbit.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/smoothing/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/smoothing/_kernel_uncertainty.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/smoothing/_nan_bridge.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/smoothing/compare_smoothings.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/smoothing/smooth_gaussian.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/smoothing/smooth_savgol.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/stacking/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/stacking/merge_echelle_orders.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/stacking/stack.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/timeseries/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/timeseries/_light_curve.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/timeseries/box_least_squares.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/timeseries/lomb_scargle.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/timeseries/phase_dispersion_minimization.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/timeseries/phase_fold.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/transforms/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/transforms/clip_sigma.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/transforms/combine_arithmetic.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/transforms/extract_region.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/transforms/mask_range.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/transforms/resample.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/transforms/resample_flux_conserving.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/viz/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/viz/plot_3d_surface.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/viz/plot_animation.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/viz/plot_dynamic_spectrum.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/viz/plot_plotly.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/arc_geometry.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/easyspec_wavelength.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/in_situ.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/lamp_atlas.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/match_lamp.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/reidentify.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/algorithms/wavelength_calibration/solar.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/base.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/cli.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/errors.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/io/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/io/ascii.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/io/fits.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/io/votable.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/pipeline.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/catalog/analysis/balmer_quick.yaml +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/catalog/analysis/embed_quick.yaml +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/catalog/analysis/quality_report.yaml +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/catalog/analysis/rv_quick.yaml +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/catalog/analysis/snr_check.yaml +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/catalog/analysis/time_series_overview.yaml +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/catalog/reduction/full_reduction_easyspec.yaml +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/loader.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/preset.schema.json +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/presets/variables.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/py.typed +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/registry.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/catalog.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/context.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/enums.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/history.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/image.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/line.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/types/timeseries.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_kernel/url_safety.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/__main__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/auth.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/auto_tools.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/observability.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/py.typed +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/session.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/src/spectro_mcp/url_safety.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/conftest.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/reference/conftest.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/reference/test_known_answers.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/reference/test_sun.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/reference/test_vega.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_activity_bisector.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_activity_caii_hk.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_activity_halpha.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_aperture_photometry.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_barycentric_bjd.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_base.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_catalog_queries_mocked.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_classify_template_chi2.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_cli.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_combine.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_combine_arithmetic.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_continuum.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_corrections.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_dark_flat_combine.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_denoise_2d.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_detect_lines_blind.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_detect_trace.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_disentangle_sb2.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_doppler_tomogram.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_easyspec_apply_staging.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_easyspec_sessions.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_easyspec_wrappers.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_embedding.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_embedding_pretrained.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_embedding_remote_lick.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_embedding_tier1.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_export_fits_bess.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_export_hdf5.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_extract_spectrum_sum.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_flux_atmospheric_extinction.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_flux_balmer_decrement.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_flux_calibrate_easyspec.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_flux_deredden_interstellar.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_flux_standard_star_reference.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_geometry_corrections.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_io.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_io_fits_zoo.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_lamp_atlas_match.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_line_profiles.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_lines.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_mcp.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_mcp_http_entry.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_mcp_param_guard.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_mcp_production.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_mcp_security.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_mcp_tools_extra.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_measure_arc_geometry.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_misc_algorithms.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_nebular_diagnostics.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_no_circular_imports.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_normalize_spline.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_normalize_to_region.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_output_produces_contract.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_pipeline.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_presets.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_quality.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_read_sdss.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_redshift_lines.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_registry.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_reidentify.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_resample_flux_conserving.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_resolving_power.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_response_from_standard.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_rotation_curve.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_rv_precision_bouchy.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_sky_lateral_bands.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_smoothing.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_timeseries.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_timeseries_bls.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_timeseries_pdm.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_timeseries_tvs.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_transforms.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_types.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_uncertainty_propagation.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_url_safety_kernel.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_validate_bess_header.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_viz.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_vr_ratio.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_vsini_fourier.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_wavelength_calibration_in_situ.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tests/unit/test_wavelength_calibration_solar.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tools/docs/__init__.py +0 -0
- {spectro_kernel-0.7.0 → spectro_kernel-0.8.0}/tools/docs/build.py +0 -0
- {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.
|
|
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
|