Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 75 additions & 2 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,47 @@ result = my_processing(src, param1=10, param2="value")
- Parameter class MUST be a `guidata.dataset.DataSet` subclass
- Always re-export parameter classes in `sigima.params` module
- Export computation functions in `__all__` of their module AND in `sigima/proc/{signal|image}/__init__.py`
- Every public computation function declares an operation contract (see below)

### 1b. Operation Contracts (Versioned, Replayable Identity)

Every public computation function declares its contract next to it:

```python
from sigima.proc.contracts import EXACT, ReplayQualification


@computation_function(
operation_id="sigima.signal.my_processing", # stable, written in clear
contract_version=1, # 1 at the first declaration
qualification=ReplayQualification(contract_version=1, comparison=EXACT),
)
def my_processing(src: SignalObj, p: MyParam) -> SignalObj: ...
```

Applications (DataLab, DataLab-Web, capsules) record `(operation_id, contract_version)` with each
computation and replay it later, possibly with another Sigima version: a replay requires the same
identifier and version, otherwise it is refused explicitly.

**Versioning rule (mandatory)**:
- **Increase `contract_version` (and the version of its `ReplayQualification`) in the same change
whenever a modification of a computation function changes the result it returns**: new, removed
or renamed parameters, different defaults, different outputs, a different result for the same
inputs (even for unusual inputs only, e.g. mixed real/complex or `float32`/`float64` data), or
inputs that were accepted and are now refused.
- This is **independent of the releases of DataLab or of any other application**: Sigima is a
reference of its own. Never postpone or skip an increase because an application is not released
yet.
- Do **not** increase it only when the result cannot change: inputs previously refused (error) are
now accepted; refactoring, performance or messages without effect on the result; or the change is
entirely covered by a versioned rule of the execution context (`IMAGE_ROI_CONVENTION`, X-alignment
policies), whose version is increased instead.
- Historical one-off exception: Sigima 1.4.0 kept version 1 although results changed since 1.3.0
(contracts just published, not used yet). It does **not** apply to later changes.
- Keep the identifier when renaming or moving a function (use `aliases` if it must change).

Full rules: `doc/contributing/guidelines.rst` (section "Operation contracts"); user documentation:
`doc/user_guide/contracts.rst`.

### 2. Object Model: `SignalObj` and `ImageObj`

Expand Down Expand Up @@ -303,11 +344,16 @@ result.roi = rois # Attach to result
1. **Implement in `sigima/proc/signal/processing.py` (or appropriate module)**:

```python
from sigima.proc.contracts import EXACT, ReplayQualification
from sigima.proc.decorator import computation_function
import sigima.params


@computation_function()
@computation_function(
operation_id="sigima.signal.my_feature",
contract_version=1,
qualification=ReplayQualification(contract_version=1, comparison=EXACT),
)
def my_feature(src: SignalObj, p: MyFeatureParam) -> SignalObj:
"""Apply my feature to signal.

Expand Down Expand Up @@ -391,6 +437,28 @@ def test_my_feature():
# Add usage example in doc/examples/ if complex
```

7. **Prove the replay qualification**: the `@pytest.mark.validation` test is required; run
`python scripts/run_with_env.py python -m pytest --qualification-report=report.json` (fails if a
declared qualification is not proven). Declare a tolerance only when justified (see guidelines).

### Modifying an Existing Computation Function

Checklist (see "Operation Contracts" above):

1. **Can the result change?** (values, outputs, defaults, parameters, accepted inputs) If yes:
2. **Increase `contract_version`** and the `contract_version` of its `ReplayQualification` in the
same change, whatever the releases of DataLab or other applications.
3. **Update reference cases deliberately** and explain the scientific reason (never to make a test
pass).
4. **Run the qualification report** (`--qualification-report=report.json`).
5. **Release notes**: describe the change and list the contracts whose version was increased.
6. **Downstream**: provenance tests of applications that check a contract version (e.g. DataLab
`datalab/tests/features/common/provenance_*_test.py`) must follow.

No increase when the result cannot change (previously refused inputs now accepted, refactoring
without effect) or when the change is entirely covered by a versioned rule of the execution context
(increase that rule's version instead).

### Adding Low-Level NumPy Functions to `tools`

**When to use `tools` vs `proc`**:
Expand Down Expand Up @@ -559,6 +627,8 @@ Sigima functions are **consumed by DataLab processors**:
| `sigima/objects/signal/object.py` | `SignalObj` implementation |
| `sigima/objects/image/object.py` | `ImageObj` implementation |
| `sigima/proc/decorator.py` | `@computation_function` decorator system |
| `sigima/proc/contracts/` | Operation contracts: registry, codec, execution, replay qualification |
| `doc/contributing/guidelines.rst` | Contract rules (identifiers, version increases, qualification) |
| `sigima/proc/signal/processing.py` | Signal processing functions (normalize, calibrate, etc.) |
| `sigima/proc/image/detection.py` | Image detection (blobs, peaks, contours) |
| `sigima/tools/checks.py` | Input validation (`check_1d_array`, `check_2d_array`) |
Expand All @@ -585,6 +655,9 @@ Sigima functions are **consumed by DataLab processors**:
- Entirely new computation type (e.g., new parametric images)
- New analysis methods

**Contract versions are independent of this classification**: a bug fix that changes the result
of a computation function increases its `contract_version` too.

## Getting Help

- **Documentation**: https://sigima.readthedocs.io/
Expand All @@ -593,4 +666,4 @@ Sigima functions are **consumed by DataLab processors**:

---

**Remember**: Always use `scripts/run_with_env.py`, test headlessly with pytest, and export all parameters through `sigima.params`.
**Remember**: Always use `scripts/run_with_env.py`, test headlessly with pytest, export all parameters through `sigima.params`, and increase `contract_version` whenever a change modifies the result of a computation function.
50 changes: 30 additions & 20 deletions doc/contributing/guidelines.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,30 +96,40 @@ Follow these rules when you add or change a computation function:

- If the identifier itself must change, keep the previous one in ``aliases``.

- Increase ``contract_version`` for any change of meaning: new, removed or
renamed parameters, a different default behavior, different outputs or a
different scientific result for the same inputs.

- In particular, increase it whenever a call that succeeded may now give
another result, even for unusual inputs only (e.g. mixed real and complex, or
``float32`` and ``float64``, data): a replay of an activity recorded with the
earlier version is then refused explicitly instead of being reported as
different. Do not increase it when only inputs that were refused are now
accepted, or when inputs that were accepted are now refused (the replay then
fails explicitly), nor when the change is entirely covered by a rule recorded
in the execution context (e.g. the X-alignment policy).

- Version 1 of the contracts is settled by the first stable release of an
application recording them (DataLab 1.4): until then, no recorded activity
has to be refused, and contract versions are not increased (the changes of
Sigima 1.4 are part of version 1).
- Increase ``contract_version`` whenever a change of the computation function
changes the result it returns: new, removed or renamed parameters, a
different default behavior, different outputs or a different scientific
result for the same inputs, even for unusual inputs only (e.g. mixed real and
complex, or ``float32`` and ``float64``, data), and inputs that were accepted
and are now refused. Do it in the change itself, whatever the releases of
DataLab or of any other application using Sigima: Sigima is a reference of
its own, and a replay of an activity recorded with the earlier version is
then refused explicitly instead of giving another result.

- Do not increase it when the result cannot change: only inputs that were
refused (with an error) are now accepted, the change has no effect on the
result (refactoring, performance, messages), or the change is entirely
covered by a versioned rule of the execution context (see below), whose
version is increased instead.

- Checklist for any change of an existing computation function: decide whether
the result can change; if so, increase ``contract_version`` and the version
of its ``ReplayQualification`` (after proving it, see below), update the
reference cases deliberately, run ``pytest --qualification-report``, list the
increased contracts in the release notes, and update the provenance tests of
the applications that check a contract version (e.g. DataLab).

- Historical exception: the contracts were published with Sigima 1.3.0 and
kept version 1 in Sigima 1.4.0 although several results changed, because they
had just been published and were not used yet. This one-off decision does not
apply to later changes.

- Rules that apply to every operation are recorded in the execution context
rather than in each contract: the selection of image pixels by regions of
interest (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, recorded when an
input image carries regions of interest). Increase the version of such a rule,
not the contract versions, when it changes: a replay refuses the calls
recorded with another version.
input image carries regions of interest) and the X-alignment policies (see
below). Increase the version of such a rule, not the contract versions, when
it changes: a replay refuses the calls recorded with another version.

- The structure of the contract (input roles, parameter arguments, outputs) is
inferred from the annotations. Annotate every argument and the return value
Expand Down
19 changes: 11 additions & 8 deletions doc/locale/fr/LC_MESSAGES/contributing/guidelines.po
Original file line number Diff line number Diff line change
Expand Up @@ -100,17 +100,20 @@ msgstr "Écrivez l'identifiant en clair auprès de la fonction : ``sigima.signal
msgid "If the identifier itself must change, keep the previous one in ``aliases``."
msgstr "Si l'identifiant lui-même doit changer, conservez le précédent dans ``aliases``."

msgid "Increase ``contract_version`` for any change of meaning: new, removed or renamed parameters, a different default behavior, different outputs or a different scientific result for the same inputs."
msgstr "Augmentez ``contract_version`` pour tout changement de sens : paramètres ajoutés, supprimés ou renommés, comportement par défaut différent, sorties différentes ou résultat scientifique différent pour les mêmes entrées."
msgid "Increase ``contract_version`` whenever a change of the computation function changes the result it returns: new, removed or renamed parameters, a different default behavior, different outputs or a different scientific result for the same inputs, even for unusual inputs only (e.g. mixed real and complex, or ``float32`` and ``float64``, data), and inputs that were accepted and are now refused. Do it in the change itself, whatever the releases of DataLab or of any other application using Sigima: Sigima is a reference of its own, and a replay of an activity recorded with the earlier version is then refused explicitly instead of giving another result."
msgstr "Augmentez ``contract_version`` dès qu'une modification de la fonction de calcul change le résultat qu'elle renvoie : paramètres ajoutés, supprimés ou renommés, comportement par défaut différent, sorties différentes ou résultat scientifique différent pour les mêmes entrées, même pour des entrées inhabituelles seulement (par exemple des données mêlant réels et complexes, ou ``float32`` et ``float64``), ainsi que des entrées acceptées qui sont désormais refusées. Faites-le dans la modification elle-même, indépendamment des publications de DataLab ou de toute autre application utilisant Sigima : Sigima est une référence en soi, et le rejeu d'une activité enregistrée avec la version précédente est alors refusé explicitement au lieu de donner un autre résultat."

msgid "In particular, increase it whenever a call that succeeded may now give another result, even for unusual inputs only (e.g. mixed real and complex, or ``float32`` and ``float64``, data): a replay of an activity recorded with the earlier version is then refused explicitly instead of being reported as different. Do not increase it when only inputs that were refused are now accepted, or when inputs that were accepted are now refused (the replay then fails explicitly), nor when the change is entirely covered by a rule recorded in the execution context (e.g. the X-alignment policy)."
msgstr "En particulier, augmentez-la dès qu'un appel qui réussissait peut désormais donner un autre résultat, même pour des entrées inhabituelles seulement (par exemple des données mêlant réels et complexes, ou ``float32`` et ``float64``) : le rejeu d'une activité enregistrée avec la version précédente est alors refusé explicitement au lieu d'être signalé comme différent. Ne l'augmentez pas lorsque seules des entrées auparavant refusées sont désormais acceptées, ou lorsque des entrées auparavant acceptées sont désormais refusées (le rejeu échoue alors explicitement), ni lorsque le changement est entièrement couvert par une règle enregistrée dans le contexte d'exécution (par exemple la politique d'alignement en X)."
msgid "Do not increase it when the result cannot change: only inputs that were refused (with an error) are now accepted, the change has no effect on the result (refactoring, performance, messages), or the change is entirely covered by a versioned rule of the execution context (see below), whose version is increased instead."
msgstr "Ne l'augmentez pas lorsque le résultat ne peut pas changer : seules des entrées auparavant refusées (avec une erreur) sont désormais acceptées, la modification est sans effet sur le résultat (refactorisation, performances, messages), ou elle est entièrement couverte par une règle versionnée du contexte d'exécution (voir ci-dessous), dont la version est augmentée à la place."

msgid "Version 1 of the contracts is settled by the first stable release of an application recording them (DataLab 1.4): until then, no recorded activity has to be refused, and contract versions are not increased (the changes of Sigima 1.4 are part of version 1)."
msgstr "La version 1 des contrats est fixée par la première version stable d'une application qui les enregistre (DataLab 1.4) : d'ici là, aucune activité enregistrée ne doit être refusée, et les versions des contrats ne sont pas augmentées (les changements de Sigima 1.4 font partie de la version 1)."
msgid "Checklist for any change of an existing computation function: decide whether the result can change; if so, increase ``contract_version`` and the version of its ``ReplayQualification`` (after proving it, see below), update the reference cases deliberately, run ``pytest --qualification-report``, list the increased contracts in the release notes, and update the provenance tests of the applications that check a contract version (e.g. DataLab)."
msgstr "Liste de contrôle pour toute modification d'une fonction de calcul existante : déterminez si le résultat peut changer ; si c'est le cas, augmentez ``contract_version`` et la version de sa ``ReplayQualification`` (après l'avoir prouvée, voir ci-dessous), mettez à jour délibérément les cas de référence, exécutez ``pytest --qualification-report``, listez les contrats dont la version a augmenté dans les notes de version, et mettez à jour les tests de provenance des applications qui vérifient une version de contrat (par exemple DataLab)."

msgid "Rules that apply to every operation are recorded in the execution context rather than in each contract: the selection of image pixels by regions of interest (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, recorded when an input image carries regions of interest). Increase the version of such a rule, not the contract versions, when it changes: a replay refuses the calls recorded with another version."
msgstr "Les règles qui s'appliquent à toutes les opérations sont enregistrées dans le contexte d'exécution plutôt que dans chaque contrat : la sélection des pixels d'une image par les régions d'intérêt (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, enregistrée lorsqu'une image d'entrée porte des régions d'intérêt). Augmentez la version d'une telle règle, et non les versions des contrats, lorsqu'elle change : un rejeu refuse les appels enregistrés avec une autre version."
msgid "Historical exception: the contracts were published with Sigima 1.3.0 and kept version 1 in Sigima 1.4.0 although several results changed, because they had just been published and were not used yet. This one-off decision does not apply to later changes."
msgstr "Exception historique : les contrats, publiés avec Sigima 1.3.0, ont conservé la version 1 dans Sigima 1.4.0 bien que plusieurs résultats aient changé, car ils venaient d'être publiés et n'étaient pas encore exploités. Cette décision ponctuelle ne s'applique pas aux modifications ultérieures."

msgid "Rules that apply to every operation are recorded in the execution context rather than in each contract: the selection of image pixels by regions of interest (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, recorded when an input image carries regions of interest) and the X-alignment policies (see below). Increase the version of such a rule, not the contract versions, when it changes: a replay refuses the calls recorded with another version."
msgstr "Les règles qui s'appliquent à toutes les opérations sont enregistrées dans le contexte d'exécution plutôt que dans chaque contrat : la sélection des pixels d'une image par les régions d'intérêt (:data:`sigima.proc.contracts.IMAGE_ROI_CONVENTION`, enregistrée lorsqu'une image d'entrée porte des régions d'intérêt) et les politiques d'alignement en X (voir ci-dessous). Augmentez la version d'une telle règle, et non les versions des contrats, lorsqu'elle change : un rejeu refuse les appels enregistrés avec une autre version."

msgid "The structure of the contract (input roles, parameter arguments, outputs) is inferred from the annotations. Annotate every argument and the return value with names imported at runtime (not only under ``TYPE_CHECKING``). When roles cannot be inferred (more than two input objects), declare them with ``roles=(...)``."
msgstr "La structure du contrat (rôles des entrées, arguments de paramètres, sorties) est déduite des annotations. Annotez chaque argument et la valeur de retour avec des noms importés à l'exécution (pas seulement sous ``TYPE_CHECKING``). Lorsque les rôles ne peuvent pas être déduits (plus de deux objets en entrée), déclarez-les avec ``roles=(...)``."
Expand Down
Loading
Loading