Skip to content
Merged
57 changes: 40 additions & 17 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@
- One class per file when substantial; group small related classes.
- No `**kwargs` — use explicit keyword arguments.
- No string-based dispatch (e.g. `getattr(self, f'_{name}')`); write
named methods (`_set_sample_form`, `_set_beam_mode`).
named methods (`_set_sample_form`, `_set_beam_mode`). Narrow framework
metadata lookups are allowed when the attribute name is a class-level
declaration, is not user input, and is validated in one central place;
for example, `CategoryItem._category_entry_name`.
- Public attrs are either editable (getter+setter property) or read-only
(getter only). For internal mutation of read-only props, use a private
`_set_<name>` method, not a public setter.
Expand Down Expand Up @@ -77,7 +80,7 @@
## Testing

- Every new module, class, or bug fix ships with tests. See
`docs/dev/architecture.md` §10 for the full strategy.
`docs/dev/adrs/accepted/test-strategy.md` for the full strategy.
- Unit tests mirror the source tree:
`src/easydiffraction/<pkg>/<mod>.py` →
`tests/unit/easydiffraction/<pkg>/test_<mod>.py`. Verify with
Expand All @@ -102,8 +105,11 @@

- Before any structural/design change (new categories, factories,
switchable-category wiring, datablocks, CIF serialisation), read
`docs/dev/architecture.md` and follow documented patterns. Localised
bug fixes or test updates need only this file.
`docs/dev/adrs/index.md` and the relevant accepted ADRs. Localised bug
fixes or test updates need only this file.
- Development documentation lives under `docs/dev/`. Use
`docs/dev/adrs/index.md` as the architecture and decision navigation
surface; there is no separate `architecture.md` source of truth.
- Project is in beta: no legacy shims, no deprecation warnings — update
tests and tutorials to the current API.
- Minimal diffs; don't reformat working code. Fix only what's asked;
Expand All @@ -112,7 +118,10 @@
resolves them.
- Never remove or replace existing functionality without explicit
confirmation — highlight every removal and wait for approval.
- When renaming, grep the entire project (code, tests, tutorials, docs).
- When renaming or auditing usages, search the entire project (code,
tests, tutorials, docs). Use `git grep -n` because all contributors
have Git; do not assume `rg` is installed. If `git grep` is
unavailable, fall back to `find ... -type f` plus `grep -n`.
- Each change is atomic and single-commit-sized: make one change,
suggest the commit message, then stop and wait for confirmation.
- When in doubt, ask.
Expand All @@ -137,34 +146,45 @@

Non-trivial changes use a two-phase workflow:

- **Phase 1 — Implementation.** Code, docs, and architecture updates
only. Do not create or run tests unless the user explicitly asks. When
done, present for review and iterate until approved.
- **Phase 1 — Implementation.** Code and docs updates only. Update ADRs
when the change affects architecture or documented decisions. Do not
create or run tests unless the user explicitly asks. When done,
present for review and iterate until approved.
- **Phase 2 — Verification.** Add/update tests, then run `pixi run fix`,
`pixi run check`, `pixi run unit-tests`, `pixi run integration-tests`,
`pixi run script-tests`.

Notes:

- `pixi run fix` regenerates `docs/dev/package-structure-*.md`
automatically — never edit those by hand. Don't review auto-fixes;
accept and move on. Then `pixi run check` until clean.
- `pixi run fix` regenerates `docs/dev/package-structure/full.md` and
`docs/dev/package-structure/short.md` automatically — never edit those
by hand. Don't review auto-fixes; accept and move on. Then
`pixi run check` until clean.
- Open issues / design questions / planned improvements live in
`docs/dev/issues_open.md` (priority-ordered). On resolution, move to
`docs/dev/issues_closed.md` and update `architecture.md` if affected.
`docs/dev/issues/open.md` (priority-ordered). On resolution, move to
`docs/dev/issues/closed.md` and update the relevant ADR or
`docs/dev/adrs/index.md` if affected.

### Planning

When asked to create a plan:

- Start the plan by referencing this file:
`.github/copilot-instructions.md`. State any deliberate exception to
these instructions in the plan itself.
- First gather enough repository context to make the plan concrete. Ask
all ambiguous or unclear questions in one concise batch; record
unresolved questions in the plan if the user wants it saved before
answering them.
- Save plans as `docs/dev/plan_<feature-name>.md` (lowercase,
dash-separated, e.g. `plan_background-refactor.md`). Use the same
`<feature-name>` for the implementation branch
(`feature/<feature-name>`). Do not push the branch unless asked.
- Save plans as `docs/dev/plans/<feature-name>.md` (lowercase,
dash-separated, e.g. `docs/dev/plans/background-refactor.md`). When a
plan implements one ADR, use the same slug as the ADR file; for
example, `docs/dev/adrs/suggestions/foo.md` maps to
`docs/dev/plans/foo.md`. If a plan has no corresponding ADR or spans
multiple ADRs, choose a concise feature slug and list all related ADRs
in the plan. Use the same `<feature-name>` for the implementation
branch (`feature/<feature-name>`). Do not push the branch unless
asked.
- Include a status checklist with `[ ]` items; mark `[x]` as completed
during implementation.
- Apply the two-phase workflow (Phase 1 implementation, Phase 2
Expand All @@ -184,6 +204,9 @@ When asked to create a plan:
files likely to change, decisions already made, open questions,
verification commands for Phase 2, and a short suggested commit
message or branch name when useful.
- Before saving a plan, verify that referenced files, directories,
scripts, and task names exist locally when that is practical. If a
referenced tool is optional or missing, include an available fallback.
- End every plan with a "Suggested Pull Request" section containing a
short PR title and a brief end-user-oriented description. Keep this
section non-technical enough for scientists and other users to
Expand Down
44 changes: 44 additions & 0 deletions docs/dev/adrs/accepted/category-parameter-access.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# ADR: Two-Level Category Parameter Access

## Status

Accepted.

## Date

2026-05-17

## Group

Core model.

## Context

EasyDiffraction models CIF concepts as datablocks, categories, category
collections, and parameters. Users need predictable navigation paths
from a structure or experiment to any parameter.

## Decision

Use at most two navigation levels from a datablock to a parameter:

```python
structure.cell.length_a = 3.88
experiment.instrument.setup_wavelength = 1.494
structure.atom_sites['Si'].adp_iso = 0.47
experiment.background['10'].y = 170
```

The general forms are:

- `DATABLOCK.CATEGORY_ITEM.PARAMETER`
- `DATABLOCK.CATEGORY_COLLECTION[ITEM_ID].PARAMETER`

Categories are flat siblings under their owner. A category must not own
another category of a different type.

## Consequences

API paths remain short, regular, and close to CIF category structure.
Nested category designs are rejected because they make parameter access
depth depend on the domain area and obscure the CIF-like model.
66 changes: 66 additions & 0 deletions docs/dev/adrs/accepted/development-docs-structure.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# ADR: Development Documentation Structure

## Status

Accepted.

## Date

2026-05-17

## Group

Documentation.

## Context

Development notes, ADRs, roadmap material, package-structure snapshots,
and issue backlogs were all kept directly under `docs/dev` or under
mixed-case directories. That made the development documentation harder
to scan and created several competing naming conventions.

The repository also contains `docs/docs`, which is the MkDocs source for
published user documentation. Development-only material should not be
mixed into that tree unless it is intentionally published.

## Decision

Keep development-only documentation under `docs/dev`.

Use this structure:

```text
docs/dev/
|-- index.md
|-- adrs/
| |-- index.md
| |-- accepted/
| `-- suggestions/
|-- issues/
| |-- open.md
| `-- closed.md
|-- package-structure/
| |-- full.md
| `-- short.md
|-- plans/
`-- roadmap/
`-- ROADMAP.md
```

Use lowercase directory names for new development-doc folders. Keep
`ROADMAP.md` uppercase because it may later be copied into published
user documentation where the conventional filename is useful.

Use `docs/dev/adrs/index.md` as the architecture and decision navigation
surface. Do not keep a separate architecture overview that duplicates
ADR content.

## Consequences

- Development documentation remains under the documentation tree without
being part of the published MkDocs source by default.
- ADRs have one entry point for architecture navigation and separate
accepted and proposed areas.
- Package-structure snapshots have stable, script-friendly paths.
- Roadmap publication can later be implemented as a build-time copy from
`docs/dev/roadmap/ROADMAP.md` into `docs/docs`.
File renamed without changes.
34 changes: 34 additions & 0 deletions docs/dev/adrs/accepted/enum-backed-closed-values.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# ADR: Enum-Backed Closed Value Sets

## Status

Accepted.

## Date

2026-05-17

## Group

Core model.

## Context

Many attributes accept a finite set of values: experiment axes, factory
tags, fit modes, calculators, minimizers, and rendering engines.
String-only dispatch is hard to grep and easy to mistype.

## Decision

Represent every finite closed set with a `(str, Enum)` class.

Use enum members as the internal source of truth for validation,
dispatch, default rules, and descriptions. User-facing setters may
accept either enum members or their string values, but internal code
compares against enum members.

## Consequences

Finite choices are discoverable, type-checkable, and greppable.
Validation can use enum membership instead of hand-written string
patterns.
44 changes: 44 additions & 0 deletions docs/dev/adrs/accepted/factory-contracts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# ADR: Factory Contracts and Metadata

## Status

Accepted.

## Date

2026-05-17

## Group

Factories.

## Context

Many domain categories have multiple implementations, and some currently
have only one implementation but may gain more later. Construction,
default selection, compatibility filtering, and supported-option display
need a common contract.

## Decision

Every category that can be constructed by framework code is created
through a factory, even when it currently has only one implementation.

Concrete factory-created classes carry metadata appropriate to their
role:

- `type_info` for stable tag lookup and user descriptions.
- `compatibility` for experiment-axis compatibility where relevant.
- `calculator_support` for calculation-engine support where relevant.

Child rows that only exist inside a collection do not carry factory
metadata; the collection carries the metadata.

Factory registration is triggered by explicit imports in package
`__init__.py` files.

## Consequences

Construction, default resolution, support tables, and compatibility
filtering stay uniform. Single-implementation categories are ready for
future alternatives without changing the public construction pattern.
48 changes: 48 additions & 0 deletions docs/dev/adrs/accepted/factory-tag-naming.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# ADR: Factory Tag Naming

## Status

Accepted.

## Date

2026-05-17

## Group

Naming.

## Context

Factory tags are persisted and used for public selection. Inconsistent
abbreviations or ordering would make saved files and user code harder to
read.

## Decision

Canonical factory tags are:

- lowercase
- hyphen-separated
- semantically ordered from general to specific
- unique within a factory

Use standard abbreviations:

| Concept | Abbreviation |
| ------------------- | ------------ |
| Powder | `pd` |
| Single crystal | `sc` |
| Constant wavelength | `cwl` |
| Time-of-flight | `tof` |
| Bragg scattering | `bragg` |
| Total scattering | `total` |

Context-local aliases are allowed when the owning object already
disambiguates the choice, for example `pseudo-voigt` inside a CWL
experiment.

## Consequences

Saved tags are stable and greppable, while user-facing APIs can remain
short where context makes the canonical prefix redundant.
39 changes: 39 additions & 0 deletions docs/dev/adrs/accepted/free-flag-cif-encoding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# ADR: Free-Flag CIF Encoding

## Status

Accepted.

## Date

2026-05-17

## Group

Persistence.

## Context

Fitting needs to persist whether a parameter is fixed or free. A
separate free-parameter list would duplicate state already attached to
individual parameter values and could become stale.

## Decision

Encode a parameter's free/fixed status in the CIF value uncertainty
syntax:

```text
3.89 fixed
3.89(2) free with estimated standard deviation
3.89() free without estimated standard deviation
```

Do not persist a separate list of free parameters as the source of
truth.

## Consequences

Each parameter carries its own fit-state information in the same place
as the value. CIF round-trips are simpler because there is no separate
state table to reconcile.
Loading
Loading