86 lines
3.6 KiB
Markdown
86 lines
3.6 KiB
Markdown
# Rayforge external binary-driver addon spike
|
|
|
|
- Date: 2026-10-18
|
|
- Upstream: `barebaric/rayforge`
|
|
- Commit: `c28918c87f8273dd1d244f5a057addc192807fa5`
|
|
- Rayforge addon API: 23
|
|
- Scope: Windows, external addon lifecycle, no device or transport
|
|
|
|
## Result
|
|
|
|
An external addon loaded through Rayforge's real `AddonManager` and registered
|
|
`PirateRayJCZNoDeviceDriver`. The driver declares `uses_gcode = False`, has no
|
|
discovery specification or transport, and returns a recognizable binary test
|
|
payload from an `OpsEncoder`.
|
|
|
|
The following lifecycle completes against the pinned Rayforge checkout:
|
|
|
|
1. load the external addon;
|
|
2. resolve the driver by class name;
|
|
3. create its binary encoder and verify its payload;
|
|
4. disable the addon and verify driver removal;
|
|
5. re-enable it and verify re-registration;
|
|
6. reload it and verify there is exactly one registry entry;
|
|
7. package its source and manifest files into a ZIP; and
|
|
8. disable it cleanly.
|
|
|
|
The current checked result is `tests/fixtures/rayforge-addon-spike.json` with
|
|
SHA-256
|
|
`c7a7ba7d561af58829c9c0fc271234e7600d50cca5f8125fc574fa3f0a1aa25f`.
|
|
|
|
## Registration seam finding
|
|
|
|
Rayforge's addon hook specification still exposes
|
|
`register_machines(machine_manager)`, but the inspected startup registration
|
|
table does not invoke that hook, and `MachineManager` has no method for adding
|
|
a driver class. The source-level driver registry instead exposes
|
|
`rayforge.machine.driver.register_driver(driver)`.
|
|
|
|
The working spike therefore registers during import of the addon's worker
|
|
entry point and removes its class in `on_unload`. All access to the mutable
|
|
module-level registry is now isolated in `rayforge_compat.py`. The adapter
|
|
accepts only addon API 23, checks the expected registry shape, refuses class
|
|
name collisions, is idempotent, and verifies symmetric cleanup. Contract drift
|
|
therefore fails closed instead of silently replacing another driver. Before a
|
|
production addon, Rayforge should still expose a dedicated addon-aware driver
|
|
registry hook with supported unregister behavior.
|
|
|
|
## Reproduce
|
|
|
|
Create an isolated environment containing Rayforge's non-GUI runtime driver
|
|
dependencies, then run:
|
|
|
|
```powershell
|
|
python scripts/spike_rayforge_addon.py C:\path\to\rayforge
|
|
```
|
|
|
|
The script refuses any Rayforge revision other than the pinned commit.
|
|
|
|
## What this proves
|
|
|
|
- An external addon can load a binary, non-G-code driver without patching the
|
|
Rayforge source tree.
|
|
- Its worker entry point is sufficient in headless mode.
|
|
- The driver can remain completely free of discovery and transport.
|
|
- Addon load, disable, enable and reload work when registration and cleanup are
|
|
handled explicitly.
|
|
- The addon itself can be packaged as a deterministic set of files on Windows.
|
|
|
|
## What remains open
|
|
|
|
- The ZIP check is addon packaging, not a proof that a complete PyInstaller or
|
|
MSIX Rayforge build discovers an externally installed addon.
|
|
- The compatibility adapter contains the unstable seam, but does not make it
|
|
a supported addon API. Upstream feasibility should be assessed before
|
|
relying on it in production.
|
|
- No JCZ command encoding, machine profile, UI, project reload, discovery,
|
|
transport, controller status or cancellation is implemented here.
|
|
- Nothing in this spike authorizes USB access, galvo movement or emission.
|
|
|
|
## Decision impact
|
|
|
|
Spike B is a qualified go: the external binary-driver boundary is viable
|
|
without a Rayforge core fork, but registration lifecycle support should be
|
|
made explicit upstream. The next technical proof is Spike C: translate a tiny
|
|
Rayforge `Ops` stream into a deterministic no-I/O JCZ trace and compare its
|
|
semantics with the pinned BalorMK mock trace.
|