dkist-processing-visp 5.7.11__tar.gz → 5.7.13__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 (119) hide show
  1. dkist_processing_visp-5.7.13/.rovodev/.review-agent.md +537 -0
  2. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/CHANGELOG.rst +18 -0
  3. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/PKG-INFO +53 -43
  4. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/README.rst +12 -2
  5. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/workflows/l0_processing.py +6 -1
  6. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/workflows/trial_workflows.py +1 -1
  7. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp.egg-info/PKG-INFO +53 -43
  8. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp.egg-info/SOURCES.txt +1 -0
  9. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp.egg-info/requires.txt +40 -40
  10. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/pyproject.toml +42 -42
  11. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/.gitignore +0 -0
  12. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/.pre-commit-config.yaml +0 -0
  13. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/.readthedocs.yml +0 -0
  14. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/.snyk +0 -0
  15. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/SCIENCE_CHANGELOG.rst +0 -0
  16. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/bitbucket-pipelines.yml +0 -0
  17. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/changelog/.gitempty +0 -0
  18. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/__init__.py +0 -0
  19. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/config.py +0 -0
  20. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/fonts/Lato-Regular.ttf +0 -0
  21. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/models/__init__.py +0 -0
  22. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/models/constants.py +0 -0
  23. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/models/dataset_extras.py +0 -0
  24. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/models/fits_access.py +0 -0
  25. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/models/metric_code.py +0 -0
  26. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/models/parameters.py +0 -0
  27. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/models/tags.py +0 -0
  28. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/models/task_name.py +0 -0
  29. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/parsers/__init__.py +0 -0
  30. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/parsers/map_repeats.py +0 -0
  31. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/parsers/modulator_states.py +0 -0
  32. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/parsers/polarimeter_mode.py +0 -0
  33. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/parsers/raster_step.py +0 -0
  34. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/parsers/spectrograph_configuration.py +0 -0
  35. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/parsers/time.py +0 -0
  36. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/parsers/visp_l0_fits_access.py +0 -0
  37. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/parsers/visp_l1_fits_access.py +0 -0
  38. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/__init__.py +0 -0
  39. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/assemble_movie.py +0 -0
  40. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/background_light.py +0 -0
  41. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/dark.py +0 -0
  42. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/geometric.py +0 -0
  43. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/instrument_polarization.py +0 -0
  44. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/l1_output_data.py +0 -0
  45. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/lamp.py +0 -0
  46. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/make_movie_frames.py +0 -0
  47. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/mixin/__init__.py +0 -0
  48. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/mixin/beam_access.py +0 -0
  49. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/mixin/corrections.py +0 -0
  50. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/mixin/downsample.py +0 -0
  51. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/parse.py +0 -0
  52. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/polcal_as_science.py +0 -0
  53. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/quality_metrics.py +0 -0
  54. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/science.py +0 -0
  55. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/solar.py +0 -0
  56. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/visp_base.py +0 -0
  57. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/wavelength_calibration.py +0 -0
  58. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/write_dataset_extras.py +0 -0
  59. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tasks/write_l1.py +0 -0
  60. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/README.rst +0 -0
  61. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/__init__.py +0 -0
  62. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/conftest.py +0 -0
  63. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/header_models.py +0 -0
  64. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/local_trial_workflows/__init__.py +0 -0
  65. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/local_trial_workflows/l0_cals_only.py +0 -0
  66. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/local_trial_workflows/l0_polcals_as_science.py +0 -0
  67. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/local_trial_workflows/l0_solar_gain_as_science.py +0 -0
  68. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/local_trial_workflows/l0_to_l1.py +0 -0
  69. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/local_trial_workflows/local_trial_helpers.py +0 -0
  70. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_assemble_movie.py +0 -0
  71. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_assemble_quality.py +0 -0
  72. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_background_light.py +0 -0
  73. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_dark.py +0 -0
  74. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_downsample.py +0 -0
  75. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_fits_access.py +0 -0
  76. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_geometric.py +0 -0
  77. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_instrument_polarization.py +0 -0
  78. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_lamp.py +0 -0
  79. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_make_movie_frames.py +0 -0
  80. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_map_repeats.py +0 -0
  81. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_parameters.py +0 -0
  82. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_parse.py +0 -0
  83. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_polcal_as_science.py +0 -0
  84. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_quality.py +0 -0
  85. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_science.py +0 -0
  86. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_solar.py +0 -0
  87. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_trial_create_quality_report.py +0 -0
  88. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_visp_base.py +0 -0
  89. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_visp_constants.py +0 -0
  90. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_wavelength_calibration.py +0 -0
  91. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_workflows.py +0 -0
  92. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_write_dataset_extras.py +0 -0
  93. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/tests/test_write_l1.py +0 -0
  94. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/workflows/__init__.py +0 -0
  95. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp/workflows/single_task_workflows.py +0 -0
  96. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp.egg-info/dependency_links.txt +0 -0
  97. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/dkist_processing_visp.egg-info/top_level.txt +0 -0
  98. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/Makefile +0 -0
  99. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/background_light.rst +0 -0
  100. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/changelog.rst +0 -0
  101. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/conf.py +0 -0
  102. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/dataset_extras.rst +0 -0
  103. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/gain_correction.rst +0 -0
  104. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/geometric.rst +0 -0
  105. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/index.rst +0 -0
  106. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/l0_to_l1_visp.rst +0 -0
  107. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/l0_to_l1_visp_full-trial.rst +0 -0
  108. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/landing_page.rst +0 -0
  109. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/make.bat +0 -0
  110. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/polarization_calibration.rst +0 -0
  111. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/requirements.txt +0 -0
  112. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/requirements_table.rst +0 -0
  113. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/science_calibration.rst +0 -0
  114. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/scientific_changelog.rst +0 -0
  115. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/docs/wavelength_calibration.rst +0 -0
  116. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/licenses/LICENSE.rst +0 -0
  117. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/science_towncrier.sh +0 -0
  118. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/setup.cfg +0 -0
  119. {dkist_processing_visp-5.7.11 → dkist_processing_visp-5.7.13}/towncrier_science.toml +0 -0
@@ -0,0 +1,537 @@
1
+ # Custom Code Review Instructions for dkist-processing-* Repositories
2
+
3
+ This document configures automated PR review guidance for DKIST science data processing repositories.
4
+ It is intended for repos in the `dkist-processing-*` family, including:
5
+
6
+ - `dkist-processing-core`: shared workflow/task abstractions used by other processing packages.
7
+ - `dkist-processing-common`: common processing utilities and task bases used by instrument packages.
8
+ - Instrument processing packages such as `dkist-processing-visp`, where most science pipeline changes occur.
9
+
10
+ Focus comments on objective, code-reviewable findings. Avoid blocking a PR on subjective preferences unless the code clearly violates one of the conventions below or introduces a maintainability/bug risk.
11
+
12
+ ---
13
+
14
+ ## Review Priorities
15
+
16
+ When reviewing a PR, prioritize:
17
+
18
+ 1. Correctness and data integrity bugs.
19
+ 2. Violations of repository architecture or dependency direction.
20
+ 3. Patterns that make future instrument-specific changes harder.
21
+ 4. Missing tests for changed behavior.
22
+ 5. Maintainability issues that are concrete and actionable.
23
+
24
+ Do not repeat findings that are already clearly handled by linters, formatters, or type checkers unless the issue has architectural or runtime significance.
25
+
26
+ ---
27
+
28
+ ## Repository Layering and Dependency Direction
29
+
30
+ **Description**: Preserve the dependency direction across processing repositories.
31
+
32
+ - `dkist-processing-core` must remain generic and must not import from `dkist-processing-common` or any instrument package.
33
+ - `dkist-processing-common` may depend on core abstractions, but must not import instrument-specific packages such as `dkist-processing-visp`.
34
+ - Instrument packages may depend on common and core, but should not create cross-instrument dependencies.
35
+
36
+ **Flag when**:
37
+
38
+ - A lower-level package imports a higher-level package.
39
+ - Shared code gains instrument-specific logic that belongs in an instrument package.
40
+ - Instrument-specific constants, FITS keys, task names, or parser behavior are added to common/core without a clear abstraction.
41
+
42
+ **Good pattern**:
43
+
44
+ ```python
45
+ # instrument package code may use common abstractions
46
+ from dkist_processing_common.tasks import WorkflowTaskBase
47
+ ```
48
+
49
+ **Bad pattern**:
50
+
51
+ ```python
52
+ # common/core code should not depend on an instrument package
53
+ from dkist_processing_visp.models.tags import VispTag
54
+ ```
55
+
56
+ ---
57
+
58
+ ## Task Mixins
59
+
60
+ **Description**: Task mixins encapsulate reusable task functionality in small modules and are composed into concrete task classes only where needed. Prefer minimizing new mixins and concentrating them around dependencies on external/shared resources rather than using mixins as general helper-method buckets.
61
+
62
+ **Flag when**:
63
+
64
+ - A mixin class does not end with `Mixin`.
65
+ - A public mixin method is not prefixed with the mixin-specific namespace.
66
+ - Example: methods on `MetadataStoreMixin` should begin with `metadata_store_`.
67
+ - Example: methods on `ObjectStoreMixin` should begin with `object_store_`.
68
+ - A mixin imports another mixin, creating hidden coupling between reusable behaviors.
69
+ - A mixin contains behavior that is only meaningful for a single concrete task or instrument and would be clearer on that task/base class.
70
+
71
+ **Good example**:
72
+
73
+ ```python
74
+ class MetadataStoreMixin:
75
+ def metadata_store_get_parameter(self, key: str) -> object:
76
+ ...
77
+ ```
78
+
79
+ **Bad example**:
80
+
81
+ ```python
82
+ class MetadataStoreMixin:
83
+ def get_parameter(self, key: str) -> object:
84
+ ...
85
+ ```
86
+
87
+ **Bad example**:
88
+
89
+ ```python
90
+ # In a mixin module
91
+ from dkist_processing_common.tasks.mixin.object_store import ObjectStoreMixin
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Task Classes and Task Imports
97
+
98
+ **Description**: Concrete task classes represent workflow/DAG nodes. Keep task dependencies explicit and avoid coupling concrete tasks to each other.
99
+
100
+ **Flag when**:
101
+
102
+ - A concrete task imports another concrete task from a `tasks` package module.
103
+ - Imports from `tasks` package modules are not from `*_base` modules or the `tasks.mixin` package.
104
+ - A class that is intended as a base class does not end with `Base`.
105
+ - A concrete task contains branching logic that appears to select instrument-specific behavior that should instead be represented by separate task classes or instrument-specific subclasses.
106
+
107
+ **Good example**:
108
+
109
+ ```python
110
+ from dkist_processing_common.tasks.write_l1_base import WriteL1Base
111
+ from dkist_processing_visp.tasks.mixin.beam_access import BeamAccessMixin
112
+ ```
113
+
114
+ **Bad example**:
115
+
116
+ ```python
117
+ from dkist_processing_visp.tasks.dark import DarkCalibration
118
+ ```
119
+
120
+ ---
121
+
122
+ ## Overriding Base-Class Methods
123
+
124
+ **Description**: When overriding methods from task or framework base classes, preserve base-class behavior unless there is an explicit reason not to.
125
+
126
+ **Flag when**:
127
+
128
+ - A subclass overrides a method that exists on a base class and does not call `super().method_name(...)`.
129
+ - The PR does not explain why the base implementation should be skipped.
130
+
131
+ **Good example**:
132
+
133
+ ```python
134
+ class ScienceCalibration(VispTaskBase):
135
+ def run(self) -> None:
136
+ super().run()
137
+ self.science_calibrate_frames()
138
+ ```
139
+
140
+ **Bad example**:
141
+
142
+ ```python
143
+ class ScienceCalibration(VispTaskBase):
144
+ def run(self) -> None:
145
+ self.science_calibrate_frames()
146
+ ```
147
+
148
+ ---
149
+
150
+ ## Task-Class Docstrings
151
+
152
+ **Description**: Concrete task class docstrings are user-facing narrative documentation.
153
+
154
+ **Flag when**:
155
+
156
+ - A new or changed concrete task class lacks a docstring.
157
+ - The docstring is only a developer note and does not explain what the task does for pipeline users/operators.
158
+ - The docstring repeats the class name without adding useful context.
159
+
160
+ **Good example**:
161
+
162
+ ```python
163
+ class DarkCalibration(VispTaskBase):
164
+ """Create dark calibration arrays for downstream ViSP calibration tasks."""
165
+ ```
166
+
167
+ **Bad example**:
168
+
169
+ ```python
170
+ class DarkCalibration(VispTaskBase):
171
+ """DarkCalibration class."""
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Tracing Span Usage
177
+
178
+ **Description**: Tracing spans using `telemetry_span...` should provide useful operational visibility without overwhelming telemetry.
179
+
180
+ **Flag when**:
181
+
182
+ - A span name is vague, e.g. `run`, `process`, `loop`, `do stuff`, or merely repeats the task name.
183
+ - A span is created inside an unbounded loop such as raster steps, DSPS repeats, frames of arbitrary count, or input records without a fixed upper bound.
184
+ - A single added span mirrors the always-on run/task span but provides no additional visibility.
185
+ - Load, process, and write phases are mixed together in one broad span when the code clearly separates those phases.
186
+ - Span placement is so low in the loop structure that it risks producing excessive telemetry volume.
187
+
188
+ **Acceptable pattern**:
189
+
190
+ - Individual spans for bounded, known-small loop dimensions such as ViSP beam or VBI camera position.
191
+ - Separate spans for load/process/write phases when they help diagnose runtime behavior.
192
+
193
+ **Bad example**:
194
+
195
+ ```python
196
+ for raster_step in raster_steps:
197
+ with telemetry_span(f"Process raster step {raster_step}"):
198
+ self.process_raster_step(raster_step)
199
+ ```
200
+
201
+ ---
202
+
203
+ ## FITS Header Access
204
+
205
+ **Description**: FITS header names and values should be abstracted through `FitsAccessBase` and instrument-specific subclasses.
206
+
207
+ **Flag when**:
208
+
209
+ - Code outside FITS access classes reads FITS header keys directly from headers or dictionaries.
210
+ - A raw Spec122 key is used where a translated `FitsAccessBase` attribute exists.
211
+ - Example: prefer `CAM_ID` over `CAM__001` when a translated attribute exists.
212
+ - Header-version-specific logic is added outside FITS access classes/parsers.
213
+ - Multiple modules duplicate the same raw header-key string.
214
+
215
+ **Good example**:
216
+
217
+ ```python
218
+ camera_id = fits_access.camera_id
219
+ ```
220
+
221
+ **Bad example**:
222
+
223
+ ```python
224
+ camera_id = header["CAM__001"]
225
+ ```
226
+
227
+ ---
228
+
229
+ ## Parameters and Parameter Storage
230
+
231
+ **Description**: Parameter storage format should be chosen deliberately based on size, serialization needs, and operator readability.
232
+
233
+ **Flag when**:
234
+
235
+ - A new parameter stores large array-like data as JSON instead of a file-backed format such as `.npy`.
236
+ - A small list or simple scalar is written as an opaque file when JSON would be clearer.
237
+ - A file parameter is not self-describing enough to inspect with ordinary tools where practical.
238
+ - The storage format appears to simply mirror the input format without considering downstream use.
239
+ - The same parameter key/string is duplicated across multiple locations instead of being defined once.
240
+
241
+ **Good pattern**:
242
+
243
+ - Use JSON-compatible values for small scalar/list parameters.
244
+ - Use `.npy` or another explicit file format for large array-like data.
245
+ - Prefer named constants, enums, or parameter model attributes over repeated string literals.
246
+
247
+ ---
248
+
249
+ ## Logging Standards
250
+
251
+ ### Instrument Pipeline Repositories (`dkist-processing-[visp|vbi|...]`)
252
+ Instrument-specific processing repositories should use the logging infrastructure provided by `dkist-service-configuration`.
253
+
254
+ **Expected pattern**:
255
+ ```python
256
+ from dkist_service_configuration.logging import logger
257
+ ```
258
+
259
+ or the repository-standard wrapper around that logger.
260
+
261
+ ### Core and Shared Libraries (`dkist-processing-core`, `dkist-processing-common`)
262
+ Core/shared libraries should use standard library logging with module-local loggers.
263
+
264
+ **Expected pattern**:
265
+ ```python
266
+ import logging
267
+
268
+ logger = logging.getLogger(__name__)
269
+ ```
270
+
271
+ ### General Logging Rules
272
+ - No `print()` statements in production code.
273
+ - Rare exceptions may exist for notebook-oriented execution/debug workflows.
274
+ - New modules should define a module-level logger rather than creating loggers inline repeatedly.
275
+ - Logging should provide actionable operational/debugging information and avoid noisy per-item logging inside large loops unless rate-limited or scoped by tracing spans.
276
+
277
+ ---
278
+
279
+ ## Exceptions
280
+
281
+ **Description**: Preserve exception context when converting or re-raising errors.
282
+
283
+ **Flag when**:
284
+
285
+ - Code catches an exception and raises a new exception without `raise ... from exc`.
286
+ - Code catches broad exceptions without a clear recovery path.
287
+ - Code logs an exception and continues in a way that may hide data loss or partial processing.
288
+
289
+ **Good example**:
290
+
291
+ ```python
292
+ try:
293
+ value = parse_value(raw_value)
294
+ except ValueError as exc:
295
+ raise ParameterError("Could not parse parameter value") from exc
296
+ ```
297
+
298
+ **Bad example**:
299
+
300
+ ```python
301
+ try:
302
+ value = parse_value(raw_value)
303
+ except ValueError:
304
+ raise ParameterError("Could not parse parameter value")
305
+ ```
306
+
307
+ ---
308
+
309
+ ## Type Hints
310
+
311
+ **Description**: All new or modified functions and methods should have complete type hints.
312
+
313
+ **Flag when**:
314
+
315
+ - A function parameter lacks a type annotation.
316
+ - A function lacks an explicit return type, including `-> None` for functions used only for side effects.
317
+ - New code uses `Any`, untyped containers, or `dict`/`list` without parameterization where a concrete type is practical.
318
+
319
+ **Good example**:
320
+
321
+ ```python
322
+ def write_frame(frame_path: Path, tags: list[str]) -> None:
323
+ ...
324
+ ```
325
+
326
+ **Bad example**:
327
+
328
+ ```python
329
+ def write_frame(frame_path, tags):
330
+ ...
331
+ ```
332
+
333
+ ---
334
+
335
+ ## Avoid `**kwargs` for Core Behavior
336
+
337
+ **Description**: Prefer explicit parameters over catch-all keyword arguments.
338
+
339
+ **Flag when**:
340
+
341
+ - New public functions, methods, or constructors use `**kwargs` without a strong reason.
342
+ - `**kwargs` is used to pass required domain values such as task names, tags, parameter keys, FITS keys, or calibration settings.
343
+ - `**kwargs` hides the contract between task classes and their collaborators.
344
+
345
+ **Acceptable uses**:
346
+
347
+ - Compatibility with third-party framework hooks that require `**kwargs`.
348
+ - Thin adapters where the accepted keys are documented and validated immediately.
349
+
350
+ ---
351
+
352
+ ## Avoid Python Magic That Breaks Navigation
353
+
354
+ **Description**: Avoid dynamic patterns that break IDE “Go To Definition” and “Where Used” for domain behavior.
355
+
356
+ **Flag when**:
357
+
358
+ - New code uses `getattr`, `setattr`, dynamic imports, monkeypatching, or computed method names for ordinary domain logic.
359
+ - String names are used to reference methods/classes that could be imported or called directly.
360
+ - Magic behavior makes task dependencies, parameter names, or FITS keys hard to trace.
361
+
362
+ **Acceptable uses**:
363
+
364
+ - Well-contained framework integration points.
365
+ - Tests that intentionally patch or inspect behavior.
366
+
367
+ ---
368
+
369
+ ## Function and Method Size / Complexity
370
+
371
+ **Description**: Large functions with multiple conceptual sections are hard to review, test, and maintain.
372
+
373
+ **Flag when**:
374
+
375
+ - A new or heavily modified function contains multiple comment-delimited sections such as “load”, “process”, and “write”.
376
+ - A function introduces deep nested loops/conditionals that could be split into named helpers.
377
+ - A long function mixes parsing, validation, processing, and output-writing logic.
378
+ - Complex branching is driven by instrument/task type and would be clearer in separate task classes or polymorphic methods.
379
+
380
+ **Good review suggestion**:
381
+
382
+ > Consider extracting the commented `load`, `calibrate`, and `write` sections into named helper methods. That would make each phase easier to test and would give each block a docstring/narrative name.
383
+
384
+ ---
385
+
386
+ ## Names and Common Strings
387
+
388
+ **Description**: Names should communicate domain meaning, and repeated strings should be centralized.
389
+
390
+ **Flag when**:
391
+
392
+ - Variables, methods, or functions have vague names such as `data`, `thing`, `stuff`, `foo`, `tmp`, or `val` in non-trivial code.
393
+ - A commonly used string appears multiple times and should be represented by an enum, model attribute, constant, or tag class.
394
+ - New task names, tags, metric codes, parameter names, or FITS-related strings are duplicated instead of added to the existing model/enum structure.
395
+
396
+ ---
397
+
398
+ ## Comments, TODOs, and Docstrings
399
+
400
+ **Description**: Comments and docstrings should improve maintainability and generated documentation.
401
+
402
+ **Flag when**:
403
+
404
+ - New code adds TODO/FIXME comments without an issue reference, owner, or clear follow-up path.
405
+ - A comment explains what a block of code does, but the block could be extracted into a well-named helper with a docstring.
406
+ - A public function/method docstring is missing or too vague to help generated documentation.
407
+ - Inline comments duplicate obvious code behavior instead of explaining intent or constraints.
408
+
409
+ **Good example**:
410
+
411
+ ```python
412
+ def calculate_beam_offsets(...) -> BeamOffsets:
413
+ """Calculate beam offsets used to align downstream ViSP calibration arrays."""
414
+ ```
415
+
416
+ **Bad example**:
417
+
418
+ ```python
419
+ # Calculate beam offsets
420
+ beam_offsets = calculate_offsets(...)
421
+ ```
422
+
423
+ ---
424
+
425
+ ## Protected Members and Object Representation
426
+
427
+ **Description**: Avoid reaching through object internals and ensure custom objects are debuggable.
428
+
429
+ **Flag when**:
430
+
431
+ - Production code accesses protected members such as `_value`, `_cache`, or `_internal_state` on objects it does not own.
432
+ - New domain/model classes lack a useful `repr`/Pydantic representation/dataclass representation when instances are likely to appear in logs, debugging output, or assertion failures.
433
+
434
+ ---
435
+
436
+ ## Tests and Coverage
437
+
438
+ **Description**: Changed behavior should be covered by targeted tests.
439
+
440
+ **Flag when**:
441
+
442
+ - New task behavior, parser behavior, FITS access behavior, parameter serialization, or workflow behavior is added without tests.
443
+ - A bug fix lacks a regression test.
444
+ - Tests assert only that code runs, without checking the meaningful output or side effect.
445
+ - Coverage decreases in important domain logic without explanation.
446
+ - Missing lines are justified only by “hard to test” when a smaller helper function would make the logic testable.
447
+
448
+ **Acceptable explanation**:
449
+
450
+ - Some branches require integration-test infrastructure or heavy mocking and are already covered by an integration workflow. In that case, ask for a PR note or test comment identifying the integration coverage.
451
+
452
+ ---
453
+
454
+ ## Notebook and Structured File Handling
455
+
456
+ **Description**: Use native APIs for structured formats when accuracy matters.
457
+
458
+ **Flag when**:
459
+
460
+ - Notebook code manually reads text and then calls `nbformat.reads()` where `nbformat.read()` with a `Path` or file handle would be clearer.
461
+ - Structured data such as JSON, notebooks, ASDF, or XML is searched as raw text when a native parser should be used.
462
+ - A PR changes polling/performance-sensitive code to parse full structured files repeatedly without acknowledging the performance tradeoff.
463
+
464
+ **Good example**:
465
+
466
+ ```python
467
+ notebook = nbformat.read(notebook_path, as_version=nbformat.NO_CONVERT)
468
+ ```
469
+
470
+ **Bad example**:
471
+
472
+ ```python
473
+ notebook = nbformat.reads(notebook_path.read_text(encoding="utf-8"), as_version=nbformat.NO_CONVERT)
474
+ ```
475
+
476
+ ---
477
+
478
+ ## File Creation and Partial State
479
+
480
+ **Description**: Validate inputs before creating output files to avoid partial state on errors.
481
+
482
+ **Flag when**:
483
+
484
+ - A function creates/touches output files before validating all inputs needed to populate those files.
485
+ - A failure path can leave orphaned or partially written files without cleanup.
486
+ - A write operation overwrites an existing output without an explicit overwrite policy.
487
+
488
+ **Good pattern**:
489
+
490
+ 1. Collect inputs.
491
+ 2. Validate all required values.
492
+ 3. Build complete output content in memory or a temporary location.
493
+ 4. Write/touch the final output.
494
+
495
+ ---
496
+
497
+ ## Path Handling
498
+
499
+ **Description**: Use `Path` operations consistently when working with paths.
500
+
501
+ **Flag when**:
502
+
503
+ - Code mixes string path manipulation with `pathlib.Path` objects.
504
+ - Code uses built-in `open(path)` where `path` is already a `Path` object and `path.open(...)` would be more consistent with nearby code.
505
+ - Code manually joins paths with string concatenation instead of `/` or `Path` methods.
506
+
507
+ **Good example**:
508
+
509
+ ```python
510
+ with output_path.open(mode="w", encoding="utf-8") as f:
511
+ f.write(contents)
512
+ ```
513
+
514
+ **Bad example**:
515
+
516
+ ```python
517
+ with open(output_path, "w") as f:
518
+ f.write(contents)
519
+ ```
520
+
521
+ ---
522
+
523
+ ## Review Comment Style
524
+
525
+ When reporting an issue:
526
+
527
+ - Be specific about the file/line and the convention being violated.
528
+ - Explain the practical risk: broken dependency direction, hidden task coupling, loss of FITS-key abstraction, harder telemetry, partial file state, missing regression coverage, etc.
529
+ - Suggest a concrete fix.
530
+ - Do not ask for broad rewrites unless the changed code creates a clear maintainability or correctness risk.
531
+ - Do not flag purely subjective naming/style issues unless the name obscures domain meaning or conflicts with existing conventions.
532
+
533
+ **Preferred comment format**:
534
+
535
+ ```text
536
+ This imports a concrete task from another task module. In dkist-processing-* task modules should import only from *_base modules or tasks.mixin so concrete workflow nodes do not become coupled to each other. Could this dependency move to a shared base/helper, or be expressed through the existing base class instead?
537
+ ```
@@ -1,3 +1,21 @@
1
+ v5.7.13 (2026-05-19)
2
+ ====================
3
+
4
+ Features
5
+ --------
6
+
7
+ - Update 'dkist-processing-common' to incorporate notebook export functionality for manually processed pipeline runs. (`#320 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/320>`__)
8
+
9
+
10
+ v5.7.12 (2026-05-18)
11
+ ====================
12
+
13
+ Misc
14
+ ----
15
+
16
+ - Make the `SubmitDatasetMetadata` task dependent on quality data assembly to prevent an issue with quality data causing orphaned dataset metadata. (`#313 <https://bitbucket.org/dkistdc/dkist-processing-visp/pull-requests/313>`__)
17
+
18
+
1
19
  v5.7.11 (2026-05-13)
2
20
  ====================
3
21