Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
109 commits
Select commit Hold shift + click to select a range
447c7f7
Add Orca multi-agent orchestration for plan execution and expand the par
marcosfrenkel Sep 23, 2026
46e34cd
0.1: split ParameterGroup out of ParameterManager
marcosfrenkel Sep 23, 2026
c753ae9
0.1: orchestration record
marcosfrenkel Sep 23, 2026
dcac611
Update orca orchestration tooling: switch qwen roles to qwen3.8-27b, add
marcosfrenkel Sep 23, 2026
71aa9af
0.0: per-run test ports via session-scoped server_port fixture
marcosfrenkel Sep 23, 2026
a1bce5e
0.0: orchestration record
marcosfrenkel Sep 23, 2026
8d04b42
0.2: add Broadcaster mixin and mix it into ParameterManager
marcosfrenkel Sep 23, 2026
693d4e7
0.2: fix from review round 1: pin duplicate-sink delivery semantics i…
marcosfrenkel Sep 23, 2026
779373d
0.2: orchestration record
marcosfrenkel Sep 23, 2026
0fbbddf
Orchestration: widen read-only and coder edit permissions, no bytecod…
marcosfrenkel Sep 24, 2026
04c4cbc
0.3: server registers itself as a broadcast sink on Broadcaster instr…
marcosfrenkel Sep 24, 2026
5167241
0.3: fix from review round 1: test the config-load sink registration …
marcosfrenkel Sep 24, 2026
88eeda0
0.3: orchestration record
marcosfrenkel Sep 24, 2026
56ece34
0.4: fix latent KeyError in parameter-creation broadcast and pass bro…
marcosfrenkel Sep 24, 2026
884558a
0.4: orchestration record
marcosfrenkel Sep 24, 2026
b3e6586
0.5: broadcast action constants in blueprints.py, used at every liter…
marcosfrenkel Sep 24, 2026
eec0c25
0.5: fix from review round 1: pin the broadcast action constants' wir…
marcosfrenkel Sep 24, 2026
7ae8e63
0.5: orchestration record
marcosfrenkel Sep 24, 2026
cb49c53
Orchestration: run report for 0.3..0.5
marcosfrenkel Sep 24, 2026
d07a79f
Orchestration: historian role, history for Phase 0, stop tracking raw…
marcosfrenkel Sep 24, 2026
eeef4af
Orchestration: replace deepseek reviewers with glm-5.3-flash reviewers
marcosfrenkel Sep 24, 2026
0f58c83
1.1: add ManagedParameter with pull-based Lock support and PMLockBlue…
marcosfrenkel Sep 24, 2026
9c11376
1.1: fix from review round 1: dotted full paths in locked-set error, …
marcosfrenkel Sep 24, 2026
229dc02
1.1: history
marcosfrenkel Sep 24, 2026
be90053
1.2: Lock API on ParameterManager with validate-then-mutate checks an…
marcosfrenkel Sep 24, 2026
c636f5c
1.2: fix from review round 1: unlock/relock on an already-set Lock ar…
marcosfrenkel Sep 24, 2026
8f7fb11
1.2: fix from review round 2: name every missing path in lock, unknow…
marcosfrenkel Sep 24, 2026
3481f56
1.2: fix from review round 3: Parameter Groups route add/remove to th…
marcosfrenkel Sep 24, 2026
d1937c8
1.2: history
marcosfrenkel Sep 24, 2026
d5f0c63
1.3: pm-lock-update broadcasts from the Lock API and proxy round-trip…
marcosfrenkel Sep 24, 2026
d1a4332
1.3: fix from review round 1: ruff import order, snapshot Lock broadc…
marcosfrenkel Sep 24, 2026
7ff0d71
1.3: fix from review round 2: restore the failed remove_lock no-emiss…
marcosfrenkel Sep 24, 2026
a4cc6e4
1.3: history
marcosfrenkel Sep 24, 2026
83afee7
2.1: Type registry and definitions with PMTypeBluePrint, effective-se…
marcosfrenkel Sep 24, 2026
3c275d2
2.1: fix from review round 1: root-only registry test, nesting-Type r…
marcosfrenkel Sep 24, 2026
9e73866
2.1: history
marcosfrenkel Sep 24, 2026
6d1560b
2.2: Instance matching — instances_of(type) and types_of(path) per D1…
marcosfrenkel Sep 24, 2026
ef1f2cf
2.2: fix from review round 1: test for a Type claiming through two In…
marcosfrenkel Sep 24, 2026
f6f53b1
2.2: history
marcosfrenkel Sep 24, 2026
6724148
2.3: Type edits with Instance side effects
marcosfrenkel Sep 24, 2026
bd4b457
2.3: fix from review round 1: refuse conflicting creation targets up …
marcosfrenkel Sep 24, 2026
bcaaa77
2.3: fix from review round 2: name every nester collision in add_type…
marcosfrenkel Sep 25, 2026
3969fd1
2.3: history
marcosfrenkel Sep 25, 2026
23b42e2
2.4: add_instance with up-front unit-conflict scan, dotted names and …
marcosfrenkel Sep 25, 2026
97fb5a4
2.4: fix from review round 1: pin the Globals refusal names on empty …
marcosfrenkel Sep 25, 2026
b212445
2.4: history
marcosfrenkel Sep 25, 2026
9de2239
2.5: pm-type-update and side-effect parameter-creation broadcasts fro…
marcosfrenkel Sep 25, 2026
80635c3
2.5: fix from review round 1: three-tier pm-type-update chain test an…
marcosfrenkel Sep 25, 2026
d8960ac
2.5: history
marcosfrenkel Sep 25, 2026
8daf195
3.1: _globals rules — add_parameter refusal, matching exclusion re-as…
marcosfrenkel Sep 25, 2026
175ff5a
3.1: fix from review round 1: delegate the blocked-target walk to _ch…
marcosfrenkel Sep 25, 2026
402e53d
3.1: fix from review round 2: reword the _ensure_global_target commen…
marcosfrenkel Sep 25, 2026
823facc
3.1: history
marcosfrenkel Sep 25, 2026
682ab21
3.2: lock_type_parameter / unlock_type_parameter with Type Lock appli…
marcosfrenkel Sep 25, 2026
efbafc6
3.2: fix from review round 1: never raise from the Type Lock applicat…
marcosfrenkel Sep 25, 2026
ba479ce
3.2: history
marcosfrenkel Sep 25, 2026
6b033a3
3.3: remove_parameter clears the Type Locks whose Target it deletes
marcosfrenkel Sep 25, 2026
fc60b13
3.3: fix from review round 1: pin the stale-Target skip and the per-T…
marcosfrenkel Sep 25, 2026
36aa69e
3.3: history
marcosfrenkel Sep 25, 2026
8c5db87
4.1: write and shim-read the version-2 Parameter Manager profile docu…
marcosfrenkel Sep 25, 2026
d36053c
4.1: fix from review round 1: pin includeMeta, the parameters.json le…
marcosfrenkel Sep 25, 2026
c384a70
4.1: history
marcosfrenkel Sep 25, 2026
b6ee025
4.2: read the version-2 profile document: up-front validation, D20 lo…
marcosfrenkel Sep 25, 2026
f99f580
4.2: fix from review round 1: pin complete-Instance loads, invalid Ty…
marcosfrenkel Sep 25, 2026
83e9a12
4.2: history
marcosfrenkel Sep 25, 2026
925967c
4.3: switch_to_profile clears Types and Locks via _clear_all (save, c…
marcosfrenkel Sep 25, 2026
dc15389
4.3: history
marcosfrenkel Sep 25, 2026
9928d03
5.1: client-side PMState and pm-lock-update/pm-type-update routing in…
marcosfrenkel Sep 25, 2026
d5c5ded
5.1: fix from review round 1: pin loadProfile refresh and the _setMet…
marcosfrenkel Sep 25, 2026
fc555c5
5.1: history
marcosfrenkel Sep 25, 2026
6052b32
5.2: tabs, tints and gutter bands for the Parameter Manager GUI
marcosfrenkel Sep 25, 2026
cd39235
5.2: fix from review round 1: deletion-Broadcast tint test, all-colum…
marcosfrenkel Sep 28, 2026
48c8d7f
5.2: history
marcosfrenkel Sep 28, 2026
c215c5c
5.3: Lock column, toggle, context menu, arm strip
marcosfrenkel Sep 28, 2026
5e83ead
5.3: fix from review round 1: filter re-apply, filtered-completer Ret…
marcosfrenkel Sep 28, 2026
113e482
5.3: history
marcosfrenkel Sep 28, 2026
74d2d9f
5.4: Locks panel — splitter, row model, per-row controls, lock-all/re…
marcosfrenkel Sep 28, 2026
ad3afba
5.4: fix from review round 1: panel refresh, skipped-Lock note, store…
marcosfrenkel Sep 28, 2026
c677376
5.4: history
marcosfrenkel Sep 28, 2026
7c92542
5.5: Types tab with three panes, and the parameter-creation branch fix
marcosfrenkel Sep 28, 2026
edceebc
5.5: fix from review round 1: type-list count assertions, re-target f…
marcosfrenkel Sep 28, 2026
6d12b73
5.5: history
marcosfrenkel Sep 28, 2026
f4923e3
5.6: delete-Target confirmation, Lock shortcuts and Types-tab polish
marcosfrenkel Sep 28, 2026
e32078a
5.6: fix from review round 1: followers_of fallback test, unlocked-Fo…
marcosfrenkel Sep 28, 2026
32b5a1f
5.6: history
marcosfrenkel Sep 28, 2026
da417a9
6.1: User Guide Parameter Manager page with verification script and a…
marcosfrenkel Sep 28, 2026
d1dcd42
6.1: fix from review round 1: Globals access mechanism, Server-window…
marcosfrenkel Sep 29, 2026
b8a99d2
6.1: fix from review round 2: restore the survivor assertions after t…
marcosfrenkel Sep 29, 2026
dccb468
6.1: history
marcosfrenkel Sep 29, 2026
44141d8
6.2: Technical Guide Broadcasts page with verification script and aud…
marcosfrenkel Sep 29, 2026
da12f46
6.2: fix from review round 1: pin PM payloads field by field, cover e…
marcosfrenkel Sep 29, 2026
a6e866c
6.2: history
marcosfrenkel Sep 29, 2026
966f961
6.3: bookkeeping: docs-plan blocks done, audit rows, glossary and ADR…
marcosfrenkel Sep 29, 2026
d59ad5f
6.3: fix from review round 1: Globals profile-load creation route, tw…
marcosfrenkel Sep 29, 2026
7b7c148
6.3: history
marcosfrenkel Sep 29, 2026
8276361
Move the Parameter Manager GUI out of gui/instruments.py
marcosfrenkel Sep 29, 2026
b1834d0
Add column and editor hooks to the generic parameter GUI
marcosfrenkel Sep 29, 2026
50a074e
Simplify the Parameter Manager model's row tracking and tidy leftovers
marcosfrenkel Sep 29, 2026
7ba829c
Split ParameterManagerGui into Locks and Types controllers
marcosfrenkel Sep 29, 2026
fd7f97e
Keep the Parameter Manager tree tall and the add strip at the bottom
marcosfrenkel Sep 29, 2026
0657e44
Stop the open Locks panel from asking the Server in a loop
marcosfrenkel Sep 29, 2026
f5670f5
Forget a parameter editor when Qt deletes it
marcosfrenkel Sep 29, 2026
b5d1094
Let the Locks panel's columns be resized
marcosfrenkel Sep 29, 2026
c6f9d23
Keep Qt out of the GUI log handler so the app exits cleanly
marcosfrenkel Sep 29, 2026
435003c
Give the Type tints a dark-theme palette
marcosfrenkel Oct 2, 2026
c3ee72b
Stop a dead log handler from skipping the next handler
marcosfrenkel Oct 2, 2026
39c4508
Merge branch 'toolsforexperiments:master' into marcosfrenkel/new-para…
marcosfrenkel Oct 2, 2026
5945e91
Fix the ruff and mypy failures in CI
marcosfrenkel Oct 2, 2026
0e2c5f0
Remove agent workflow files from PR
marcosfrenkel Oct 14, 2026
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
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,10 @@ BaseClient → ZMQ Network → ThreadPool + Broadcast

## Testing

Tests never use a fixed port. Use the `server_port` fixture; agents run the suite in parallel.

### Test Setup (test/pytest/conftest.py)
- `server_port` fixture - Session-scoped pair of free consecutive ports (server port and broadcast port)
- `start_server` fixture - Module-scoped server for all tests
- `cli` fixture - New Client per test
- `dummy_instrument` fixture - Test dummy instrument with submodules
Expand Down
55 changes: 52 additions & 3 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@ A serializable description of an instrument, parameter, or method that lets clie
_Avoid_: schema, spec

**Broadcast**:
A parameter-change event published by the Server on its PUB socket for any subscriber.
A parameter-change event published by the Server on its PUB socket for any subscriber. Most Broadcasts are produced by the Server when it executes a client request; an instrument that implements the **Broadcaster** contract can also emit its own.
_Avoid_: notification, event stream

**Broadcaster**:
The opt-in contract by which an instrument emits its own Broadcasts: it exposes `add_broadcast_sink` / `remove_broadcast_sink` / `broadcast`, and the Server registers itself as a sink when the instrument joins the Station. Instruments without it are untouched. The Parameter Manager is the first Broadcaster. It emits `pm-lock-update` (payload: a `PMLockBluePrint`, or `None` when its Lock was removed) and `pm-type-update` (payload: a `PMTypeBluePrint`, or `None` when the Type was removed), and re-emits the Server's own `parameter-creation` for the parameters it creates as side effects of Type edits, `add_instance` and Type Lock declarations; it emits no `parameter-deletion`.
_Avoid_: hook, callback, event emitter

**Virtual Instrument**:
An instrument that lives entirely in the Server with no hardware behind it.
_Avoid_: soft instrument, fake instrument (that's a dummy instrument, for testing)

**Parameter Manager**:
The flagship Virtual Instrument: a hierarchical, persistent, profile-aware store of experiment parameters.
The flagship Virtual Instrument: a hierarchical, persistent, profile-aware store of experiment parameters. Its root owns the Type registry and all Lock and Type methods; its submodules are **Parameter Groups**.
_Avoid_: param store, PM

**Client Station**:
Expand All @@ -49,18 +53,61 @@ A hardware-free test instrument shipped in `instrumentserver.testing` for develo
A Server acting as a Client of another Server, so instruments can be re-exported downstream.
_Avoid_: server-in-server, daisy-chaining (fine in prose, not as the term)

**Parameter Group**:
A submodule inside the Parameter Manager: a plain container of parameters and nested groups with no file, profile, Type or Lock logic of its own. Every submodule is a Parameter Group; only the root is the Parameter Manager, which extends the group with those responsibilities.
_Avoid_: nested manager, sub-manager

**Type**:
A named set of relative parameter paths (each with a default value and unit), plus **Nested Types** required at named submodules. Type entries carry no value kind and no description. A Type is structural: it describes a shape, not a list of members.
_Avoid_: template, schema, class. Not to be confused with a parameter's **value kind** (numeric, string, bool…), which is the `ParameterTypes` enum in code.

**Nested Type**:
A Type required at a named submodule of another Type, e.g. `qubit` requires a `readout` at its submodule `readout` (`add_nested_type("qubit", "readout", "readout")`). The outer Type's effective parameter set is its own entries plus every Nested Type's set under that submodule name. Nesting can go several levels deep but may not cycle.
_Avoid_: include, import, inherit, extend, subtype

**Instance**:
A Parameter Manager submodule (at any depth, never the root and never under Globals) that carries every parameter path of a Type, including those of its Nested Types, each with the unit the Type declares. Values are irrelevant to matching. Membership is duck-typed and recomputed on demand, never stored. An empty Type has no Instances. A submodule may be an Instance of several Types at once.
_Avoid_: member, tagged submodule

**Claiming Type**:
The Type whose tint a parameter row shows when several Types cover it: the innermost (most deeply nested) Type wins, then the largest.

**Lock**:
A rule attached to one parameter (the **Follower**) naming another parameter, its **Target**. While the Lock is **locked**, the Follower answers `get` with the Target's value and refuses `set`. While **unlocked**, the Follower behaves as a plain parameter but remembers its Target so it can be locked again. Removing the Lock forgets the Target. Locks chain (a Target may itself have a Lock) but never cycle. Values are pulled on `get`; nothing is ever pushed into a Follower.
_Avoid_: link, binding, mirror, source. Not the server's **instrument mutex**.

**Target**:
The parameter a Lock points at. A parameter that is the Target of at least one Lock is marked as such in the tree, its Followers counted locked and unlocked alike. Deleting a Target removes the Locks that pointed at it and clears the Type Lock rules whose stored Target it was; their Followers become plain parameters.
_Avoid_: source

**Follower**:
A parameter that has a Lock. Convenience noun for prose; the code says "the parameter's lock".

**Type Lock**:
A rule on a Type entry naming a Target. Declaring it puts an ordinary, locked Lock on that parameter in every current Instance, and every future Instance gets it at creation. Its default Target is `_globals.<type>.<path>`, created on demand with the entry's default and unit. Instance parameters that already have a Lock on another Target are skipped with a warning. Removing the Type Lock removes only the rule; the Locks it created stay until removed individually. Instances that stop matching keep their Locks.
_Avoid_: group lock, rule (alone)

**Globals**:
The reserved `_globals` submodule of the Parameter Manager that holds the default Targets of Type Locks. Its parameters are created on demand by a Type Lock or by a profile load that lists one, never through `add_parameter`, which refuses the name; otherwise a Globals parameter is ordinary: it can be read and set, and it is saved with the profile. Globals is never an Instance of anything.

**Instrument mutex**:
The server's per-instrument `threading.RLock` that serialises concurrent `call`s to one instrument. Prose uses "instrument mutex" so it never collides with **Lock**; the code keeps its current names (`_instrument_locks`) with a rename note only.
_Avoid_: instrument lock (in prose)

## Relationships

- The **Server** owns instruments; **Clients** reach them only through **Proxy Instruments** built from **Blueprints**
- Every parameter change on the **Server** produces a **Broadcast**; **Listeners** and GUIs consume Broadcasts
- A **Client Station** groups Proxy Instruments for one client; many Client Stations can share one Server
- A **Virtual Instrument** is served like any other instrument; the **Parameter Manager** is one
- **Chained Servers** compose: a downstream Server proxies an upstream Server's instruments
- A **Type** is a shape; an **Instance** is any submodule matching it. Adding a parameter to a Type writes it into every Instance; removing one from a Type leaves it on the Instances, which then show it as an untyped row
- A **Lock** makes a **Follower** read its **Target**'s value while locked; a **Type Lock** does this for every Instance of a Type, defaulting its Target to **Globals**

## Example dialogue

> **Dev:** "If I set a value on a **Proxy Instrument**, who finds out?"
> **Domain expert:** "The **Server** executes the set under that instrument's lock, then emits a **Broadcast** — every subscribed GUI and **Listener** sees it, no polling needed."
> **Domain expert:** "The **Server** executes the set under that instrument's **instrument mutex**, then emits a **Broadcast** — every subscribed GUI and **Listener** sees it, no polling needed."
> **Dev:** "And a **Client Station** is a second server?"
> **Domain expert:** "No — it never owns instruments. It's a scoped view: one client's chosen set of **Proxy Instruments** over the same shared **Server**. If you actually need a second server re-exporting instruments, that's **Chained Servers**."

Expand All @@ -69,3 +116,5 @@ _Avoid_: server-in-server, daisy-chaining (fine in prose, not as the term)
- "apps" — the five console entry points are launchers, not five separate applications; they collapse into three features (Server, Client Station, Monitoring) plus two convenience launchers (Detached GUI, Parameter Manager GUI). Resolved: docs pages follow features, not entry points.
- "sub-server" — used colloquially for **Client Station**; resolved: explanation, not terminology.
- "virtual instrument" vs "dummy instrument" — distinct: virtual = production feature with no hardware; dummy = testing stand-in for hardware.
- "type" — overloaded: the `ParameterTypes` enum is a parameter's **value kind** (drives widget choice); the Parameter Manager's **Type** is a structural shape of a submodule. Resolved: **Type** is the structural concept; the enum is called "value kind" in prose.
- "lock" — overloaded: the server's per-instrument RLock vs the Parameter Manager's user-facing **Lock**. Resolved: the RLock is the **instrument mutex** in prose; code names unchanged.
20 changes: 20 additions & 0 deletions docs/adr/0001-duck-typed-parameter-manager-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
status: accepted
date: 2026-09-16
---

# Parameter Manager Types are structural (duck-typed), never a stored membership

A Type in the Parameter Manager is a named shape: a set of relative parameter paths with units, plus Nested Types at named submodules. A submodule is an Instance of a Type purely because it carries every path of that shape with the declared unit; nothing records "q01 is a qubit". Membership is recomputed on demand and never persisted.

## Considered options

- **Explicit registry**: tag submodules with a Type and enforce structure on tagged ones. Rejected: it adds membership state to persist and migrate, and it makes "parameter removed from the Type but still on the Instance" a special case instead of the natural outcome (the row is simply untyped now).
- **Structural, computed** (chosen): matches how the lab thinks about it ("everything shaped like a qubit is a qubit"), needs no membership state, and lets one submodule match several Types (innermost, then largest, claims the tint).

## Consequences

- Deleting a required parameter silently makes a submodule stop matching; that is accepted. Deleting a parameter that is a Lock Target is the case that gets a GUI confirmation, not this one.
- An empty Type matches nothing, so a freshly created Type does not claim the tree.
- Matching walks the tree on every query. Trees are hundreds of parameters; no caching in the first version.
- The `_globals` submodule is excluded from matching at any depth.
21 changes: 21 additions & 0 deletions docs/adr/0002-pull-based-locks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
status: accepted
date: 2026-09-16
---

# Parameter Locks pull on `get`; nothing is ever pushed into a Follower

A locked Follower answers `get` by asking its Target and returning that value; `set` on a locked Follower raises. The Target knows nothing about its Followers, no value is ever copied into a Follower, and "who follows X" is computed by scanning when asked. The behaviour lives in a `Parameter` subclass (`ManagedParameter`) so the server's call path, the instrument mutex and the existing broadcasts are untouched: the server resolves the dotted path and calls the parameter exactly as it does today.

## Considered options

- **Push on set**: a Target's `set` rewrites every Follower and each rewrite needs its own broadcast. Rejected: it fans one request into many writes, requires the instrument to report side effects to the server for every value change, and forces a dedup rule so Listeners do not log twice.
- **Pull on get** (chosen): minimal change, the broadcast path is unchanged for values, cycle checking is just a walk up the Target chain, and each hop of a chain is decided by that hop's own locked/unlocked state at read time.

## Consequences

- A Follower keeps its own underlying value while locked. Unlocking exposes that own value again (deliberate: "unlock and see its own value" is the point). Profiles store the own value plus the Lock.
- QCoDeS reads the cache, not `get`, for snapshots with `update=False`. `ManagedParameter` therefore reports the Target's value in its snapshot while locked, and the profile writer reads the own value explicitly. Otherwise measurement metadata would record stale values.
- GUIs repaint Followers when they receive a `parameter-update` for the Target; the Parameter Manager emits nothing for values.
- Deleting a Target removes the Locks pointing at it; their Followers become plain parameters. When the deleted Target was the stored Target of Type Locks, those rules are cleared with it.
- Targets are restricted to parameters inside the same Parameter Manager.
23 changes: 23 additions & 0 deletions docs/adr/0003-broadcaster-contract.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
status: accepted
date: 2026-09-16
---

# Instruments emit their own Broadcasts through an opt-in `Broadcaster` contract

The Server only broadcasts what it can see: parameter sets it executed, and calls whose method name is literally `add_parameter` or `remove_parameter`. Structural changes inside an instrument (a Type edited, a Lock declared, parameters created as a side effect of `add_instance`) were invisible to other clients. We add a small mixin, `Broadcaster`, with `add_broadcast_sink`, `remove_broadcast_sink` and `broadcast(blueprint)`. When an instrument joins the Station (creation over the wire, or loading from config at startup) the Server checks `hasattr(instrument, "add_broadcast_sink")` and registers its own broadcast function as a sink. Instruments without the mixin are registered exactly as before.

## Considered options

- **Hard-code more method names in the Server**, as `add_parameter` is today. Rejected: the Server accumulates one instrument's API.
- **Instrument declares a method-name to action mapping** that the Server consults after each call. Rejected: passive and coarse; cannot express "one message per created parameter" and still couples the Server to method names.
- **Server diffs state after each call.** Rejected: heavy, cannot express Type changes.
- **GUI polling.** Rejected: latency and traffic for nothing.
- **Opt-in emitter contract** (chosen): generic, three lines in the Server, no base-class change, usable by any future Virtual Instrument, and standalone use of the instrument (no sinks) is a no-op.

## Consequences

- Messages use the existing `ParameterBroadcastBluePrint`, same socket, same topic (instrument name), same wire format. Existing subscribers parse them unchanged.
- The Parameter Manager emits `pm-lock-update` (`PMLockBluePrint` payload, `None` when a Lock was removed), `pm-type-update` (`PMTypeBluePrint` payload, `None` when a Type was removed), and re-emits `parameter-creation` for the parameters its Type edits, `add_instance` and Type Lock declarations create as side effects. It emits no `parameter-deletion`: only a direct `remove_parameter` call is announced, by the Server; the parameter removals of a profile load and of `remove_all_parameters` re-emit no creation or deletion Broadcasts. Direct `add_parameter` / `remove_parameter` calls keep being announced by the Server, so nothing is announced twice.
- `broadcast` runs on the thread that called the instrument method, which for a client request is the worker thread holding the instrument mutex: the same place the Server's own broadcasts already run.
- The `_instrument_locks` code in the Server is not renamed or altered; a comment notes that prose calls it the "instrument mutex".
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
}

templates_path = ['_templates']
exclude_patterns = ['build', 'agents', 'README.md', 'Thumbs.db', '.DS_Store', '**.ipynb_checkpoints']
exclude_patterns = ['build', 'agents', 'adr', 'README.md', 'Thumbs.db', '.DS_Store', '**.ipynb_checkpoints']

# -- Internationalization ----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#internationalization
Expand Down
2 changes: 1 addition & 1 deletion docs/getting_started/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ instruments:
type: instrumentserver.params.ParameterManager
initialize: True
gui:
type: instrumentserver.gui.instruments.ParameterManagerGui
type: instrumentserver.gui.parameter_manager.ParameterManagerGui
```

![parameter_manager_2](../_static/param_manager_2.png)
Expand Down
Loading
Loading