wenuray 1.0.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.
- wenuray-1.0.0/.aider.conf.yml +4 -0
- wenuray-1.0.0/.claude/settings.json +15 -0
- wenuray-1.0.0/.cursor/environment.json +7 -0
- wenuray-1.0.0/.cursor/hooks.json +11 -0
- wenuray-1.0.0/.cursor/rules/README.md +26 -0
- wenuray-1.0.0/.cursor/rules/casa-io.mdc +31 -0
- wenuray-1.0.0/.cursor/rules/cicd.mdc +39 -0
- wenuray-1.0.0/.cursor/rules/design.mdc +81 -0
- wenuray-1.0.0/.cursor/rules/documentation.mdc +20 -0
- wenuray-1.0.0/.cursor/rules/error-handling.mdc +13 -0
- wenuray-1.0.0/.cursor/rules/general.mdc +32 -0
- wenuray-1.0.0/.cursor/rules/implementation.mdc +28 -0
- wenuray-1.0.0/.cursor/rules/naming.mdc +16 -0
- wenuray-1.0.0/.cursor/rules/selfcal-domain.mdc +41 -0
- wenuray-1.0.0/.cursor/rules/testing.mdc +20 -0
- wenuray-1.0.0/.cursor/skills/bug-fix/SKILL.md +49 -0
- wenuray-1.0.0/.cursor/skills/convention-review/SKILL.md +69 -0
- wenuray-1.0.0/.cursor/skills/docs-maintainer/SKILL.md +49 -0
- wenuray-1.0.0/.cursor/skills/estimate-effort/SKILL.md +34 -0
- wenuray-1.0.0/.cursor/skills/feature-dev/SKILL.md +81 -0
- wenuray-1.0.0/.cursor/skills/pr-review/SKILL.md +64 -0
- wenuray-1.0.0/.cursor/skills/qa-explorer/SKILL.md +38 -0
- wenuray-1.0.0/.cursor/skills/staged-feature/SKILL.md +58 -0
- wenuray-1.0.0/.cursor/skills/verify-ab/SKILL.md +51 -0
- wenuray-1.0.0/.cursor/skills/verify-ab/ab_compare.py +123 -0
- wenuray-1.0.0/.cursor/skills/verify-ab/ab_run.py +99 -0
- wenuray-1.0.0/.github/ISSUE_TEMPLATE/bug.md +37 -0
- wenuray-1.0.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- wenuray-1.0.0/.github/ISSUE_TEMPLATE/feature.md +28 -0
- wenuray-1.0.0/.github/dependabot.yml +29 -0
- wenuray-1.0.0/.github/pull_request_template.md +19 -0
- wenuray-1.0.0/.github/workflows/build.yaml +190 -0
- wenuray-1.0.0/.github/workflows/build_base_container.yaml +59 -0
- wenuray-1.0.0/.github/workflows/build_latest_container.yaml +112 -0
- wenuray-1.0.0/.github/workflows/release.yaml +82 -0
- wenuray-1.0.0/.gitignore +139 -0
- wenuray-1.0.0/.pre-commit-config.yaml +47 -0
- wenuray-1.0.0/.readthedocs.yaml +19 -0
- wenuray-1.0.0/AGENTS.md +116 -0
- wenuray-1.0.0/CLAUDE.md +91 -0
- wenuray-1.0.0/CONTRIBUTING.md +80 -0
- wenuray-1.0.0/CONVENTIONS.md +299 -0
- wenuray-1.0.0/Dockerfile +79 -0
- wenuray-1.0.0/Dockerfile.prod +15 -0
- wenuray-1.0.0/LICENSE +674 -0
- wenuray-1.0.0/MANIFEST.in +1 -0
- wenuray-1.0.0/PKG-INFO +455 -0
- wenuray-1.0.0/README.md +415 -0
- wenuray-1.0.0/docs/conf.py +48 -0
- wenuray-1.0.0/docs/examples.md +46 -0
- wenuray-1.0.0/docs/index.md +17 -0
- wenuray-1.0.0/docs/migration.md +83 -0
- wenuray-1.0.0/docs/requirements.txt +8 -0
- wenuray-1.0.0/environment.yml +23 -0
- wenuray-1.0.0/pyproject.toml +129 -0
- wenuray-1.0.0/requirements.txt +29 -0
- wenuray-1.0.0/setup.cfg +16 -0
- wenuray-1.0.0/src/wenuray/__init__.py +7 -0
- wenuray-1.0.0/src/wenuray/cli.py +180 -0
- wenuray-1.0.0/src/wenuray/config.py +206 -0
- wenuray-1.0.0/src/wenuray/imaging/__init__.py +9 -0
- wenuray-1.0.0/src/wenuray/imaging/backends/__init__.py +6 -0
- wenuray-1.0.0/src/wenuray/imaging/backends/_pyralysis_worker.py +233 -0
- wenuray-1.0.0/src/wenuray/imaging/backends/pyralysis.py +322 -0
- wenuray-1.0.0/src/wenuray/imaging/backends/tclean.py +178 -0
- wenuray-1.0.0/src/wenuray/imaging/contract.py +260 -0
- wenuray-1.0.0/src/wenuray/imaging/registry.py +42 -0
- wenuray-1.0.0/src/wenuray/imaging/statistics.py +98 -0
- wenuray-1.0.0/src/wenuray/recipe.py +311 -0
- wenuray-1.0.0/src/wenuray/selfcalibration/__init__.py +16 -0
- wenuray-1.0.0/src/wenuray/selfcalibration/acceptance.py +357 -0
- wenuray-1.0.0/src/wenuray/selfcalibration/caltable.py +200 -0
- wenuray-1.0.0/src/wenuray/selfcalibration/flagging.py +149 -0
- wenuray-1.0.0/src/wenuray/selfcalibration/loop.py +520 -0
- wenuray-1.0.0/src/wenuray/selfcalibration/observers.py +114 -0
- wenuray-1.0.0/src/wenuray/selfcalibration/preflight.py +256 -0
- wenuray-1.0.0/src/wenuray/selfcalibration/result.py +175 -0
- wenuray-1.0.0/src/wenuray/selfcalibration/schedule.py +117 -0
- wenuray-1.0.0/src/wenuray/selfcalibration/steps.py +318 -0
- wenuray-1.0.0/src/wenuray/selfcalibration/workspace.py +79 -0
- wenuray-1.0.0/src/wenuray/testing.py +222 -0
- wenuray-1.0.0/src/wenuray/utils/__init__.py +2 -0
- wenuray-1.0.0/src/wenuray/utils/image_utils.py +260 -0
- wenuray-1.0.0/src/wenuray/utils/selfcal_utils.py +391 -0
- wenuray-1.0.0/src/wenuray.egg-info/PKG-INFO +455 -0
- wenuray-1.0.0/src/wenuray.egg-info/SOURCES.txt +135 -0
- wenuray-1.0.0/src/wenuray.egg-info/dependency_links.txt +1 -0
- wenuray-1.0.0/src/wenuray.egg-info/entry_points.txt +6 -0
- wenuray-1.0.0/src/wenuray.egg-info/requires.txt +20 -0
- wenuray-1.0.0/src/wenuray.egg-info/scm_file_list.json +138 -0
- wenuray-1.0.0/src/wenuray.egg-info/scm_version.json +8 -0
- wenuray-1.0.0/src/wenuray.egg-info/top_level.txt +1 -0
- wenuray-1.0.0/tests/__init__.py +0 -0
- wenuray-1.0.0/tests/conftest.py +14 -0
- wenuray-1.0.0/tests/examples/__init__.py +0 -0
- wenuray-1.0.0/tests/examples/test_examples.py +210 -0
- wenuray-1.0.0/tests/integration/__init__.py +0 -0
- wenuray-1.0.0/tests/integration/conftest.py +13 -0
- wenuray-1.0.0/tests/integration/test_imager_contract_casa.py +51 -0
- wenuray-1.0.0/tests/integration/test_pyralysis_casa.py +188 -0
- wenuray-1.0.0/tests/integration/test_selfcal_loop_casa.py +187 -0
- wenuray-1.0.0/tests/integration/test_selfcal_recovers_gains_casa.py +120 -0
- wenuray-1.0.0/tests/unit/__init__.py +0 -0
- wenuray-1.0.0/tests/unit/imaging/__init__.py +0 -0
- wenuray-1.0.0/tests/unit/imaging/test_contract.py +265 -0
- wenuray-1.0.0/tests/unit/imaging/test_pyralysis_backend.py +227 -0
- wenuray-1.0.0/tests/unit/imaging/test_pyralysis_worker.py +32 -0
- wenuray-1.0.0/tests/unit/imaging/test_registry.py +47 -0
- wenuray-1.0.0/tests/unit/imaging/test_statistics.py +81 -0
- wenuray-1.0.0/tests/unit/imaging/test_tclean_backend.py +163 -0
- wenuray-1.0.0/tests/unit/selfcalibration/__init__.py +0 -0
- wenuray-1.0.0/tests/unit/selfcalibration/test_acceptance.py +238 -0
- wenuray-1.0.0/tests/unit/selfcalibration/test_caltable.py +102 -0
- wenuray-1.0.0/tests/unit/selfcalibration/test_flagging.py +105 -0
- wenuray-1.0.0/tests/unit/selfcalibration/test_loop.py +548 -0
- wenuray-1.0.0/tests/unit/selfcalibration/test_preflight.py +60 -0
- wenuray-1.0.0/tests/unit/selfcalibration/test_schedule.py +121 -0
- wenuray-1.0.0/tests/unit/selfcalibration/test_steps.py +146 -0
- wenuray-1.0.0/tests/unit/test_cli.py +166 -0
- wenuray-1.0.0/tests/unit/test_config.py +80 -0
- wenuray-1.0.0/tests/unit/test_recipe.py +309 -0
- wenuray-1.0.0/tests/unit/utils/__init__.py +0 -0
- wenuray-1.0.0/tests/unit/utils/test_image_utils.py +76 -0
- wenuray-1.0.0/tests/unit/utils/test_selfcal_utils.py +142 -0
- wenuray-1.0.0/tools/fetch_casa_data.py +68 -0
- wenuray-1.0.0/tools/hooks/block-protected-push.sh +52 -0
- wenuray-1.0.0/tools/sync_conventions.py +64 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Cursor project rules
|
|
2
|
+
|
|
3
|
+
Conventions for Wenuray contributors and AI agents. Cursor loads every `.mdc` file in this directory according to its `description`, `globs` and `alwaysApply` frontmatter. `CONVENTIONS.md` at the repo root mirrors these files for other tools. Claude Code reads them through `CLAUDE.md`.
|
|
4
|
+
|
|
5
|
+
| File | Topic |
|
|
6
|
+
|------|-------|
|
|
7
|
+
| [`general.mdc`](general.mdc) | Purpose of Wenuray and general principles — read first |
|
|
8
|
+
| [`design.mdc`](design.mdc) | Parameter budget, config objects, rounds, policies, backends |
|
|
9
|
+
| [`selfcal-domain.mdc`](selfcal-domain.mdc) | Self-calibration theory invariants |
|
|
10
|
+
| [`casa-io.mdc`](casa-io.mdc) | CASA tools, Measurement Sets, files, subprocesses, FITS |
|
|
11
|
+
| [`implementation.mdc`](implementation.mdc) | Clear purpose, explicit code, known types, reading data in samples |
|
|
12
|
+
| [`naming.mdc`](naming.mdc) | Naming and method order |
|
|
13
|
+
| [`documentation.mdc`](documentation.mdc) | Docstrings, comments, README/examples |
|
|
14
|
+
| [`error-handling.mdc`](error-handling.mdc) | Validation, exceptions, logging |
|
|
15
|
+
| [`testing.mdc`](testing.mdc) | pytest layout, CASA-free unit tests, simulated end-to-end tests |
|
|
16
|
+
| [`cicd.mdc`](cicd.mdc) | GitHub Actions, packaging, containers, branches/commits |
|
|
17
|
+
|
|
18
|
+
Naming, OOP and policy conventions follow [Pyralysis](https://gitlab.com/clirai/pyralysis) so the two projects read the same. Pyralysis's array-backend, precision and UML rules have no counterpart here. How agents use these rules, with skills and hooks, is in [`AGENTS.md`](../../AGENTS.md).
|
|
19
|
+
|
|
20
|
+
## Keeping `CONVENTIONS.md` in sync
|
|
21
|
+
|
|
22
|
+
The `.mdc` files are the source of truth. After editing them, regenerate the mirror:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
python tools/sync_conventions.py
|
|
26
|
+
```
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Working with CASA tools/tasks, Measurement Sets, files and subprocesses safely
|
|
3
|
+
globs:
|
|
4
|
+
- "src/**/*.py"
|
|
5
|
+
alwaysApply: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# CASA tools and tables
|
|
9
|
+
|
|
10
|
+
- Don't create module-level tool singletons (`tb = table()`). Create the tool where it's needed and **always close it**, using `try/finally` or a small context manager (`with open_table(path) as tb:`). Close TaQL result tables too.
|
|
11
|
+
- Call CASA tasks with keyword arguments only. Use the task's own parameter names.
|
|
12
|
+
- Check that a column exists before reading it. Remember that virtual models carry no `MODEL_DATA` column.
|
|
13
|
+
- CASA ≥ 6.6.4 needs measures data (`casaconfig`). Don't rely on a runtime download in tests or containers.
|
|
14
|
+
|
|
15
|
+
# Files and directories
|
|
16
|
+
|
|
17
|
+
- Never run shell commands to delete files (`os.system("rm -rf ...")`). Remove only paths Wenuray created itself, with `shutil.rmtree`/`Path.unlink`.
|
|
18
|
+
- Put temporary products in a per-run directory (`tempfile.mkdtemp` under the output directory), never in the current working directory with fixed names.
|
|
19
|
+
- Build paths with `pathlib.Path`. Don't concatenate strings or `partition(".ms")`.
|
|
20
|
+
- A Measurement Set comes with a sibling `<ms>.flagversions` directory. Copying, moving or deleting the MS must handle both.
|
|
21
|
+
|
|
22
|
+
# Subprocesses (external executables)
|
|
23
|
+
|
|
24
|
+
- `subprocess.run([...], check=True)` with an argument **list**. No shell strings and no `shlex.split` of concatenated text.
|
|
25
|
+
- Log the command, and surface the exit code and stderr in the raised exception.
|
|
26
|
+
|
|
27
|
+
# FITS
|
|
28
|
+
|
|
29
|
+
- Read data inside the `with fits.open(...)` block, or copy it. Don't return an HDU from a closed file.
|
|
30
|
+
- NumPy image shapes are `(NAXIS2, NAXIS1)`.
|
|
31
|
+
- Interpolate masks with nearest-neighbour (or threshold afterwards), never bilinear.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: GitHub Actions, containers, packaging and releases
|
|
3
|
+
globs:
|
|
4
|
+
- ".github/**"
|
|
5
|
+
- "Dockerfile*"
|
|
6
|
+
- "pyproject.toml"
|
|
7
|
+
- ".pre-commit-config.yaml"
|
|
8
|
+
alwaysApply: false
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# GitHub Actions
|
|
12
|
+
|
|
13
|
+
- Pin actions to a release tag (or SHA), never `@master`. Keep them current via `.github/dependabot.yml`.
|
|
14
|
+
- Only push container images on pushes to default branches or tags, **never from `pull_request`**. PRs build with `push: false`.
|
|
15
|
+
- Build images from the checked-out source (COPY the wheel or source), not `pip install git+...` of a branch.
|
|
16
|
+
- Pure-Python jobs (lint, build, unit tests) run on `actions/setup-python` with pip caching, not in the CUDA image. Reserve the heavy image for GPU and integration jobs.
|
|
17
|
+
- Use `GITHUB_TOKEN` with minimal `permissions:`. Publish to PyPI with trusted publishing (OIDC).
|
|
18
|
+
- Add `concurrency:` groups so superseded runs are cancelled.
|
|
19
|
+
- Lint in CI with `pre-commit run --all-files`, the same hooks as locally.
|
|
20
|
+
|
|
21
|
+
# Packaging
|
|
22
|
+
|
|
23
|
+
- `pyproject.toml` is the single source of metadata and dependencies. Use lower bounds in `dependencies`; put exact pins only in lock/constraint files for CI and Docker.
|
|
24
|
+
- Declare only what is imported. Optional backends go in extras (e.g. `wenuray[pyralysis]`).
|
|
25
|
+
- Version comes from setuptools-scm, read at run time with `importlib.metadata.version("wenuray")`; no version file is written.
|
|
26
|
+
- `requirements.txt` is the pinned set for CI and Docker, generated with `uv pip compile pyproject.toml`.
|
|
27
|
+
|
|
28
|
+
# Containers
|
|
29
|
+
|
|
30
|
+
- Combine `apt-get update && apt-get install ... && rm -rf /var/lib/apt/lists/*` in one layer.
|
|
31
|
+
- Pin external sources (tags or commit SHAs) cloned or downloaded during builds.
|
|
32
|
+
- Use multi-stage builds: compile in a devel stage, run on a runtime base.
|
|
33
|
+
|
|
34
|
+
# Branches, commits and releases
|
|
35
|
+
|
|
36
|
+
- `development` is the integration branch and `master` the release branch. Work on `chg/<issue>-<slug>` or `fix/<issue>-<slug>` branches from `development` and open PRs into `development`. Never base a PR on another unmerged PR (squash merges strand it). Direct pushes to `development` and `master` are blocked by `tools/hooks/block-protected-push.sh` for agents.
|
|
37
|
+
- Commit prefixes used in history: `CHG:` (change/feature) and `FIX:` (bug fix).
|
|
38
|
+
- Reference the GitHub issue in commits and PRs (`Closes #25`).
|
|
39
|
+
- Semantic versioning via git tags; releases publish to PyPI.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Class design for Wenuray — one self-cal loop, CalibrationStep hierarchy, CalStack, schedule rules, imager contract, config objects, policies
|
|
3
|
+
globs:
|
|
4
|
+
- "src/**/*.py"
|
|
5
|
+
alwaysApply: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Architecture (see GitHub issues #22 and #23)
|
|
9
|
+
|
|
10
|
+
```text
|
|
11
|
+
Selfcal (the ONLY self-cal loop — template method)
|
|
12
|
+
├── steps: CalibrationStep (ABC, frozen dataclass)
|
|
13
|
+
│ ├── Phasecal
|
|
14
|
+
│ └── _AmplitudeStep (solnorm, amplitude prerequisites)
|
|
15
|
+
│ ├── Ampcal
|
|
16
|
+
│ └── AmpPhasecal
|
|
17
|
+
├── imager: Imager (ABC; image() enforces the contract, subclasses implement _reconstruct)
|
|
18
|
+
├── solver: GainSolver · acceptance: AcceptancePolicy · stack policy: StackPolicy
|
|
19
|
+
├── workspace: MSWorkspace (snapshot / restore)
|
|
20
|
+
└── observers: SelfcalObserver (logging, plots, provenance)
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
# Inheritance: put it where behaviour varies
|
|
24
|
+
|
|
25
|
+
- **The loop exists once**, in `Selfcal.run`. Never add a `run()` loop to a step or imager subclass.
|
|
26
|
+
- Calibration types are **subclasses of `CalibrationStep`** because they differ in behaviour (prerequisites, normalisation, plotting, gaincal extras). Put what varies in `ClassVar`s and small hooks (`_extra_gaincal_kwargs`), and build everything shared once in the base class. Shared amplitude behaviour lives once in `_AmplitudeStep`.
|
|
27
|
+
- A new calibration type = a new subclass plus its transition rules. The loop doesn't change.
|
|
28
|
+
- Imagers subclass `Imager` and implement only `_reconstruct`. The public `image()` is final, so it can always enforce the contract.
|
|
29
|
+
- Use a subclass when behaviour differs; use a field when only a value differs. Don't add `if calmode == ...` or `isinstance` branches in the loop.
|
|
30
|
+
|
|
31
|
+
# Dataclasses
|
|
32
|
+
|
|
33
|
+
- Steps, config objects (`Selection`, `SolveOptions`, `ApplyOptions`, `ImageGeometry`, `Weighting`), `CalTable`, `CalStack` and results are `@dataclass(frozen=True)`.
|
|
34
|
+
- Use `KW_ONLY` so the primary value stays positional (`Phasecal("60s")`) and everything else is keyword-only. That keeps subclasses free to add defaulted fields.
|
|
35
|
+
- Validate in `__post_init__`, once. Derive values with `dataclasses.replace(...)`, which rejects unknown names, never `setattr` by string.
|
|
36
|
+
- No mutable defaults: use `field(default_factory=...)`, tuples or `MappingProxyType`.
|
|
37
|
+
|
|
38
|
+
# multimethod: type dispatch at boundaries and for rules
|
|
39
|
+
|
|
40
|
+
Use `multimethod` (as Pyralysis does) when behaviour depends on the **runtime types of the arguments**, instead of `isinstance` chains:
|
|
41
|
+
|
|
42
|
+
- `check_transition(prev, step, stack)`: schedule rules dispatched on `(previous step type, next step type)`. Each theory rule is one overload with its own test.
|
|
43
|
+
- Coercion at the API boundary: `solint_seconds(str | float)`, `to_casa_quantity(str | float | Quantity)`, `as_initial_stack(None | CalStack | SelfcalResult | str)`.
|
|
44
|
+
|
|
45
|
+
Don't use it where ordinary polymorphism (a method on the class) is enough.
|
|
46
|
+
|
|
47
|
+
# Calibration state and schedules
|
|
48
|
+
|
|
49
|
+
- Calibration state is an immutable `CalStack` of `CalTable(path, step, spwmap, interp)`. Only `CalStack` builds `gaincal` pre-apply and `applycal` argument lists. `spwmap` is derived per table, never a user parameter.
|
|
50
|
+
- Every solve pre-applies the accepted stack (the `StackPolicy` decides which tables are kept).
|
|
51
|
+
- `Selfcal.__init__` validates the whole schedule by simulating the stack. A schedule that breaks calibration theory raises `ScheduleError` **before any I/O**. Continuing a run uses `Selfcal.resume(result, ...)`, never another object's internals.
|
|
52
|
+
|
|
53
|
+
# Parameter budget
|
|
54
|
+
|
|
55
|
+
- A public constructor takes **at most ~8 parameters**, all of them used. If you need more, group them into a config object or split the class.
|
|
56
|
+
- Don't add a parameter that theory or the data can supply (spwmap, cell/imsize, noise-based thresholds).
|
|
57
|
+
|
|
58
|
+
# Policies and strategies
|
|
59
|
+
|
|
60
|
+
Follow the Pyralysis "Policies" convention ([`.cursor/rules/design.mdc` in Pyralysis](https://gitlab.com/clirai/pyralysis/-/blob/development/.cursor/rules/design.mdc)): one family per configurable decision, frozen dataclasses, the default is an instance, and policies decide but never act.
|
|
61
|
+
|
|
62
|
+
- `AcceptancePolicy` (keep or roll back a round; `AllOf`/`AnyOf` combinators), `StackPolicy` (`Accumulate`, `ReplacePhase`), `GainSolver`.
|
|
63
|
+
- Strings (`"tclean"`, `"ap"`, `"alma"`) only at the boundary (config files, CLI), turned into objects by one registry/factory per family. Third-party imagers register via the `wenuray.imagers` entry point.
|
|
64
|
+
|
|
65
|
+
# Imagers (plug-ins)
|
|
66
|
+
|
|
67
|
+
- The contract: image `CORRECTED_DATA` and write `MODEL_DATA` to the MS given in the request. `Imager.image()` invalidates the model first and verifies it afterwards, raising `ImagerContractError` if it wasn't written. It also refuses an imager whose `geometry.cell` is coarser than the Nyquist cell of the selected data (#60): visibilities beyond the grid can't be modelled. The opt-out is an explicit `Selection(uvrange=...)`, never a silent drop.
|
|
68
|
+
- Imagers are stateless with respect to the MS (it arrives in every `ImagingRequest`).
|
|
69
|
+
- Backend options pass through using the backend's own names (`tclean`'s `usemask`). Validate them against the backend's signature.
|
|
70
|
+
- Every imager, including users' own, should pass `wenuray.testing.ImagerContractSuite`.
|
|
71
|
+
- Image statistics are computed once, by `ImageStatistics`, for all imagers. Optional backends are extras with lazy imports.
|
|
72
|
+
|
|
73
|
+
# Cross-cutting concerns and side effects
|
|
74
|
+
|
|
75
|
+
- Logging, gain plots and provenance are `SelfcalObserver`s, not constructor flags.
|
|
76
|
+
- No I/O in constructors. `MSWorkspace` owns snapshots, rollback and cleanup.
|
|
77
|
+
- Keep argument building, rules and statistics pure, so they can be tested without CASA.
|
|
78
|
+
|
|
79
|
+
# Backwards compatibility
|
|
80
|
+
|
|
81
|
+
v1.0 replaces the legacy constructors. Legacy arguments (`visfile=`, `solint=[...]`, `previous_selfcal=`) raise a `TypeError` that shows the new call. Deprecated aliases emit `DeprecationWarning` and have a test showing they behave like the new form.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Docstrings, comments, user docs and examples
|
|
3
|
+
globs:
|
|
4
|
+
- "**/*.py"
|
|
5
|
+
- "**/*.md"
|
|
6
|
+
alwaysApply: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Docstrings and comments
|
|
10
|
+
|
|
11
|
+
- NumPy-style docstrings on every public class, method and function, with units and accepted values (e.g. `solint : str` — `"inf"`, `"60s"`, `"int"`).
|
|
12
|
+
- For self-cal behaviour, state the **theory** assumption in the docstring (which column is solved and which tables are pre-applied).
|
|
13
|
+
- Comments explain **why**, never what. Keep them sparse.
|
|
14
|
+
|
|
15
|
+
# User documentation
|
|
16
|
+
|
|
17
|
+
- The README and every example (`examples/<name>/recipe.toml` and `selfcal.py`) **must run against the current API**. When you change a public API, update them in the same PR. CI smoke-tests the examples.
|
|
18
|
+
- Examples should be recipes a user can copy: a config file plus one command, not a script that parses `sys.argv` with `eval`.
|
|
19
|
+
- Each example is a recipe a user can copy plus the Python script that builds the same run; a test keeps them equal.
|
|
20
|
+
- Wenuray ships no instrument presets. Instrument-specific values in examples are marked as the example's, with links to CASA guides where relevant.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Exceptions, validation and logging
|
|
3
|
+
globs:
|
|
4
|
+
- "**/*.py"
|
|
5
|
+
alwaysApply: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Errors and logging
|
|
9
|
+
|
|
10
|
+
- Validate configuration **early**, at construction or before the first round, with messages that say how to fix it (e.g. "spw selection starts at 1 but spwmap maps to 0").
|
|
11
|
+
- Raise exceptions; never return sentinel values or swallow errors. Use `FileNotFoundError` for missing inputs, `ValueError` for bad values, and `TypeError` for wrong types.
|
|
12
|
+
- Silent fallbacks that change the science (point-source model, wrong column, dropped table) must be **errors**, or at least explicit warnings.
|
|
13
|
+
- Use `logging` (module logger `logging.getLogger(__name__)`), not `print`. Log each round's metrics at INFO.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Wenuray purpose, scope, and general principles — read first
|
|
3
|
+
globs:
|
|
4
|
+
- "**/*.py"
|
|
5
|
+
alwaysApply: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# What Wenuray is for
|
|
9
|
+
|
|
10
|
+
Wenuray (ca**S**a pytho**N** self-calibrati**O**n frame**W**ork) exists so radio astronomers stop rewriting a one-off self-calibration script for every new dataset. A user should be able to describe a self-cal procedure **briefly, declaratively and reproducibly**, and rerun it on another dataset by changing only what really differs.
|
|
11
|
+
|
|
12
|
+
Judge every change against that goal:
|
|
13
|
+
|
|
14
|
+
- Does it make the user's procedure **shorter or clearer**? Prefer defaults derived from theory or the data over new user-facing parameters. Wenuray ships **no instrument presets**: users keep their own shared settings as TOML files (`preset = "group.toml"`), and limits that depend on an instrument or the science are opt-in, with no default value.
|
|
15
|
+
- Does it keep runs **reproducible**? Same config + same input data + same versions must give the same result, and a run must record what it did.
|
|
16
|
+
- Is it **correct by radio-interferometry theory**? See `selfcal-domain.mdc`. A convenient API that gives wrong calibration is worse than no API.
|
|
17
|
+
|
|
18
|
+
Layers, from the user inward:
|
|
19
|
+
|
|
20
|
+
1. **TOML recipe** (`wenuray run recipe.toml`) — what most users touch. A recipe is a serialisation of the Python objects, not a second API (`wenuray.recipe`).
|
|
21
|
+
2. **Python API** (`Selfcal`, the `Phasecal`/`Ampcal`/`AmpPhasecal` steps, config objects) — for power users and scripting.
|
|
22
|
+
3. **Backends** — CASA tasks (`gaincal`, `applycal`, `tclean`) and imagers (`Tclean`, `Pyralysis`, or a user's own through the `wenuray.imagers` entry point).
|
|
23
|
+
|
|
24
|
+
The design was set in GitHub issue #22 (milestone "v1.0 redesign"); self-cal with the Pyralysis imager is #24. How agents work on the repository is in `AGENTS.md`.
|
|
25
|
+
|
|
26
|
+
# General principles
|
|
27
|
+
|
|
28
|
+
- Python version follows `requires-python` in `pyproject.toml`. It is `>=3.12,<3.14`, the overlap of the CASA wheels and Pyralysis (#30).
|
|
29
|
+
- Keep code simple, readable and maintainable; avoid duplication (the three copied self-cal loops are the cautionary example).
|
|
30
|
+
- PEP 8, formatted with `yapf` + `isort` (config in `setup.cfg`, column limit 100), enforced via `pre-commit`.
|
|
31
|
+
- `pytest` for tests. New behaviour needs tests (see `testing.mdc`).
|
|
32
|
+
- **Legacy code predates these rules.** New and changed code must follow them. Don't mass-refactor untouched code outside the scope of the issue you are working on; open or extend an issue instead.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Implementation principles — clear purpose, explicit code, known types, data read in samples
|
|
3
|
+
globs:
|
|
4
|
+
- "**/*.py"
|
|
5
|
+
alwaysApply: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Implementation
|
|
9
|
+
|
|
10
|
+
- Every class, function and module has one clear purpose. If its docstring needs "and", split it.
|
|
11
|
+
- No global or module-level mutable state, and no module-level CASA tool instances (`casa-io.mdc`).
|
|
12
|
+
Code is as stateless as possible: config objects are frozen, and a run's state lives in the
|
|
13
|
+
loop and its result.
|
|
14
|
+
- Prefer explicit code over implicit behaviour. No magic methods without a clear reason, no hidden
|
|
15
|
+
defaults that change the science.
|
|
16
|
+
- **Dispatch on known types with `isinstance` or `multimethod`, never by probing attributes**
|
|
17
|
+
(`getattr(obj, "geometry", None)`). An optional capability is a base-class attribute or hook with
|
|
18
|
+
a documented default (e.g. `Imager.geometry = None`, `Imager.provenance()`).
|
|
19
|
+
- **Read measurement sets in samples, never whole columns, when a statistic is enough.** Large
|
|
20
|
+
datasets have billions of visibilities: sample rows evenly (`selected_rows`), read blocks
|
|
21
|
+
spread through the selection (`preflight.read_sample`), and keep the cost independent of the
|
|
22
|
+
dataset's size. Read whole columns only when the operation needs every row (e.g. `uv_extent`
|
|
23
|
+
for the Nyquist check reads `u`, `v` and `FLAG_ROW`, never the visibilities).
|
|
24
|
+
- Use NumPy vectorisation and broadcasting over Python loops on rows; loop over baselines,
|
|
25
|
+
spectral windows or tables, not over visibilities.
|
|
26
|
+
- Profile before optimising (`cProfile`, `line_profiler`). Most of a run's time is in CASA tasks
|
|
27
|
+
and the imager; optimise Wenuray's own code only where a profile shows it matters.
|
|
28
|
+
- Use the dtype's limits (`np.finfo(dtype).eps`) instead of hard-coded small constants.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Naming conventions and method order (aligned with Pyralysis)
|
|
3
|
+
globs:
|
|
4
|
+
- "**/*.py"
|
|
5
|
+
alwaysApply: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Naming
|
|
9
|
+
|
|
10
|
+
- Classes `CamelCase` (`SelfcalPipeline`, `CalTable`); functions, methods and modules `snake_case`.
|
|
11
|
+
- Functions start with a verb (`derive_spwmap()`, `build_applycal_args()`).
|
|
12
|
+
- Plural names for collections (`steps`, `caltables`).
|
|
13
|
+
- Use radio-astronomy / CASA terminology consistently: `solint`, `calmode`, `refant`, `spwmap`, `combine`, `MODEL_DATA`. Don't invent synonyms.
|
|
14
|
+
- User-facing parameters that map onto a CASA parameter keep the **CASA name** (`usemask`, `minblperant`).
|
|
15
|
+
- Private `__name`, protected `_name`. Inside a class, order methods private → protected → public.
|
|
16
|
+
- Booleans read as predicates (`is_model_written`, `has_corrected_column`).
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Radio-interferometric self-calibration invariants every change must respect (gaincal/applycal/model/spwmap/acceptance)
|
|
3
|
+
globs:
|
|
4
|
+
- "src/**/*.py"
|
|
5
|
+
alwaysApply: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Self-calibration invariants
|
|
9
|
+
|
|
10
|
+
These are domain rules, not style preferences. Code that breaks them produces wrong science silently. When unsure, cite the CASA documentation for the task in the PR.
|
|
11
|
+
|
|
12
|
+
## Data and model
|
|
13
|
+
|
|
14
|
+
- `gaincal` **solves against `DATA`**, with any `gaintable`s applied on the fly. It never reads `CORRECTED_DATA`. The input MS must therefore have calibrated data in `DATA` (`split` first), or every prior table must be pre-applied explicitly. Detect and warn when this doesn't hold.
|
|
15
|
+
- The solve needs a model in `MODEL_DATA` (or a virtual model). Without one, `gaincal` silently uses a **1 Jy point source at the phase centre**. Every imager used for self-cal **must** write the model of the accepted image to the MS that the next `gaincal` reads, and the pipeline must check that it did.
|
|
16
|
+
- The imager must image the **calibrated** visibilities (`CORRECTED_DATA` after `applycal`). This applies to Pyralysis too: request and fit `CORRECTED_DATA`, not `DATA`.
|
|
17
|
+
|
|
18
|
+
## Calibration tables
|
|
19
|
+
|
|
20
|
+
- Keep an **ordered stack** of accepted tables. Each new solve **pre-applies** the stack (`gaincal(gaintable=stack)`), and `applycal` applies the stack plus the new table. Don't replace the stack with the latest table.
|
|
21
|
+
- Exception: a fresh phase-only solve may deliberately solve from scratch and replace the previous phase table. Make that explicit in the code.
|
|
22
|
+
- `spwmap` and `interp` belong to **each table**. When `combine` includes `'spw'`, map every selected spw to the **lowest selected spw id**; otherwise use `[]`. Never share one `spwmap` across tables.
|
|
23
|
+
- Amplitude (`a`/`ap`) solves pre-apply the best phase solutions, normally at a shorter solint. Otherwise phase decorrelation within the solint biases the amplitudes low.
|
|
24
|
+
- `solnorm=True` for amplitude solutions preserves the flux scale. Document any change to that.
|
|
25
|
+
- `calwt=False` is the default for self-cal.
|
|
26
|
+
|
|
27
|
+
## Selection
|
|
28
|
+
|
|
29
|
+
- Solve, apply and image use the **same** field/spw/uvrange selection (one `Selection` object). A `uvrange` restriction for the solve must be explicit and recorded.
|
|
30
|
+
|
|
31
|
+
## Acceptance and stopping
|
|
32
|
+
|
|
33
|
+
- PSNR alone is not sufficient. Amplitude self-cal can raise PSNR by absorbing noise or unmodelled flux.
|
|
34
|
+
- Compare rounds with the **same** noise estimator and region, with the source masked, for every imager.
|
|
35
|
+
- Also track the fraction of flagged solutions (`calflag` flags data where a solution fails), the restoring beam, and the integrated flux.
|
|
36
|
+
- Rollback restores the previous round's data, flags, model and table stack together.
|
|
37
|
+
|
|
38
|
+
## Reproducibility
|
|
39
|
+
|
|
40
|
+
- Each run records: the fully resolved config (presets and defaults expanded), Wenuray/CASA/backend versions, an input MS fingerprint, the accepted table stack, and per-round metrics.
|
|
41
|
+
- No hidden randomness. If a backend is stochastic, a seed is part of the config.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: pytest layout, CASA-free unit tests, simulated end-to-end self-cal tests
|
|
3
|
+
globs:
|
|
4
|
+
- "tests/**"
|
|
5
|
+
- "src/**/*.py"
|
|
6
|
+
alwaysApply: true
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Tests
|
|
10
|
+
|
|
11
|
+
- Layout: `tests/unit/` mirrors `src/wenuray/`; `tests/integration/` holds tests that need CASA; `tests/examples/` runs the README and `examples/`.
|
|
12
|
+
- **Unit tests must not need CASA.** Keep argument building, spwmap derivation, config validation and statistics pure, so they can be tested with synthetic inputs (FITS arrays, dataclasses).
|
|
13
|
+
- Mark CASA-dependent tests `@pytest.mark.casa`, GPU tests `@pytest.mark.gpu`, and long tests `@pytest.mark.slow`.
|
|
14
|
+
- **Scientific regression test:** `wenuray.testing.simulate_point_source_ms` simulates a small MS with seeded antenna phase errors, and `injected_phases` returns them. `tests/integration/test_selfcal_recovers_gains_casa.py` self-calibrates it with each imager and asserts that the recovered phases match the injected ones, up to the reference antenna's phase and a source shift (self-cal can't fix the absolute position), and that the PSNR improves. Any change to the self-cal loop must keep it passing (#32).
|
|
15
|
+
- **Import Wenuray as users get it:** `from wenuray.x import Y`, never `src.wenuray.x`, also in `mock.patch` and `monkeypatch` targets. Two import paths give two copies of every class, and `isinstance` fails between them.
|
|
16
|
+
- **Assert values, not just form.** A test of a written, recorded or converted value compares what reads back with the value computed the plain way; asserting only a type, a shape or that a call ran passes broken code. A fixture makes the tested quantity non-trivial: e.g. an imaging test whose cell is coarser than the Nyquist cell runs on data the grid can't hold, so it tests nothing (#60). Simulated data must be physically valid for the check.
|
|
17
|
+
- **Compare with physics in mind:** gains up to the degeneracies self-calibration has (reference phase, position), noise-limited tolerances derived from the simulation (e.g. one solution's phase noise σ / (S √((N−1) n_pol))), not tolerances tuned until the test passes.
|
|
18
|
+
- Mocks declare absent data explicitly. A bare `Mock` attribute pretends a column or a capability exists.
|
|
19
|
+
- Every bug fix comes with a test that fails before the fix.
|
|
20
|
+
- Test the README/example recipes at least by import or a dry run.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bug-fix
|
|
3
|
+
description: >-
|
|
4
|
+
Fix a Wenuray bug: reproduce it with a failing test, fix the root cause, keep the regression test,
|
|
5
|
+
run pre-commit and pytest, push and open a PR. Use for issues labelled bug or when asked to fix a
|
|
6
|
+
defect.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Bug fix
|
|
10
|
+
|
|
11
|
+
## Before coding
|
|
12
|
+
|
|
13
|
+
1. Read the issue: steps to reproduce, expected vs actual behaviour, environment, logs.
|
|
14
|
+
2. Read `AGENTS.md` and `.cursor/rules/`. Search for duplicates and for the tests that cover the
|
|
15
|
+
area.
|
|
16
|
+
3. Decide whether the bug is Wenuray's or upstream's (CASA, Pyralysis). An upstream bug gets an
|
|
17
|
+
upstream issue with a minimal reproduction and, if needed, a workaround in Wenuray that names the
|
|
18
|
+
upstream issue.
|
|
19
|
+
|
|
20
|
+
## Reproduce first
|
|
21
|
+
|
|
22
|
+
1. Write the smallest test that fails because of the bug: a unit test with mocks if the logic is
|
|
23
|
+
pure, an integration test on simulated data if it needs CASA.
|
|
24
|
+
2. Run it and see it fail for the reason in the issue, not another one.
|
|
25
|
+
3. Check the test's data can show the bug: e.g. a cell must be Nyquist-sampled for an imaging
|
|
26
|
+
check to mean anything (`testing.mdc`, "Assert values, not just form").
|
|
27
|
+
|
|
28
|
+
## Fix
|
|
29
|
+
|
|
30
|
+
- Branch `fix/<issue>-<slug>` from `origin/development`; commits `FIX: <what> (#<issue>)`.
|
|
31
|
+
- Fix the root cause, not the symptom. A silent fallback that changes the science becomes an
|
|
32
|
+
error or an explicit warning (`error-handling.mdc`).
|
|
33
|
+
- Keep the regression test. Confirm it fails with only the fix reverted (`git checkout
|
|
34
|
+
origin/development -- <file>`, run, restore), not by stashing unrelated work.
|
|
35
|
+
|
|
36
|
+
## Quality gates
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pre-commit run --files <changed files>
|
|
40
|
+
pytest <targeted tests> # CASA tests in Docker if needed, see AGENTS.md
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Run `convention-review` on the diff; run `verify-ab` if the fix changes results of an existing
|
|
44
|
+
path.
|
|
45
|
+
|
|
46
|
+
## Pull request
|
|
47
|
+
|
|
48
|
+
Open a PR into `development` with `Closes #<issue>`, the reproduction, the root cause, the
|
|
49
|
+
regression test and the commands run. If the issue has no `effort:N` label, suggest one.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: convention-review
|
|
3
|
+
description: >-
|
|
4
|
+
Deep review of a Wenuray diff (branch or PR) against the repository's rules: self-calibration
|
|
5
|
+
invariants, genericity across arrays, CASA I/O, design budget, tests and docs. Reports findings
|
|
6
|
+
ranked by severity, each with a concrete failure scenario. Use before opening or merging a PR, or
|
|
7
|
+
when asked to review code. For CI failures and review threads use pr-review.
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Convention review
|
|
11
|
+
|
|
12
|
+
Review with fresh eyes: read the diff (`git diff origin/development...HEAD`) and the rules it
|
|
13
|
+
touches, not the author's explanation. Every finding needs a concrete scenario (data, selection,
|
|
14
|
+
configuration) that produces wrong calibration, a crash, a regression or a broken rule. No
|
|
15
|
+
scenario, no finding. Cite the rule file.
|
|
16
|
+
|
|
17
|
+
## Checklist
|
|
18
|
+
|
|
19
|
+
**Self-calibration theory** (`selfcal-domain.mdc`)
|
|
20
|
+
|
|
21
|
+
- gaincal solves against `DATA` with the accepted stack pre-applied; the imager images
|
|
22
|
+
`CORRECTED_DATA` and writes `MODEL_DATA` for the selected data.
|
|
23
|
+
- One `Selection` for solve, apply and imaging; every table's `spwmap` and `interp` travel with it
|
|
24
|
+
through `CalStack`.
|
|
25
|
+
- A rejected round restores data, flags, model and stack; acceptance is never PSNR alone.
|
|
26
|
+
- Amplitude steps come after a phase solution; schedules are validated before any I/O.
|
|
27
|
+
|
|
28
|
+
**Generic across arrays** (`general.mdc`, `design.mdc`)
|
|
29
|
+
|
|
30
|
+
- No instrument names, instrument-tuned numbers or `if instrument == ...`. Defaults come from
|
|
31
|
+
theory (closure needs 3 or 4 antennas, Nyquist sampling) or from the data; instrument- or
|
|
32
|
+
science-dependent limits are opt-in with no default.
|
|
33
|
+
- Variation goes through hooks, ClassVars, registries and `multimethod`, not `if` chains.
|
|
34
|
+
|
|
35
|
+
**Design** (`design.mdc`, `implementation.mdc`)
|
|
36
|
+
|
|
37
|
+
- At most about eight constructor parameters, all used; options grouped in frozen config objects.
|
|
38
|
+
- Known types dispatched with `isinstance`/`multimethod`, not attribute probing.
|
|
39
|
+
- Recipes: a new public class serialises (fields, or `to_recipe`/`from_recipe`) and round-trips.
|
|
40
|
+
|
|
41
|
+
**CASA and data** (`casa-io.mdc`, `implementation.mdc`)
|
|
42
|
+
|
|
43
|
+
- No I/O in constructors; no module-level CASA tools; tables closed on every path.
|
|
44
|
+
- No fixed file names in the working directory; outputs under the run's directory.
|
|
45
|
+
- Reads of a measurement set in samples when a statistic suffices: what happens on 10⁹ rows?
|
|
46
|
+
|
|
47
|
+
**Errors** (`error-handling.mdc`)
|
|
48
|
+
|
|
49
|
+
- Silent fallbacks that change the science are errors or explicit warnings; messages say how to
|
|
50
|
+
fix the problem.
|
|
51
|
+
|
|
52
|
+
**Tests** (`testing.mdc`)
|
|
53
|
+
|
|
54
|
+
- Behaviour changes have tests; regression tests fail on the old code.
|
|
55
|
+
- Assertions about values, fixtures where the tested quantity is non-trivial and the simulated
|
|
56
|
+
data are physically valid (Nyquist-sampled cells, enough S/N).
|
|
57
|
+
- Tolerances derived from the simulation's noise, and the degeneracies of self-cal respected.
|
|
58
|
+
- Unit tests stay CASA-free; patch targets are `wenuray.…`, never `src.wenuray.…`.
|
|
59
|
+
|
|
60
|
+
**Docs** (`documentation.mdc`)
|
|
61
|
+
|
|
62
|
+
- Docstrings, README, `docs/`, examples and recipes match the new behaviour; examples still build
|
|
63
|
+
equal runs; comments say why.
|
|
64
|
+
|
|
65
|
+
## Output
|
|
66
|
+
|
|
67
|
+
Findings ranked most severe first: `file:line`, the defect in one sentence, the failure scenario,
|
|
68
|
+
the suggested fix. Then a short list of what was checked and found fine. A finding that needs a
|
|
69
|
+
run (results, speed) goes to `verify-ab` instead of a guess.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: docs-maintainer
|
|
3
|
+
description: >-
|
|
4
|
+
Improve Wenuray's documentation: README, Sphinx pages (MyST Markdown, Read the Docs), examples and
|
|
5
|
+
docstrings, keeping docs and code in step. Docs-only PRs. Use for documentation issues or when
|
|
6
|
+
asked to audit or improve the docs.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Docs maintainer
|
|
10
|
+
|
|
11
|
+
## Scope
|
|
12
|
+
|
|
13
|
+
| In | Paths |
|
|
14
|
+
| --- | --- |
|
|
15
|
+
| Front page and guides | `README.md` (it is the docs' front page), `docs/*.md`, `docs/migration.md` |
|
|
16
|
+
| Examples | `examples/<name>/recipe.toml` and `selfcal.py`, `examples/README.md` |
|
|
17
|
+
| API reference | Public docstrings (numpy style), rendered by sphinx-autoapi |
|
|
18
|
+
| Agent and contributor docs | `AGENTS.md`, `CONTRIBUTING.md` (only when that is the task) |
|
|
19
|
+
|
|
20
|
+
Out of scope: behaviour changes. If the docs can only be right after a code change, open an issue
|
|
21
|
+
or use `feature-dev`.
|
|
22
|
+
|
|
23
|
+
## Goals, in order
|
|
24
|
+
|
|
25
|
+
1. **Correctness:** every snippet runs against the current API; names, defaults and file paths
|
|
26
|
+
match the code; instrument values in examples are marked as the example's, not Wenuray's.
|
|
27
|
+
2. **Parity:** every public class and option is documented; recipes and scripts in `examples/`
|
|
28
|
+
build equal runs (a test checks it).
|
|
29
|
+
3. **Structure:** pages in the right toctree, no duplication (the README is included, not copied).
|
|
30
|
+
4. **Clarity:** short sentences, the theory where a choice depends on it (e.g. why a cell must be
|
|
31
|
+
Nyquist-sampled), links to CASA guides for CASA parameters.
|
|
32
|
+
|
|
33
|
+
## Checks
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pre-commit run --files <changed files> # markdownlint, TOML formatting
|
|
37
|
+
pip install -r docs/requirements.txt
|
|
38
|
+
sphinx-build -W --keep-going -b html docs docs/_build/html # no warnings allowed
|
|
39
|
+
pytest tests/examples # README and examples run against the API
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Docstrings render as reStructuredText: quote `|u|` and `**kwargs` as literals (` ``|u|`` `), and
|
|
43
|
+
end a sentence with `::` before an indented code block.
|
|
44
|
+
|
|
45
|
+
## Pull request
|
|
46
|
+
|
|
47
|
+
Branch `chg/docs-<slug>` (or `chg/<issue>-<slug>`), PR into `development`: what was wrong, what
|
|
48
|
+
changed, how to verify (the Sphinx build, the example tests). Escalate when a fix needs an API
|
|
49
|
+
change, a structural decision, or domain review (self-calibration theory, CASA behaviour).
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: estimate-effort
|
|
3
|
+
description: >-
|
|
4
|
+
Estimate the effort of a Wenuray GitHub issue and set its effort:N label (Fibonacci). Use when an
|
|
5
|
+
issue has no effort label, or when asked to size or prioritise issues.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Effort estimation
|
|
9
|
+
|
|
10
|
+
## Input
|
|
11
|
+
|
|
12
|
+
The whole issue: description, acceptance criteria, open questions, labels, milestone, linked
|
|
13
|
+
issues and PRs. Check the code it touches.
|
|
14
|
+
|
|
15
|
+
## Output
|
|
16
|
+
|
|
17
|
+
1. One `effort:N` label, N in 1, 2, 3, 5, 8, 13 (`gh issue edit N --add-label effort:3`; remove an
|
|
18
|
+
old one).
|
|
19
|
+
2. One comment: the effort, 2–4 sentences of rationale (modules touched, test surface, CASA or
|
|
20
|
+
Pyralysis involvement, risk), and the tests it needs.
|
|
21
|
+
|
|
22
|
+
## Rubric
|
|
23
|
+
|
|
24
|
+
| Effort | Typical scope |
|
|
25
|
+
| --- | --- |
|
|
26
|
+
| 1 | Docs, a one-line fix, a trivial test |
|
|
27
|
+
| 2 | A small change in one or two files, unit tests |
|
|
28
|
+
| 3 | A feature across a few modules, unit tests, maybe one integration test |
|
|
29
|
+
| 5 | Cross-module change, integration tests on simulated data, docs and recipes |
|
|
30
|
+
| 8 | A new backend or calibration type, the loop, or upstream coordination (CASA, Pyralysis) |
|
|
31
|
+
| 13 | Architecture: split it into issues of 8 or less instead |
|
|
32
|
+
|
|
33
|
+
Raise the estimate when the change needs a design decision (`design` label), touches the
|
|
34
|
+
self-calibration invariants, or depends on an unreleased upstream fix.
|