qprogram-qdac 0.1.0__tar.gz → 0.2.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.
Files changed (37) hide show
  1. qprogram_qdac-0.2.0/CHANGELOG.md +23 -0
  2. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/PKG-INFO +12 -46
  3. qprogram_qdac-0.2.0/README.md +83 -0
  4. qprogram_qdac-0.2.0/docs/developer/contributing.md +148 -0
  5. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/docs/developer/index.md +3 -12
  6. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/docs/developer/lowering.md +124 -227
  7. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/docs/getting-started.md +22 -64
  8. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/docs/guide/capabilities.md +33 -106
  9. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/docs/guide/index.md +2 -9
  10. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/docs/guide/operations.md +31 -93
  11. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/docs/guide/serialization.md +42 -106
  12. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/docs/index.md +7 -26
  13. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/docs/reference/api.md +6 -22
  14. qprogram_qdac-0.2.0/docs/reference/index.md +11 -0
  15. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/pyproject.toml +2 -2
  16. qprogram_qdac-0.2.0/tests/_header.py +43 -0
  17. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/tests/test_serialization.py +17 -11
  18. qprogram_qdac-0.1.0/CHANGELOG.md +0 -24
  19. qprogram_qdac-0.1.0/README.md +0 -117
  20. qprogram_qdac-0.1.0/docs/developer/contributing.md +0 -240
  21. qprogram_qdac-0.1.0/docs/reference/index.md +0 -21
  22. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/.gitignore +0 -0
  23. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/LICENSE +0 -0
  24. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/src/qprogram_qdac/__init__.py +0 -0
  25. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/src/qprogram_qdac/mixin.py +0 -0
  26. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/src/qprogram_qdac/namespace.py +0 -0
  27. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/src/qprogram_qdac/operations.py +0 -0
  28. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/src/qprogram_qdac/profiles.py +0 -0
  29. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/src/qprogram_qdac/py.typed +0 -0
  30. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/tests/conftest.py +0 -0
  31. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/tests/test_docstring_style.py +0 -0
  32. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/tests/test_mixin.py +0 -0
  33. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/tests/test_namespace.py +0 -0
  34. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/tests/test_operations.py +0 -0
  35. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/tests/test_profile.py +0 -0
  36. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/tests/test_registration.py +0 -0
  37. {qprogram_qdac-0.1.0 → qprogram_qdac-0.2.0}/zensical.toml +0 -0
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ Notable changes to QProgram QDAC, newest first. Each entry starts life as a news fragment under `changelog/`, and `towncrier build` assembles the fragments into a release section here. See [Contributing](https://qilimanjaro-tech.github.io/qprogram-qdac/developer/contributing.html) for how to add one.
4
+
5
+ <!-- towncrier release notes start -->
6
+
7
+ ## 0.2.0 (2026-09-08)
8
+
9
+ This release requires qprogram 0.2, which is what a `require qdac` line is now read against. One thing follows from the core release rather than from anything here: a `.qp` file written before it no longer loads, because its `#!QProgram 1.0` header reads as a later version than the `0.2` the core writes now, and nothing runs backwards. A file whose header is current is carried up instead, since a `require qdac` line naming an earlier major is migrated rather than refused.
10
+
11
+ ### Changed
12
+
13
+ - The `require qdac <major>.<minor>` line is read by the rule the core DSL applies to a file header, which needs qprogram 0.2. A line asking for more than the installed package provides is still refused, and the message now names what to install: `file requires qdac 1.0, newer than the installed qdac 0.2.0 — install qdac 1.0 or newer`. A line carrying a patch (`require qdac 0.1.0`) is refused rather than rounded down, since a patch release of this package changes code and never the wire form. A line naming an earlier major loads instead of being refused, so a release that changes an operation's wire form registers a rewrite for it with `qp.register_vendor_migration("qdac", "<major>.<minor>")` and the files users already have go on loading. Nothing in this package's own API changes. ([PR #4](https://github.com/qilimanjaro-tech/qprogram-qdac/pull/4))
14
+
15
+
16
+ ## 0.1.0 (2026-08-25)
17
+
18
+ ### Added
19
+
20
+ - First release. QProgram QDAC adds the `qdac` vendor namespace to the core DSL, covering the QDevil QDAC, a slow high-precision DAC used most often for flux biasing. Importing the package is what turns it on.
21
+ - Four operations covering DC offsets, the waveform engine, and the chassis trigger network.
22
+ - A capability profile describing what a QDAC channel can do, so a program is validated against the hardware before it is run.
23
+ - `.qp` serialization for everything the package adds, registered so that a file naming the `qdac` vendor resolves without the caller importing this package first.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: qprogram-qdac
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: QDevil QDAC vendor extensions for the QProgram pulse-level quantum programming DSL.
5
5
  Project-URL: Homepage, https://github.com/qilimanjaro-tech/qprogram-qdac
6
6
  Project-URL: Documentation, https://qilimanjaro-tech.github.io/qprogram-qdac/
@@ -27,26 +27,16 @@ Classifier: Topic :: Scientific/Engineering :: Physics
27
27
  Classifier: Topic :: Scientific/Engineering :: Quantum Computing
28
28
  Classifier: Typing :: Typed
29
29
  Requires-Python: >=3.11
30
- Requires-Dist: qprogram>=0.1.0
30
+ Requires-Dist: qprogram>=0.2.0
31
31
  Description-Content-Type: text/markdown
32
32
 
33
33
  # QProgram QDAC
34
34
 
35
- [![Tests](https://github.com/qilimanjaro-tech/qprogram-qdac/actions/workflows/tests.yml/badge.svg)](https://github.com/qilimanjaro-tech/qprogram-qdac/actions/workflows/tests.yml)
36
- [![Code Quality](https://github.com/qilimanjaro-tech/qprogram-qdac/actions/workflows/code_quality.yml/badge.svg)](https://github.com/qilimanjaro-tech/qprogram-qdac/actions/workflows/code_quality.yml)
37
- [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)](https://www.python.org/)
38
- [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)
35
+ [![Tests](https://github.com/qilimanjaro-tech/qprogram-qdac/actions/workflows/tests.yml/badge.svg)](https://github.com/qilimanjaro-tech/qprogram-qdac/actions/workflows/tests.yml) [![Code Quality](https://github.com/qilimanjaro-tech/qprogram-qdac/actions/workflows/code_quality.yml/badge.svg)](https://github.com/qilimanjaro-tech/qprogram-qdac/actions/workflows/code_quality.yml) [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)](https://www.python.org/) [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)
39
36
 
40
- QDevil QDAC extensions for
41
- [QProgram](https://github.com/qilimanjaro-tech/qprogram), the pulse-level
42
- quantum programming DSL. The QDAC is a slow high-precision DAC, used most
43
- often for flux biasing.
37
+ QDevil QDAC extensions for [QProgram](https://github.com/qilimanjaro-tech/qprogram), the pulse-level quantum programming DSL. The QDAC is a slow high-precision DAC, used most often for flux biasing.
44
38
 
45
- The core DSL knows nothing about any instrument. This package adds the
46
- `qdac` vendor namespace to it: four operations covering DC offsets, the
47
- waveform engine, and the chassis trigger network, a capability profile that
48
- says what a QDAC channel can do, and `.qp` serialization for all of it.
49
- Importing the package is what turns those on.
39
+ The core DSL knows nothing about any instrument. This package adds the `qdac` vendor namespace to it: four operations covering DC offsets, the waveform engine, and the chassis trigger network, a capability profile that says what a QDAC channel can do, and `.qp` serialization for all of it. Importing the package is what turns those on.
50
40
 
51
41
  ## Installation
52
42
 
@@ -90,43 +80,19 @@ data = result.get(m0)
90
80
  print(data.dims, data.shape) # ('bias', 'IQ') (41, 2)
91
81
  ```
92
82
 
93
- The flux bias comes from the QDAC, the drive and readout from whatever
94
- vendor owns those buses. One program describes both, and the nesting is
95
- what makes a mixed platform accept it: the bias sweep dispatches from the
96
- host, one upload per point, while the `average` block under it runs in the
97
- real-time sequencer. Flatten the two into one block and no domain runs
98
- both. `sync` names its buses because the argument-less form syncs every bus
99
- in the program, and a QDAC channel is not something a real-time barrier can
100
- wait on.
83
+ The flux bias comes from the QDAC, the drive and readout from whatever vendor owns those buses. One program describes both, and the nesting is what makes a mixed platform accept it: the bias sweep dispatches from the host, one upload per point, while the `average` block under it runs in the real-time sequencer. Flatten the two into one block and no domain runs both. `sync` names its buses because the argument-less form syncs every bus in the program, and a QDAC channel is not something a real-time barrier can wait on.
101
84
 
102
85
  ## What you get
103
86
 
104
- - **Four operations under one namespace.** `set_offset` holds a DC voltage
105
- on a channel, `play` uploads an envelope to the channel's waveform engine
106
- with explicit dwell, delay, repetitions and stepped mode, and
107
- `set_trigger` / `wait_trigger` wire the channel into the chassis trigger
108
- network so QDAC sequences line up with instruments driving other buses.
109
- - **Host-side sweeps, declared rather than guessed.** The QDAC has no FPGA,
110
- so a swept parameter has to be re-uploaded from the host between
111
- iterations. The `qdac-default-v1` profile says so through a domain
112
- constraint, and `qp.explain(program, capabilities)` prints the enclosing
113
- loop as `[host]` instead of failing at compile time.
114
- - **Single-channel by declaration.** The profile lists only single-channel
115
- waveform tokens. An IQ waveform on a QDAC bus is a `missing-capability`
116
- error from `qp.validate`, not a runtime surprise.
117
- - **Serialization included.** Every operation round-trips through the `.qp`
118
- text format, under a `require qdac 0.1` header line. A file that names
119
- `qdac` activates this package on load through its entry point, so the
120
- reader does not have to import it first.
121
- - **Typed access.** `QProgram` from this package is the core builder with a
122
- typed `.qdac` property. `QdacMixin` composes with other vendor mixins
123
- when a platform spans several instruments.
87
+ - **Four operations under one namespace.** `set_offset` holds a DC voltage on a channel, `play` uploads an envelope to the channel's waveform engine with explicit dwell, delay, repetitions and stepped mode, and `set_trigger` / `wait_trigger` wire the channel into the chassis trigger network so QDAC sequences line up with instruments driving other buses.
88
+ - **Host-side sweeps, declared rather than guessed.** The QDAC has no FPGA, so a swept parameter has to be re-uploaded from the host between iterations. The `qdac-default-v1` profile says so through a domain constraint, and `qp.explain(program, capabilities)` prints the enclosing loop as `[host]` instead of failing at compile time.
89
+ - **Single-channel by declaration.** The profile lists only single-channel waveform tokens. An IQ waveform on a QDAC bus is a `missing-capability` error from `qp.validate`, not a runtime surprise.
90
+ - **Serialization included.** Every operation round-trips through the `.qp` text format, under a `require qdac 0.2` header line. A file that names `qdac` activates this package on load through its entry point, so the reader does not have to import it first.
91
+ - **Typed access.** `QProgram` from this package is the core builder with a typed `.qdac` property. `QdacMixin` composes with other vendor mixins when a platform spans several instruments.
124
92
 
125
93
  ## Documentation
126
94
 
127
- Full documentation, including the operation reference, the capability
128
- profile, and the generated API reference, lives at
129
- <https://qilimanjaro-tech.github.io/qprogram-qdac/>.
95
+ Full documentation, including the operation reference, the capability profile, and the generated API reference, lives at <https://qilimanjaro-tech.github.io/qprogram-qdac/>.
130
96
 
131
97
  ## Development
132
98
 
@@ -0,0 +1,83 @@
1
+ # QProgram QDAC
2
+
3
+ [![Tests](https://github.com/qilimanjaro-tech/qprogram-qdac/actions/workflows/tests.yml/badge.svg)](https://github.com/qilimanjaro-tech/qprogram-qdac/actions/workflows/tests.yml) [![Code Quality](https://github.com/qilimanjaro-tech/qprogram-qdac/actions/workflows/code_quality.yml/badge.svg)](https://github.com/qilimanjaro-tech/qprogram-qdac/actions/workflows/code_quality.yml) [![Python](https://img.shields.io/badge/python-3.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue)](https://www.python.org/) [![License](https://img.shields.io/badge/license-Apache%202.0-blue)](LICENSE)
4
+
5
+ QDevil QDAC extensions for [QProgram](https://github.com/qilimanjaro-tech/qprogram), the pulse-level quantum programming DSL. The QDAC is a slow high-precision DAC, used most often for flux biasing.
6
+
7
+ The core DSL knows nothing about any instrument. This package adds the `qdac` vendor namespace to it: four operations covering DC offsets, the waveform engine, and the chassis trigger network, a capability profile that says what a QDAC channel can do, and `.qp` serialization for all of it. Importing the package is what turns those on.
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ pip install qprogram-qdac
13
+ ```
14
+
15
+ The only dependency is `qprogram` itself.
16
+
17
+ ## A first program
18
+
19
+ ```python
20
+ import qprogram as qp
21
+ from qprogram import BusSchema
22
+ from qprogram.waveforms import IQPair, Square
23
+ from qprogram_qdac import QProgram
24
+
25
+ schema = BusSchema.flux_tunable_transmon()
26
+ q = schema.q
27
+
28
+ program = QProgram(label="flux-spectroscopy", schema=schema)
29
+ bias = program.variable("bias", units="V")
30
+
31
+ with program.sweep(bias).from_range(-0.2, 0.2, 0.01):
32
+ program.qdac.set_offset(q[0].flux, bias)
33
+ with program.average(shots=100):
34
+ program.play(q[0].drive, "pi_pulse")
35
+ program.sync([q[0].drive, q[0].readout])
36
+ m0 = program.measure(q[0].readout, "readout", "weights")
37
+
38
+ resolved = program.with_waveforms(
39
+ {
40
+ "pi_pulse": IQPair(Square(0.5, 40), Square(0.0, 40)),
41
+ "readout": IQPair(Square(1.0, 2000), Square(0.0, 2000)),
42
+ "weights": IQPair(Square(1.0, 2000), Square(1.0, 2000)),
43
+ }
44
+ )
45
+
46
+ result = qp.simulate(resolved)
47
+ data = result.get(m0)
48
+ print(data.dims, data.shape) # ('bias', 'IQ') (41, 2)
49
+ ```
50
+
51
+ The flux bias comes from the QDAC, the drive and readout from whatever vendor owns those buses. One program describes both, and the nesting is what makes a mixed platform accept it: the bias sweep dispatches from the host, one upload per point, while the `average` block under it runs in the real-time sequencer. Flatten the two into one block and no domain runs both. `sync` names its buses because the argument-less form syncs every bus in the program, and a QDAC channel is not something a real-time barrier can wait on.
52
+
53
+ ## What you get
54
+
55
+ - **Four operations under one namespace.** `set_offset` holds a DC voltage on a channel, `play` uploads an envelope to the channel's waveform engine with explicit dwell, delay, repetitions and stepped mode, and `set_trigger` / `wait_trigger` wire the channel into the chassis trigger network so QDAC sequences line up with instruments driving other buses.
56
+ - **Host-side sweeps, declared rather than guessed.** The QDAC has no FPGA, so a swept parameter has to be re-uploaded from the host between iterations. The `qdac-default-v1` profile says so through a domain constraint, and `qp.explain(program, capabilities)` prints the enclosing loop as `[host]` instead of failing at compile time.
57
+ - **Single-channel by declaration.** The profile lists only single-channel waveform tokens. An IQ waveform on a QDAC bus is a `missing-capability` error from `qp.validate`, not a runtime surprise.
58
+ - **Serialization included.** Every operation round-trips through the `.qp` text format, under a `require qdac 0.2` header line. A file that names `qdac` activates this package on load through its entry point, so the reader does not have to import it first.
59
+ - **Typed access.** `QProgram` from this package is the core builder with a typed `.qdac` property. `QdacMixin` composes with other vendor mixins when a platform spans several instruments.
60
+
61
+ ## Documentation
62
+
63
+ Full documentation, including the operation reference, the capability profile, and the generated API reference, lives at <https://qilimanjaro-tech.github.io/qprogram-qdac/>.
64
+
65
+ ## Development
66
+
67
+ The project uses [uv](https://docs.astral.sh/uv/).
68
+
69
+ ```bash
70
+ git clone https://github.com/qilimanjaro-tech/qprogram-qdac
71
+ cd qprogram-qdac
72
+
73
+ uv sync --group dev # create .venv and install everything
74
+ uv run pytest # run the test suite
75
+ uv run ruff check . # lint
76
+ uv run ruff format . # format
77
+ uv run ty check # type-check
78
+ uv run --group docs zensical serve # preview the documentation
79
+ ```
80
+
81
+ ## License
82
+
83
+ Apache License 2.0 - see [LICENSE](LICENSE).
@@ -0,0 +1,148 @@
1
+ # Contributing
2
+
3
+ The short version: open a small pull request, run the linter and the tests, write the docs for anything that changes user-visible behavior.
4
+
5
+ ## Before you start
6
+
7
+ Skim two things first.
8
+
9
+ 1. **[Lowering onto hardware](lowering.md).** It explains the five modules, the registration hooks, and what a platform does with each operation. Almost every change touches one of those.
10
+ 2. **The core DSL's developer guide**, in particular [building a vendor extension](https://qilimanjaro-tech.github.io/qprogram/developer/vendor-extensions.html), the template this package follows, and [capability protocol internals](https://qilimanjaro-tech.github.io/qprogram/developer/capability-protocol.html), the normative account of tokens, slots and predicates. The AST, the validator, and the `.qp` format live there too. A change to this package that needs a core change is usually a core issue instead.
11
+
12
+ Then decide which repository the change belongs in. An operation only a QDAC can do belongs here. Anything a second backend would also want belongs in the core, so every backend gets it.
13
+
14
+
15
+ ## Development workflow
16
+
17
+ 1. **Install the package.**
18
+
19
+ ```bash
20
+ uv sync --group dev
21
+ ```
22
+
23
+ 2. **Install the docs environment** (optional, only if you change docs).
24
+
25
+ ```bash
26
+ uv sync --group docs
27
+ uv run --group docs zensical serve
28
+ ```
29
+
30
+ `mkdocstrings` imports the package to render the API reference, so the docs build needs the project installed, not just the docs tooling.
31
+
32
+ 3. **Make your change.**
33
+
34
+ 4. **Lint and format.**
35
+
36
+ ```bash
37
+ uv run ruff check .
38
+ uv run ruff format .
39
+ ```
40
+
41
+ 5. **Type-check.**
42
+
43
+ ```bash
44
+ uv run ty check
45
+ ```
46
+
47
+ 6. **Run the tests.**
48
+
49
+ ```bash
50
+ uv run pytest
51
+ uv run pytest --cov=qprogram_qdac # check coverage on touched files
52
+ ```
53
+
54
+ 7. **Update the docs.** Anything user-visible needs an entry in the relevant guide page and, for a new class or method, an entry in [`docs/reference/api.md`](../reference/api.md), whose `members:` lists are explicit.
55
+
56
+ 8. **Add a changelog entry.** Anything a caller would notice gets one news fragment under `changelog/`, named `<pr-number>.<type>.md`, where the type is `added`, `changed`, `fixed`, or `removed`.
57
+
58
+ ```bash
59
+ uv run towncrier create 123.added.md
60
+ ```
61
+
62
+ Write one or two sentences about what changed for somebody using the package. A fragment written before the pull request has a number takes a `+` prefix and any name, as in `+trigger-network-reset.added.md`; rename it once the number exists so the entry carries a link. Internal refactors, test-only changes, and docs corrections do not need one.
63
+
64
+ 9. **Open the pull request.** The workflows under `.github/workflows/` run on it. `tests.yml` runs the suite on Python 3.11 and 3.14 for a pull request, across 3.11 through 3.14 on a push to `main`, and uploads coverage from the 3.13 job. `code_quality.yml` runs `ruff check` and `ruff format --diff` once on 3.13, then `ty check` once per supported version. `docs.yml` builds this site, and deploys it on a push to `main`.
65
+
66
+ ## What "small PR" means
67
+
68
+ One concept per pull request. A new operation plus a new predicate plus a serialization fix is three pull requests. Each one is easier to review, easier to revert, and easier to bisect against.
69
+
70
+ ## The checklist for a new operation
71
+
72
+ All five code edits are listed in [Adding an operation](lowering.md#adding-an-operation). A complete pull request carries four kinds of change:
73
+
74
+ | Kind | What it means |
75
+ |-------------------|----------------------------------------------------------------------------------------------------------------------------------|
76
+ | **Code** | The node in `operations.py`, the method on `QdacNamespace`, the token in `profiles.py` twice, the registration in `__init__.py`. |
77
+ | **Tests** | One test per module in `tests/`: operations, namespace, serialization, registration, profile. |
78
+ | **Docs** | The operation's row and section in the user guide, its wire form in the serialization page, and anything the platform must know in [Lowering onto hardware](lowering.md). |
79
+ | **API reference** | The class and the namespace method added to the `members:` lists in `docs/reference/api.md`. |
80
+
81
+ Adding an operation is a minor version bump of the package, which is also the vendor protocol version in the `require qdac <major>.<minor>` header. Removing or renaming one, or changing what it means on the wire, is a major bump — and it needs a [migration](lowering.md#changing-one-that-already-exists) registered with `qp.register_vendor_migration("qdac", ...)` under the version that ships it, so the files users already have keep loading.
82
+
83
+ ## Style notes
84
+
85
+ These are the rules the project enforces; they are not exhaustive. All of them are configured in `pyproject.toml`.
86
+
87
+ - **Ruff with `preview = true` and `select = ["ALL"]`**, minus a curated ignore list written as rule *names* rather than codes, so the reason for each exemption reads off the config. Suppression comments use the same form: `# ruff: ignore[import-outside-top-level]`, never a numeric code. Line length is 120 and the formatter owns it. Expect the linter to push back on most external code.
88
+ - **Docstrings are enforced.** Preview mode means both the `D` and the `DOC` families run, under the Google convention. Every parameter gets a `name (type): Description.` entry, with the parenthesized type as house style even though the signature is annotated. A non-`None` return needs a `Returns:` section unless the summary line already opens with the word "Return", and every exception a caller can observe needs a `Raises:` entry. Constructor arguments are documented on the **class** docstring, since `merge_init_into_class` is on and `undocumented-public-init` is ignored. `Attributes:` is for a value object whose fields are the interface and are not all constructor arguments.
89
+ - **Cross-references are Markdown, not Sphinx roles.** Write `` [`SetOffset`][qprogram_qdac.SetOffset] `` for a target this site documents, and `` [`Expression`][qprogram.Expression] `` for one the core DSL documents: `zensical.toml` loads that project's published `objects.inv` as an `inventories` entry, so a core type resolves to its page on that site. Plain `` `Expression` `` is for anything neither site renders, such as a builtin or a stdlib name. A Sphinx role such as `` :class:`~qprogram.Expression` `` would reach the page as literal text, since mkdocstrings reads a docstring as Markdown and has no reStructuredText reader; `tests/test_docstring_style.py` fails the suite on one. The docs build runs with `--strict`, so a cross-reference neither site can resolve fails CI as well.
90
+ - **Every file carries the Apache header**, the standard 13-line notice with `Copyright 2026 Qilimanjaro Quantum Tech`, above the module docstring. Ruff's `missing-copyright-notice` rule fails the lint on a file without it. Tests included.
91
+ - **Type hints everywhere.** `ty` is in the dev group and checks `src`, and CI runs it once per supported Python version.
92
+ - **Function-style tests.** No test classes. Use fixtures from `tests/conftest.py` and parametrization. Tests are exempt from the `ANN`, `D`, and `DOC` families, so a test needs no docstring, but one explaining *why* a case exists is worth writing.
93
+ - **Two-space indentation in `.qp` fixtures.** That is what the writer emits.
94
+ - **No new runtime dependencies.** This package depends on `qprogram` and nothing else, and that is on purpose. A vendor SDK belongs in the platform library that drives the instrument, not in the package that describes it.
95
+
96
+ ## What goes where
97
+
98
+ | Change kind | Where it lands |
99
+ |------------------------------------|-----------------------------------------------------------------------|
100
+ | New operation | `src/qprogram_qdac/operations.py` plus the four other edits. |
101
+ | New method on the namespace | `src/qprogram_qdac/namespace.py`. |
102
+ | New capability token or predicate | `src/qprogram_qdac/profiles.py`. |
103
+ | New limit | `src/qprogram_qdac/profiles.py`, in `QDAC_DEFAULT_V1.limits`. |
104
+ | New profile bundle | `src/qprogram_qdac/profiles.py`, registered from `__init__.py`. |
105
+ | Registration or the typed program | `src/qprogram_qdac/__init__.py`. |
106
+ | Docs | `docs/`; the nav lives in `zensical.toml`. |
107
+ | Anything about the AST or `.qp` | The core DSL, not here. |
108
+
109
+ ## Releasing
110
+
111
+ `CHANGELOG.md` is assembled from the fragments in `changelog/`, so it is written once per release rather than edited per PR. A release goes out from its own pull request:
112
+
113
+ 1. Branch from an up-to-date `main`.
114
+ 2. Set the new version. This writes both `pyproject.toml` and `uv.lock`; nothing else holds the literal, since the version is read from the installed metadata.
115
+
116
+ ```bash
117
+ uv version 0.2.0
118
+ uv sync
119
+ ```
120
+
121
+ 3. Assemble the changelog. Pass the version explicitly. Left to guess, towncrier reads the *installed* metadata and can render a stale number into a heading that is never regenerated.
122
+
123
+ ```bash
124
+ uv run towncrier build --draft --version "$(uv version --short)" # preview
125
+ uv run towncrier build --version "$(uv version --short)" --yes
126
+ ```
127
+
128
+ 4. Read the rendered section and edit it. Fragments are written weeks apart by different people and rarely read as one voice when they land together.
129
+ 5. Open the release PR, and merge it once CI is green.
130
+ 6. Create the GitHub Release on the merge commit, tagged with the version now in `pyproject.toml` and no `v` prefix. Publishing it starts `publish.yml`, which runs `uv build` for the wheel and the sdist, checks both with `twine check`, and uploads them through trusted publishing. Pre-releases publish too.
131
+ 7. Approve the deployment. The run waits on the `pypi` environment until a reviewer releases it. PyPI never lets a file be replaced, so this approval is the last point at which a wrong version can be stopped.
132
+
133
+ `publish.yml` can also be started by hand from the Actions tab, which is how the first release goes out and how a run that failed on a transient error is retried. A manual run takes three inputs: `platform` chooses between PyPI and the `qilimanjaro` AWS CodeArtifact domain, `repository` names the CodeArtifact repository, and `dry_run` builds and validates the distributions without uploading them.
134
+
135
+ ## Commit messages
136
+
137
+ Short, imperative, explanatory. The body matters more than the title: explain *why*, not *what*. The history in `git log` is a good template.
138
+
139
+ ## License and attribution
140
+
141
+ `qprogram-qdac` is licensed under the Apache License, Version 2.0. The full text is in the `LICENSE` file at the repository root, and every source file carries the matching 13-line header. By opening a pull request you agree to license your contribution under the same terms.
142
+
143
+ ## Where to ask
144
+
145
+ - **Bug reports and feature requests.** Open a GitHub issue; the templates are short on purpose. Include a minimal program and, for anything about the file format, its `.qp` text. A problem that reproduces without importing `qprogram_qdac` belongs in the core repository.
146
+ - **Design discussion.** Open an issue naming the affected code, the behavior you expect, and the guide page that documents it.
147
+
148
+ Pull requests with a clear scope and tests are the fastest path to a merge.
@@ -1,21 +1,12 @@
1
1
  # Developer guide
2
2
 
3
- These pages document the internals: what you need to drive real hardware from
4
- a QDAC program, and what you need to change this package.
3
+ These pages document the internals: what you need to drive real hardware from a QDAC program, and what you need to change this package.
5
4
 
6
5
  | Topic | What you find there |
7
6
  |---------------------------------------|----------------------------------------------------------------------------------------------------------------|
8
7
  | [Lowering onto hardware](lowering.md) | The module layout, the registration hooks, what a platform does with each operation, and how to add a new one. |
9
8
  | [Contributing](contributing.md) | The uv workflow, the lint and test gate, style rules, and the checklist for a change. |
10
9
 
11
- The [User guide](../guide/index.md) covers the same operations from the caller's
12
- side, and the [API reference](../reference/api.md) is generated from the
13
- docstrings under `src/`, so the code and the reference move together.
10
+ The [User guide](../guide/index.md) covers the same operations from the caller's side, and the [API reference](../reference/api.md) is generated from the docstrings under `src/`, so the code and the reference move together.
14
11
 
15
- This package is only the QDAC half. The AST, the `.qp` format, the capability
16
- protocol, and the reference executor all live in the core DSL. Two of its
17
- developer pages are the background for everything here:
18
- [building a vendor extension](https://qilimanjaro-tech.github.io/qprogram/developer/vendor-extensions.html)
19
- is the template this package follows, and
20
- [capability protocol internals](https://qilimanjaro-tech.github.io/qprogram/developer/capability-protocol.html)
21
- is the normative account of tokens, slots and predicates.
12
+ This package is only the QDAC half. The AST, the `.qp` format, the capability protocol, and the reference executor all live in the core DSL. Two of its developer pages are the background for everything here: [building a vendor extension](https://qilimanjaro-tech.github.io/qprogram/developer/vendor-extensions.html) is the template this package follows, and [capability protocol internals](https://qilimanjaro-tech.github.io/qprogram/developer/capability-protocol.html) is the normative account of tokens, slots and predicates.