Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
13ade7c
fix(stage-a): 🐛 renew leases against the deadline the owner granted
muthmann Aug 3, 2026
8239fae
fix(stage-a): 🐛 say when a run wrote no sensor readout
muthmann Aug 3, 2026
5145e5e
ci: 👷 build installable plugin bundles for four platforms
muthmann Aug 4, 2026
ce99eae
ci: 🐛 stop the build failing on the host's own CI layout
muthmann Aug 4, 2026
35b5696
ci: 🔧 build against the host branch that has the API
muthmann Aug 4, 2026
b2bcf9b
refactor(evesmlm): ♻️ share types through a crate, not between plugins
muthmann Aug 4, 2026
2a46196
fix(stage-a): 🐛 find the Teensy on Windows' nameless COM ports
muthmann Aug 4, 2026
b7166dc
fix(stage-a-a1): 🐛 accept protocol files a spreadsheet saved
muthmann Aug 5, 2026
2861912
feat(stage-a-a1): ✨ apply confirmed camera profiles
muthmann Aug 12, 2026
6941fd1
fix(stage-a-a1): 🐛 use generic camera sessions
muthmann Aug 12, 2026
60d97fa
refactor(stage-a-a1): ♻️ trust generic host confirmation
muthmann Aug 13, 2026
29e5897
feat(stage-a): ✨ carry A2 timing and photodiode geometry on the contract
muthmann Aug 13, 2026
553d85d
feat(stage-a-modulation): ✨ prepare the firmware for A2 step latency
muthmann Aug 13, 2026
cc9d18a
feat(stage-a-photodiode): ✨ measure the direct camera and emission paths
muthmann Aug 13, 2026
39eaa9f
feat(stage-a-a1): ✨ let the sidecar own experiment provenance
muthmann Aug 13, 2026
5f05720
feat(stage-a-a2): ✨ add the fluorescence step-latency protocol runner
muthmann Aug 13, 2026
130ef98
feat(stage-a-a4): ✨ add the contrast-threshold survey plugin
muthmann Aug 13, 2026
df4229e
docs: 📝 index the two new Stage-A workflows
muthmann Aug 13, 2026
7c23805
fix(stage-a-a1): 🐛 keep the protocol's remaining bench time on screen
muthmann Aug 14, 2026
f123331
feat(stage-a): ✨ add guided PD references and A2 bring-up automation
muthmann Sep 5, 2026
c216424
fix(ci): 🐛 build plugins against augur-rs main
muthmann Sep 5, 2026
0d01f08
feat(stage-a-a2): ✨ simplify protocol and capture owner state
muthmann Sep 5, 2026
e38cb09
fix(stage-a-a2): 🐛 declare camera configuration commands
muthmann Sep 5, 2026
b40b878
fix(stage-a-a2): 🐛 automate measurement point setup
muthmann Sep 5, 2026
a00db48
fix(stage-a-a2): 🐛 preserve synchronized captures and diagnose failures
muthmann Sep 7, 2026
7c43446
feat(stage-a): ✨ add Windows capture alerts and lab packaging
muthmann Sep 7, 2026
71242a4
fix(stage-a-a1): 🐛 retry a rejected camera start instead of skipping …
muthmann Sep 7, 2026
c6d7b4e
fix(stage-a-a1): 🐛 refuse a survey whose sidecars the placement canno…
muthmann Sep 7, 2026
a068f6b
fix(stage-a-a1): 🐛 repeat a failed point, lose it, and stop on a streak
muthmann Sep 7, 2026
e6fc903
fix(stage-a-a1): 🐛 state the controller's A1 mode instead of inheriti…
muthmann Sep 7, 2026
d9deb62
fix(stage-a-a1): 🐛 change the controller mode through its owner, not …
muthmann Sep 7, 2026
c05ecff
fix(stage-a-photodiode): 🐛 write recordings off the stream reader thread
muthmann Sep 7, 2026
583129d
fix(stage-a): 🐛 confirm controller commands and keep every point's re…
muthmann Sep 7, 2026
2ef83ef
chore(stage-a-a1): 📄 add the resumed flux-discriminator protocols
muthmann Sep 7, 2026
b90926c
fix(stage-a): 🐛 keep the protocol parser out of the plugins' link graph
muthmann Sep 7, 2026
cf0244e
test(stage-a-a1): ✅ compare the sidecar path against the same derivation
muthmann Sep 7, 2026
e146b12
fix(stage-a): 🐛 resume complete protocol rows and keep recordings tog…
muthmann Sep 8, 2026
cb2558a
fix(stage-a-a2): 🐛 keep continue and stop accessible in the UI mirror
muthmann Sep 8, 2026
537113f
fix(stage-a-a2): 🐛 anchor owner-reported photodiode paths to the run …
muthmann Sep 8, 2026
1e4dc98
feat(stage-a-a3): ✨ automate threshold acquisition with shared A1 rec…
muthmann Sep 9, 2026
d687475
chore(stage-a-a3): 🔧 remove generated test sidecar
muthmann Sep 9, 2026
a3bd9f7
perf(stage-a-a3): 🚀 shorten protocols with targeted frequency checks
muthmann Sep 9, 2026
1f93f4d
test(stage-a): ✅ renew control lease during long protocol validation
muthmann Sep 9, 2026
6edda43
fix(stage-a): 🐛 query controller completions through cached host routing
muthmann Sep 9, 2026
3c46221
feat(stage-a-a4): ✨ record a matched constant-light background reference
muthmann Sep 10, 2026
3aade0e
feat(a4): ✨ automate matched threshold survey
muthmann Sep 10, 2026
ab8592d
feat(stage-a): ✨ add universal measurement plan and A5 protocol
muthmann Sep 10, 2026
705ea8c
feat(stage-a): ✨ define A5 scientific point parameters
muthmann Sep 10, 2026
94528d9
feat(stage-a): ✨ define universal block handoff contract
muthmann Sep 10, 2026
a80ddb7
feat(a4): ✨ accept universal runner block requests
muthmann Sep 10, 2026
d1285c5
feat(stage-a): ✨ add universal runner plugin dispatcher
muthmann Sep 10, 2026
bc6b9d5
fix(stage-a): 🐛 enable universal runner by default
muthmann Sep 10, 2026
b27894a
fix(stage-a): 🐛 resolve built-in A4 protocol handoff
muthmann Sep 10, 2026
3f55be8
feat(a2): ✨ accept universal runner block requests
muthmann Sep 10, 2026
27a3993
feat(stage-a): ✨ accept universal runner blocks in A1 and A3
muthmann Sep 10, 2026
0602ddc
fix(stage-a): 🐛 wait for explicit block completion
muthmann Sep 10, 2026
a4c3c65
feat(stage-a): ✨ publish A1-A3 block completion snapshots
muthmann Sep 10, 2026
b2748b6
feat(a5): ✨ add refractory and load acquisition adapter
muthmann Sep 10, 2026
118b447
fix(a5): 🐛 publish A5 completion identity
muthmann Sep 10, 2026
b8dce9e
fix(stage-a): 🐛 correct A1-A5 plan point count
muthmann Sep 10, 2026
81636c7
feat(stage-a): ✨ carry camera settings in block handoff
muthmann Sep 10, 2026
6b812a4
feat(a5): ✨ ship complete built-in scientific protocol
muthmann Sep 10, 2026
7c36e55
fix: 🐛 make universal Stage-A plan runnable by default
muthmann Sep 10, 2026
0907e21
fix: 🐛 preserve configured A1-A3 protocols in universal runs
muthmann Sep 10, 2026
9447d4e
fix(a5): 🐛 avoid duplicate plugin entrypoint when reusing A2
muthmann Sep 10, 2026
23c1496
fix(build): 🐛 isolate plugin feature resolution
muthmann Sep 10, 2026
a16ac65
fix(stage-a): 🐛 freeze camera biases for A5
muthmann Sep 10, 2026
68a5ca5
feat(stage-a): ✨ require optical state confirmation
muthmann Sep 10, 2026
68465f5
fix(stage-a): 🐛 gate block transitions after AOD changes
muthmann Sep 10, 2026
59da8ed
fix(a5): 🐛 preserve requester identity for delegated acquisition
muthmann Sep 10, 2026
f8f316d
test(stage-a): ✅ add universal runner smoke protocol
muthmann Sep 10, 2026
8b4ccb9
fix(stage-a): 🐛 use automatic camera and PD readbacks
muthmann Sep 10, 2026
9c72eff
feat(stage-a): ✨ automate low-light final campaign
muthmann Sep 10, 2026
603e10c
fix(stage-a): 🐛 centralize universal output folder
muthmann Sep 10, 2026
c96508a
fix(stage-a): 🐛 implement bounded low-light calibration campaigns
muthmann Sep 11, 2026
422ee11
fix(build): 🐛 preserve standalone A2 plugin entrypoint
muthmann Sep 11, 2026
bc66aec
fix(stage-a-a2): 🐛 keep Windows plugin entrypoint exported
muthmann Sep 11, 2026
49c56b4
fix(stage-a-a2): 🐛 export Windows vtable explicitly
muthmann Sep 11, 2026
fed1f86
fix(build): 🐛 avoid empty array expansion in macOS shell
muthmann Sep 11, 2026
f49c5b9
fix(stage-a): 🐛 fit bias preparation and final into four hours
muthmann Sep 11, 2026
660117a
fix(stage-a-a2): 🐛 share the step recorder with A5 through a non-runt…
muthmann Sep 11, 2026
e98a11f
fix(stage-a-universal-runner): 🐛 survive host settings syncs during a…
muthmann Sep 11, 2026
4bce08c
fix(stage-a-universal-runner): 🐛 close the operator-wait deadlocks
muthmann Sep 11, 2026
ed3dfa5
fix(stage-a-universal-runner): 🐛 confirm the optical state without an…
muthmann Sep 11, 2026
a4fb0ce
fix(stage-a): 🐛 keep universal hand-offs out of reach of host setting…
muthmann Sep 11, 2026
e5f6d3c
fix(stage-a-universal-runner): 🐛 request the fo code the sensor confirms
muthmann Sep 11, 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
225 changes: 225 additions & 0 deletions .github/workflows/build-plugins.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
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: 142ccb3877151a172979c2cec6a5efee599831d0

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

permissions:
contents: read

env:
CARGO_TERM_COLOR: always
# The recording root API requires the paired host fix in augur-rs PR #53.
# Pin its tested commit for matching, reproducible host and plugin bundles.
AUGUR_RS_REF: ${{ inputs.augur_rs_ref || '142ccb3877151a172979c2cec6a5efee599831d0' }}

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-15-intel
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: Test Stage-A acquisition and controller contracts
working-directory: augur-plugins
run: cargo test -p augur-plugin-stage-a-a1 -p augur-plugin-stage-a-a2 -p stage-a-step-acquisition -p augur-plugin-stage-a-modulation -p augur-plugin-stage-a-photodiode -p stage-a-io -p stage-a-plugin-contract

- 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: Verify Windows runtime exports
if: runner.os == 'Windows'
working-directory: augur-plugins
shell: pwsh
run: ./scripts/check-windows-runtime-exports.ps1 -Root "dist/windows-x86_64"

- name: Set up Python for the lab watcher
uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Test the independent lab watcher
working-directory: augur-plugins
run: python scripts/test_stage_a_watch.py

- name: Build the Windows lab watcher
if: runner.os == 'Windows'
working-directory: augur-plugins
run: |
python -m pip install pyinstaller==6.16.0
python -m PyInstaller --noconfirm --clean --onefile --name Laborwache \
--distpath dist/windows-x86_64/lab-watch --workpath target/lab-watch \
--specpath target scripts/stage_a_watch.py
cp docs/features/stage-a-lab-watch.md dist/windows-x86_64/lab-watch/README.md

- 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
10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
[workspace]
members = [
"evesmlm-types",
"stage-a-io",
"stage-a-plugin-contract",
"stage-a-sine-acquisition",
"stage-a-step-acquisition",
"stage-a-universal-runner",
"plugins/stage-a-universal-runner",
"plugins/stage-a-a5",
"plugins/stage-a-a1",
"plugins/stage-a-a3",
"plugins/stage-a-a2",
"plugins/stage-a-a4",
"plugins/stage-a-modulation",
"plugins/stage-a-photodiode",
"plugins/localization",
Expand All @@ -27,6 +36,7 @@ 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"
Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,37 @@ The plugin crates under `plugins/` are under active development and not yet read
| `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 |
| `stage-a-a3` | `RawEvents` + orchestration | Shared A1 recorder with A3 identity, simple protocol controls and offline threshold metadata |

`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 Down Expand Up @@ -145,6 +171,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