Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
9c92229
feat(stage-a-io): ✨ add shared Stage-A Teensy I/O library
muthmann Jul 13, 2026
3f0d57f
feat(stage-a-monitor): ✨ add commissioning monitor plugin
muthmann Jul 13, 2026
8f448c2
feat(stage-a-a1): ✨ add A1 minimum-depth Bode calibration plugin
muthmann Jul 13, 2026
77e859a
docs(stage-a): 📝 add Stage-A feature brief and device-ownership ADR
muthmann Jul 13, 2026
d6c6f78
feat(evesmlm,plugins): ✨ batch review findings and current plugin-API…
muthmann Jul 13, 2026
c801fbb
merge: integrate eve-batch API refresh with the stage-a stack
muthmann Jul 13, 2026
10dabfe
fix(plugins): 🐛 rebuild legacy plugins against plugin ABI v5
muthmann Jul 13, 2026
0a4be4d
chore(plugins): 🧹 apply rustfmt across the workspace
muthmann Jul 13, 2026
9c0f349
fix(stage-a): 🐛 align mock and host plugins with firmware 0.2.0 protocol
muthmann Jul 14, 2026
58296eb
feat(stage-a): ✨ add function-generator familiarisation plugin
muthmann Jul 14, 2026
525cc4d
feat(stage-a): ✨ replace commissioning plugins with minimal modulatio…
muthmann Jul 15, 2026
73f4ce1
fix(stage-a): 🐛 exchange enum settings as indices so radio buttons apply
muthmann Jul 16, 2026
bcb2dfc
feat(stage-a): ✨ auto-detect the correct Teensy port in both plugins
muthmann Jul 16, 2026
cc4b435
feat(stage-a): ✨ label port choices with their USB product name
muthmann Jul 16, 2026
449f758
fix(stage-a): 🐛 make device control settings-driven so it works witho…
muthmann Jul 16, 2026
5f323c2
feat(stage-a): ✨ read the PDA1 photodiode stream at 20 kSa/s with env…
muthmann Jul 16, 2026
2553581
feat(stage-a): ✨ add monitor-cache snapshots and disk recording to th…
muthmann Jul 17, 2026
0cd8be9
feat(stage-a): ✨ add spectrum view and absolute-time axis to the phot…
muthmann Jul 17, 2026
8d7c7eb
feat(stage-a): ✨ add a TOML protocol executor to the modulation plugin
muthmann Jul 17, 2026
38752e3
perf(stage-a): ⚡ decimate the photodiode chart from incremental summa…
muthmann Jul 17, 2026
bb83705
fix(stage-a): 🐛 report the legacy ASCII stream as a firmware-flash hint
muthmann Jul 20, 2026
4046b7a
feat(stage-a): ✨ add the A1 orchestration plugin and shared plugin co…
muthmann Jul 27, 2026
c0e091a
fix(stage-a): 🐛 publish the excitation contrast independently of the …
muthmann Jul 27, 2026
029638a
fix(stage-a): 🐛 stop settings syncs from overwriting a running protoc…
muthmann Jul 27, 2026
c1303d7
fix(stage-a): 🐛 normalise the A1 rolling response over the ROI, and m…
muthmann Jul 27, 2026
f22ce9a
docs(stage-a): 📝 record the contrast-geometry decision as ADR 012
muthmann Jul 27, 2026
361fc67
feat(stage-a): ✨ bring the a₀ depth lock onto the fixed contrast geom…
muthmann Jul 27, 2026
3fee3d6
feat(stage-a): ✨ run the A1 a₀ frequency ladder unattended on one lease
muthmann Jul 27, 2026
5011dac
fix(stage-a): 🐛 keep A1 recordings full-length and in one folder
muthmann Jul 25, 2026
7b78432
fix(stage-a): 🐛 write the A1 PDQ straight into the measurement folder
muthmann Jul 28, 2026
bcd0e66
docs(stage-a): 📝 index the recording-robustness ADR after its renumbe…
muthmann Jul 28, 2026
fc6eaab
fix(stage-a): 🐛 align Pockels and contrast semantics
muthmann Jul 29, 2026
1677169
merge: 🔀 integrate latest Stage-A A₀ workflow
muthmann Jul 29, 2026
f9b7dde
fix(stage-a): 🐛 configure Pockels lobes by observed endpoints
muthmann Jul 29, 2026
e1ca7cf
fix(stage-a): 🐛 calibrate millivolt-scale photodiode signals
muthmann Jul 29, 2026
3816067
merge: 🔀 integrate observed Pockels lobe endpoints
muthmann Jul 29, 2026
f80c160
fix(stage-a): 🐛 allow microvolt photodiode inputs
muthmann Jul 29, 2026
764f4f2
feat(stage-a): ✨ drive A1 surveys from declarative protocols
muthmann Aug 3, 2026
938d76b
fix(stage-a): 🐛 renew leases against the deadline the owner granted
muthmann Aug 3, 2026
30e677c
fix(stage-a): 🐛 say when a run wrote no sensor readout
muthmann Aug 3, 2026
5f118e5
ci: 👷 build installable plugin bundles for four platforms
muthmann Aug 4, 2026
c61fa6c
ci: 🐛 stop the build failing on the host's own CI layout
muthmann Aug 4, 2026
671b254
ci: 🔧 build against the host branch that has the API
muthmann Aug 4, 2026
a24e135
refactor(evesmlm): ♻️ share types through a crate, not between plugins
muthmann Aug 4, 2026
e5cd98b
fix(stage-a): 🐛 find the Teensy on Windows' nameless COM ports
muthmann Aug 4, 2026
0e07d88
fix(stage-a-a1): 🐛 accept protocol files a spreadsheet saved
muthmann Aug 5, 2026
eacf268
style(stage-a): 🎨 apply rustfmt to the port-discovery tests
muthmann Aug 5, 2026
57e75b3
fix(stage-a-a1): 🐛 make every protocol row name itself
muthmann Aug 5, 2026
4bb4826
feat(stage-a-a1): ✨ say how much longer a run has to go
muthmann Aug 5, 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
199 changes: 199 additions & 0 deletions .github/workflows/build-plugins.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
name: Build Plugins

# Produces drop-in plugin folders for a machine that has no Rust toolchain: the
# bench downloads a bundle, copies its folders into ~/.augur/plugins/, and hits
# "Scan for New Plugins". Pull requests get workflow artifacts; main also
# publishes a rolling release so the download needs no GitHub login.

on:
push:
branches:
- main
pull_request:
workflow_dispatch:
inputs:
augur_rs_ref:
description: "augur-rs ref to build against (branch, tag or SHA)"
required: false
default: fix/gui-layout-and-alignment

concurrency:
group: build-plugins-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

env:
CARGO_TERM_COLOR: always
# The host branch this repository actually compiles against. augur-rs `main`
# does not carry the TableSchema, host-view and dataset-descriptor API these
# plugins use, so defaulting to `main` would be a guaranteed red build and
# would never hand the bench a bundle. Move this back to `main` in the same
# commit that the host API lands there.
AUGUR_RS_REF: ${{ inputs.augur_rs_ref || 'fix/gui-layout-and-alignment' }}

jobs:
build:
name: ${{ matrix.name }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: macos-latest
name: macOS (arm64)
bundle: macos-arm64
- os: macos-13
name: macOS (x86_64)
bundle: macos-x86_64
- os: ubuntu-latest
name: Linux (x86_64)
bundle: linux-x86_64
- os: windows-latest
name: Windows (x86_64)
bundle: windows-x86_64

defaults:
run:
# The repo drives its builds through two bash scripts; use the same
# shell on Windows so there is exactly one code path to reason about.
shell: bash

steps:
# This workspace depends on the host by path (../augur-rs/augur-core), so
# CI has to reproduce the two-sibling-checkout layout, not clone one repo.
- name: Check out augur-plugins
uses: actions/checkout@v5
with:
path: augur-plugins

- name: Check out augur-rs
uses: actions/checkout@v5
with:
repository: muthmann/augur-rs
ref: ${{ env.AUGUR_RS_REF }}
path: augur-rs

- name: Pin the host revision and disarm the source patch
run: |
echo "AUGUR_RS_SHA=$(git -C augur-rs rev-parse HEAD)" >> "$GITHUB_ENV"
# build-runtime-plugins.sh patches [patch."…/augur-rs.git"] whenever a
# sibling augur-rs *git checkout* exists. This workspace already
# depends on it by path, so that patch matches nothing in the crate
# graph — it only costs cargo a fetch of the checkout. Removing .git
# makes the script's detection fail and the path deps win outright.
rm -rf augur-rs/.git

- name: Resolve the pinned Rust toolchain
run: |
channel="$(sed -n 's/^channel *= *"\(.*\)"$/\1/p' augur-plugins/rust-toolchain.toml | head -n 1)"
if [[ -z "${channel}" ]]; then
echo "No channel found in augur-plugins/rust-toolchain.toml" >&2
exit 1
fi
echo "RUST_CHANNEL=${channel}" >> "$GITHUB_ENV"

- name: Install Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: ${{ env.RUST_CHANNEL }}
cache-workspaces: augur-plugins
# The action injects RUSTFLAGS="-D warnings" by default. That is right
# for a lint job and wrong here: this job ships artifacts, and a dead-
# code warning in one plugin must not deny the bench a bundle for all
# of them. Lint gating belongs in its own job, not in the build.
rustflags: ""

- name: Install Linux system dependencies
if: runner.os == 'Linux'
# Only what the plugin crates actually link. augur-gui's own dependency
# script is deliberately not reused: it is a superset (X11/Wayland/GL for
# the GUI, which no plugin links) and it does not exist on every augur-rs
# revision this job can be pointed at, so borrowing it made the Linux
# build fail on the value of augur_rs_ref. serialport needs libudev.
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends pkg-config libudev-dev

- name: Build runtime plugins
working-directory: augur-plugins
run: bash scripts/build-runtime-plugins.sh --profile release

- name: Stage installable plugin folders
working-directory: augur-plugins
run: bash scripts/install-built-plugins.sh --profile release --dest "dist/${{ matrix.bundle }}"

- name: Write build provenance
working-directory: augur-plugins
run: |
{
echo "bundle: ${{ matrix.bundle }}"
echo "built_at: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
echo "augur_plugins: $(git rev-parse HEAD)"
echo "augur_rs_ref: ${AUGUR_RS_REF}"
echo "augur_rs_sha: ${AUGUR_RS_SHA}"
echo "rustc: $(rustc --version)"
echo
echo "Copy the plugin folders next to this file into ~/.augur/plugins/,"
echo "then use Plugins -> Scan for New Plugins in augur-gui."
} > "dist/${{ matrix.bundle }}/BUILD-INFO.txt"

- name: Upload plugin bundle
uses: actions/upload-artifact@v4
with:
name: augur-plugins-${{ matrix.bundle }}
path: augur-plugins/dist/${{ matrix.bundle }}
if-no-files-found: error

release:
name: Publish rolling release
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Download every plugin bundle
uses: actions/download-artifact@v4
with:
path: bundles
pattern: augur-plugins-*

- name: Package one archive per platform
run: |
set -euo pipefail
mkdir -p dist
for bundle_dir in bundles/augur-plugins-*/; do
bundle="$(basename "${bundle_dir%/}")"
(cd "${bundle_dir}" && zip -qr "${GITHUB_WORKSPACE}/dist/${bundle}.zip" .)
echo "Packaged ${bundle}.zip"
done
(cd dist && sha256sum ./*.zip > SHA256SUMS.txt)
ls -l dist

- name: Publish rolling release
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
tag="plugins-latest"
# Delete and recreate rather than upload --clobber: it retags at the
# new commit and guarantees no asset from an older build survives.
gh release delete "${tag}" --yes --cleanup-tag || true
gh release create "${tag}" dist/* \
--title "Prebuilt plugins (latest main)" \
--notes "$(printf '%s\n' \
"Prebuilt AugurRS plugins, rebuilt on every push to \`main\`." \
"" \
"- augur-plugins: \`${GITHUB_SHA}\`" \
"- built against augur-rs \`${AUGUR_RS_REF}\`" \
"" \
"Download the archive for your platform, unpack it, and copy the" \
"plugin folders inside into \`~/.augur/plugins/\`. Then open augur-gui," \
"go to **Plugins**, and click **Scan for New Plugins**." \
"" \
"\`BUILD-INFO.txt\` in each archive records the exact revisions and" \
"compiler the libraries were built with. Verify downloads against" \
"\`SHA256SUMS.txt\`.")"
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/target
/dist
Cargo.lock
*.swp
*.swo
Expand Down
20 changes: 20 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,18 @@ Plugins do not render `egui` directly. Instead, expose:

The host owns rendering, export, caching, and window state for declared host views.

When a table dataset should participate in the linked investigation workspace, also populate the additive metadata the host can use:

- `coordinate_space_2d`
- `coordinate_space_3d`
- `row_id_column`
- `time_column`
- `layer_id`
- `semantic_label`
- `HostDatasetDescriptor.display`

Prefer structured datasets for selection/linking and use overlays only for supplemental 2D annotations or hit-testing.

### 7. Write `plugin.toml`

Use the runtime format:
Expand All @@ -160,6 +172,14 @@ cp plugins/my-plugin/plugin.toml ~/.augur/plugins/my-plugin/
cp target/release/libaugur_plugin_my_plugin.dylib ~/.augur/plugins/my-plugin/
```

On macOS, either run `./scripts/install-built-plugins.sh --profile release` instead of the manual
copy steps or rewrite the installed dylib id yourself:

```bash
install_name_tool -id "@loader_path/libaugur_plugin_my_plugin.dylib" \
~/.augur/plugins/my-plugin/libaugur_plugin_my_plugin.dylib
```

Then open `augur-gui`, go to **Plugins**, click **Scan for New Plugins**, and enable the plugin.

## Migrating Older Plugins
Expand Down
9 changes: 9 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
[workspace]
members = [
"evesmlm-types",
"stage-a-io",
"stage-a-plugin-contract",
"plugins/stage-a-a1",
"plugins/stage-a-modulation",
"plugins/stage-a-photodiode",
"plugins/localization",
"plugins/reconstruction",
"plugins/focus-metrics",
Expand All @@ -22,6 +28,9 @@ augur-core = { path = "../augur-rs/augur-core" }
augur-plugin-api = { path = "../augur-rs/augur-plugin-api" }
augur-plugin-types = { path = "../augur-rs/augur-plugin-types" }
egui = "0.27"
evesmlm-types = { path = "evesmlm-types" }
rustfft = "6"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
serialport = "4"
stage-a-plugin-contract = { path = "stage-a-plugin-contract" }
60 changes: 54 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,72 @@ Use this repository for the plugin implementations, template crate, and repo-loc
## Runtime Model

- Each plugin ships as a `plugin.toml` manifest plus one platform library (`.dylib`, `.so`, or `.dll`).
- `augur-gui` discovers plugins from `~/.augur/plugins/`, loads the exported `augur_plugin_vtable`, and renders settings, status, and host views through the host.
- `augur-gui` discovers plugins from `~/.augur/plugins/`, loads the exported `augur_plugin_vtable`, and renders settings, status, and linked investigation datasets/views through the host.
- Host-owned built-in tools stay in `augur-gui`; they are not runtime plugins in this repository.
- Host-owned experiment settings such as pixel scale, sensor geometry, acquisition time, and EventStore budget are published to plugins as `GlobalSettings` on `augur.global_settings`.
- Standard shared scientific payloads can also live in companion crates such as `augur-plugin-types`.

## Investigation Workspace Contract

The host now owns a generic linked workspace across:

- 2D preview
- 3D inspection
- host-rendered tables

For plugins, that means:

- structured datasets are the primary linking mechanism
- stable row ids should be provided when possible
- 2D/3D coordinate metadata should be declared when the plugin has it
- layer/display metadata should describe visibility, color, marker shape, and size
- overlays are supplemental annotations, not the primary integration surface

## In-Tree Runtime Plugins (work in progress)

The plugin crates under `plugins/` are under active development and not yet ready for external use. The template crate and documentation are stable references for writing your own plugins.

| Plugin | Phase | Notes |
|---|---|---|
| `localization` | `RawEvents` | Wavelet/Gaussian SMLM localization and standard `LocalizationResults` output |
| `reconstruction` | `DerivedData` | Accumulated localization table plus host-rendered reconstruction windows |
| `reconstruction` | `DerivedData` | Accumulated localization dataset with stable ids, time metadata, density rendering, and 3D inspection |
| `focus-metrics` | `DerivedData` | Focus metrics from localization results or FFT preview sharpness |
| `evesmlm-candidates` | `RawEvents` | Event-domain candidate clustering for eveSMLM |
| `evesmlm-fitting` | `DerivedData` | Candidate fitting plus EVE and compatibility localization outputs |
| `evesmlm-postproc` | `DerivedData` | Filtering, drift correction, evaluation, and the later EVE compact view provider |
| `evesmlm-candidates` | `RawEvents` | Event-domain candidate clustering plus accepted/rejected raw-event investigation layers |
| `evesmlm-fitting` | `DerivedData` | Candidate fitting plus shared current-localization datasets, stable ids, and linked 3D inspection |
| `evesmlm-postproc` | `DerivedData` | Filtering, drift correction, evaluation, and the later shared EVE current-localization provider |
| `stage-a-modulation` | control service | Sole owner of the Stage-A Teensy command port and ACKed modulation state |
| `stage-a-photodiode` | control service | Sole owner of the Stage-A stream port, PDA1 ingestion, and PDQ persistence |
| `stage-a-a1` | `RawEvents` + orchestration | A1 protocol/schedule validation, raw phase quicklooks, analysis core, and a safety-gated commissioning run through the two owner services |

`plugin-template/` is the starting point for new plugin crates.

## Quick Start

### Download Prebuilt Plugins (no toolchain needed)

Every push to `main` publishes freshly built plugins for macOS (arm64 and x86_64),
Linux and Windows to the rolling
[`plugins-latest`](https://github.com/muthmann/augur-plugins/releases/tag/plugins-latest)
release. This is the recommended route for a measurement machine.

```bash
curl -LO https://github.com/muthmann/augur-plugins/releases/download/plugins-latest/augur-plugins-macos-arm64.zip
unzip augur-plugins-macos-arm64.zip -d augur-plugins-bundle
mkdir -p ~/.augur/plugins
cp -R augur-plugins-bundle/*/ ~/.augur/plugins/
```

Pick the archive matching the machine: `macos-arm64`, `macos-x86_64`,
`linux-x86_64`, or `windows-x86_64`. Then open `augur-gui`, go to **Plugins**, and
click **Scan for New Plugins**.

Each archive contains a `BUILD-INFO.txt` recording the `augur-rs` revision and the
`rustc` version the libraries were built against — quote it in any ABI-mismatch
report. Verify downloads against `SHA256SUMS.txt` from the same release.

Pull requests build the same bundles as workflow artifacts. See
[CI Prebuilt Plugin Bundles](./docs/features/ci-prebuilt-plugin-bundles.md).

### Build One Plugin

```bash
Expand All @@ -62,6 +106,8 @@ cp target/release/libaugur_plugin_localization.dylib ~/.augur/plugins/localizati
```

On Linux, copy the `.so`. On Windows, copy the `.dll`.
On macOS, prefer `./scripts/install-built-plugins.sh --profile release`; it rewrites the copied
plugin dylib id so Plugin Manager reloads do not keep pointing at Cargo's build tree.

Then open `augur-gui`, go to **Plugins**, click **Scan for New Plugins**, and enable the plugin.

Expand Down Expand Up @@ -101,7 +147,8 @@ The current authoring flow is:
2. export the vtable with `export_plugin!`
3. choose `input_kind()` and optional `PluginCapabilities`
4. use `HostContext` for shared payloads, companion crates such as `augur-plugin-types` for reusable payload types, and `CTX_GLOBAL_SETTINGS` for host-owned calibration/settings
5. declare host-rendered outputs with `host_views()` when needed
5. declare host-rendered outputs with `host_views()` when needed and populate stable-id / coordinate / layer metadata when the dataset should participate in linked investigation
- to expose interactive operations, append `HostActionDescriptor`s to `HostViewRegistry.actions` (scope `Dataset`/`Row`/`Cluster`, optional `param_schema`); consume requests from the persistent context key `CTX_INVESTIGATION_ACTION_REQUESTS`
6. build a `cdylib`
7. install `plugin.toml` plus the compiled library into `~/.augur/plugins/<name>/`

Expand All @@ -123,6 +170,7 @@ augur-plugins/

- [Plugin API Notes](./docs/plugin-api.md) — repo-local summary of the current runtime contract
- [Installing Plugins](./docs/installing-plugins.md) — build, copy, reload, and troubleshoot installed plugins
- [CI Prebuilt Plugin Bundles](./docs/features/ci-prebuilt-plugin-bundles.md) — how the downloadable per-platform bundles are built and published
- [Architecture Notes](./docs/architecture.md) — repository role, execution model, host views, and shared settings
- [augur-rs Plugin Authoring Guide](https://github.com/muthmann/augur-rs/blob/main/docs/features/plugin-authoring-guide.md) — canonical host/runtime authoring guide
- [augur-rs Global Settings Guide](https://github.com/muthmann/augur-rs/blob/main/docs/features/global-settings-menu.md) — host-owned settings published to plugins
Expand Down
Loading
Loading