docs: update all context files for v0.5.2 session (2026-05-17)
Browse filesSYNC.md β full account of what was fixed:
- Ableton-style TreeView picker (PluginPickerComponent)
- addChildComponent vs addAndMakeVisible freeze bug
- Background scan / live Helper shared memory conflict
- Plugin deduplication (VST3 + AU + system + user dirs)
- Manufacturer name extraction (copyright string cleanup)
MACHINE-CONTEXT.md β new file:
- All key paths (T7 Shield home, plugin dirs, safety DB)
- VST3 bundle structure on this machine (no moduleinfo.json)
- Verified Info.plist fields for manufacturer extraction
- Known plugin behaviors (safe/unsafe list)
- Verified working and broken JUCE 8 APIs
- Build environment
FAILED-APPROACHES.md β added entry #10:
- CAContext + CALayerHost cross-process GUI attempt
- Why it fails in Ableton's process environment
- All sub-attempts and results
CLAUDE.md β full rewrite to match actual v0.5.2 architecture:
- Hybrid safety-scan model (not out-of-process floating window)
- Load flow diagram (Unknown/Safe/Unsafe paths)
- Updated file structure (PluginPickerComponent, PluginSafetyDB, PBLog)
- Correct JUCE 8 API table
- addChildComponent warning added
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- CLAUDE.md +178 -103
- FAILED-APPROACHES.md +45 -0
- MACHINE-CONTEXT.md +144 -97
- SYNC.md +148 -56
|
@@ -2,125 +2,200 @@
|
|
| 2 |
|
| 3 |
## What This Is
|
| 4 |
A JUCE-based VST3/AU plugin that:
|
| 5 |
-
1. Hosts any third-party VST3/AU plugin
|
| 6 |
-
2.
|
| 7 |
-
3.
|
| 8 |
-
4.
|
|
|
|
| 9 |
|
| 10 |
-
## Current Status
|
| 11 |
-
- **Sprint 1:** β
Done β plugin shell + MCP server
|
| 12 |
-
- **Sprint 2:** β
Done β out-of-process plugin hosting, crash-safe, GUI working
|
| 13 |
-
- **Sprint 3:** Next β full MCP client integration test (search/get/set params via curl)
|
| 14 |
-
- **Sprint 4:** Pending β real-time audio analysis (LUFS, FFT)
|
| 15 |
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
|
| 18 |
```
|
| 19 |
-
ββ Ableton Process βββββββββββββββββ
|
| 20 |
-
β
|
| 21 |
-
β PluginBridge.vst3
|
| 22 |
-
β βββ
|
| 23 |
-
β
|
| 24 |
-
β β βββ
|
| 25 |
-
β β βββ
|
| 26 |
-
β β
|
| 27 |
-
β
|
| 28 |
-
β
|
| 29 |
-
β
|
| 30 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
```
|
| 32 |
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
## Build System
|
| 36 |
-
- **JUCE 8** via CMake (git submodule at `/JUCE`)
|
| 37 |
-
- **Two build targets:**
|
| 38 |
-
- `PluginBridge` β VST3/AU plugin (loaded in DAW)
|
| 39 |
-
- `PluginBridgeHelper` β standalone executable (bundled inside .vst3/Contents/Resources/)
|
| 40 |
-
- **cpp-httplib** β `libs/httplib.h` (MIT, header-only)
|
| 41 |
-
- **nlohmann/json** β `libs/json.hpp` (MIT, header-only)
|
| 42 |
-
- **Build:** `cd build && cmake .. -DCMAKE_C_COMPILER=/usr/bin/cc -DCMAKE_CXX_COMPILER=/usr/bin/c++ && cmake --build . --config Release`
|
| 43 |
-
- **CMake 3.28** required (via pip β system CMake 4.x incompatible with JUCE 8)
|
| 44 |
-
|
| 45 |
-
## Project Decisions (Confirmed)
|
| 46 |
-
- **Platform:** macOS only
|
| 47 |
-
- **License:** GPL (JUCE GPL mode)
|
| 48 |
-
- **VST3 install:** `~/Library/Audio/Plug-Ins/VST3/`
|
| 49 |
-
- **Test plugins:** Pro-Q 4, LA-2A, Kickstart 2 (crash test)
|
| 50 |
-
- **Xcode CLI tools:** 2410+
|
| 51 |
-
- **DAW:** Ableton Live 12 (primary)
|
| 52 |
-
|
| 53 |
-
## IPC Between Plugin β Helper
|
| 54 |
-
- **Audio:** POSIX shared memory (`shm_open` + `mmap`) β zero-copy
|
| 55 |
-
- **Sync:** Named semaphores (`sem_open`) β ~microsecond wakeup
|
| 56 |
-
- **Commands:** Unix domain socket, newline-delimited JSON
|
| 57 |
-
- **Protocol:** See `Source/Shared/IPCProtocol.h` for all message formats
|
| 58 |
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
-
|
| 84 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 85 |
|
| 86 |
-
|
| 87 |
-
- β `MSG_NOSIGNAL` β doesn't exist on macOS. Use `signal(SIGPIPE, SIG_IGN)` + `SO_NOSIGPIPE`
|
| 88 |
-
- β Plugin scanning in background thread β some plugins show NSAlert β crash. Solution: don't scan at all, just list .vst3/.component files from disk
|
| 89 |
-
- β `NSApplicationActivationPolicyRegular` β steals focus from DAW. Use `Accessory` (policy 1)
|
| 90 |
-
- β `activateIgnoringOtherApps:YES` β switches away from Ableton. Use `orderFrontRegardless` instead
|
| 91 |
-
- β
`posix_spawn` β works for launching helper from within a plugin process
|
| 92 |
|
| 93 |
## File Structure
|
|
|
|
| 94 |
```
|
| 95 |
pluginbridge/
|
| 96 |
-
βββ CMakeLists.txt
|
| 97 |
-
βββ JUCE/
|
| 98 |
βββ libs/
|
| 99 |
-
β βββ httplib.h
|
| 100 |
-
β βββ json.hpp
|
| 101 |
βββ Source/
|
| 102 |
-
β βββ Plugin/
|
| 103 |
-
β β βββ PluginBridgeProcessor.h/.cpp
|
| 104 |
-
β β βββ PluginBridgeEditor.h/.
|
| 105 |
-
β β βββ
|
| 106 |
-
β β
|
| 107 |
-
β βββ
|
| 108 |
-
β β βββ
|
| 109 |
-
β β βββ
|
|
|
|
|
|
|
|
|
|
|
|
|
| 110 |
β β βββ HelperIPC.h/.cpp
|
| 111 |
-
β βββ Shared/
|
| 112 |
-
β βββ SharedAudioBuffer.h
|
| 113 |
-
β βββ IPCProtocol.h
|
| 114 |
β βββ Constants.h
|
| 115 |
-
βββ CLAUDE.md
|
| 116 |
-
βββ
|
| 117 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
```
|
| 119 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
## What NOT To Do
|
| 121 |
-
|
| 122 |
-
- Don't
|
| 123 |
-
- Don't use `
|
| 124 |
-
- Don't
|
| 125 |
-
- Don't use `
|
| 126 |
-
- Don't
|
|
|
|
| 2 |
|
| 3 |
## What This Is
|
| 4 |
A JUCE-based VST3/AU plugin that:
|
| 5 |
+
1. Hosts any third-party VST3/AU plugin with full GUI embedded in Ableton's window
|
| 6 |
+
2. Crash-isolates unsafe plugins by loading them in a separate Helper process (audio-only mode)
|
| 7 |
+
3. Exposes ALL hosted plugin parameters via a local MCP server (port 16620)
|
| 8 |
+
4. Audio routes through shared memory (zero added latency)
|
| 9 |
+
5. Any AI that speaks MCP (Claude Code, Codex CLI, Gemini CLI) can control any plugin
|
| 10 |
|
| 11 |
+
## Current Status (v0.5.2 β 2026-05-17)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
+
- β
In-process GUI hosting (full GUI for safe plugins)
|
| 14 |
+
- β
Safety scanning system (Helper-based pre-test, results cached in safety_db.json)
|
| 15 |
+
- β
Audio-only mode for unsafe plugins (iZotope, etc.)
|
| 16 |
+
- β
Ableton-style plugin picker (manufacturer TreeView, search, safety badges)
|
| 17 |
+
- β
MCP server (port 16620, HTTP JSON-RPC 2.0)
|
| 18 |
+
- β
Verified working: Pro-Q 4, God Particle, Little MicroShift, LIMITER
|
| 19 |
+
|
| 20 |
+
## Architecture: HYBRID SAFETY-SCAN
|
| 21 |
|
| 22 |
```
|
| 23 |
+
ββ Ableton Process βββββββββββββββββββββββββββββββββββββββββββββββ
|
| 24 |
+
β β
|
| 25 |
+
β PluginBridge.vst3 β
|
| 26 |
+
β βββ PluginBridgeProcessor β
|
| 27 |
+
β β βββ getInstalledPluginFiles() β scans VST3+AU dirs β
|
| 28 |
+
β β βββ getPluginManufacturer() β reads Info.plist, cached β
|
| 29 |
+
β β βββ PluginSafetyDB β safe/unsafe/unknown JSON DB β
|
| 30 |
+
β β βββ startBackgroundScan() β pre-tests all unknown plugins β
|
| 31 |
+
β β βββ loadPlugin() β orchestrates load flow β
|
| 32 |
+
β β βββ McpServer β HTTP MCP port 16620 β
|
| 33 |
+
β β β
|
| 34 |
+
β βββ PluginBridgeEditor β
|
| 35 |
+
β β βββ PluginPickerComponent β manufacturer TreeView + searchβ
|
| 36 |
+
β β βββ pluginEditor β hosted plugin's own editor β
|
| 37 |
+
β β β
|
| 38 |
+
β βββ HelperConnection β IPC + shared memory to Helperβ
|
| 39 |
+
β β
|
| 40 |
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 41 |
+
β Unix socket + shared memory
|
| 42 |
+
βΌ
|
| 43 |
+
ββ PluginBridgeHelper.app (separate process) ββββββββββββββββββββββ
|
| 44 |
+
β Bundled in PluginBridge.vst3/Contents/Resources/ β
|
| 45 |
+
β β
|
| 46 |
+
β Modes: β
|
| 47 |
+
β --load <path> Load plugin, route audio via shared memory β
|
| 48 |
+
β --scan <path> Test-load plugin, exit 0=safe / crash=unsafe β
|
| 49 |
+
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 50 |
```
|
| 51 |
|
| 52 |
+
## Plugin Load Flow
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
+
```
|
| 55 |
+
User selects plugin in picker
|
| 56 |
+
β
|
| 57 |
+
βΌ
|
| 58 |
+
loadPlugin() checks PluginSafetyDB
|
| 59 |
+
β
|
| 60 |
+
ββββββ΄βββββββββββββββββββ
|
| 61 |
+
β β
|
| 62 |
+
Unknown Safe/Unsafe already known
|
| 63 |
+
β β
|
| 64 |
+
βΌ β
|
| 65 |
+
Wait for Helper to exit β
|
| 66 |
+
Spawn: Helper --scan β
|
| 67 |
+
β β
|
| 68 |
+
Crashed? β mark Unsafe β
|
| 69 |
+
Exit 0? β mark Safe β
|
| 70 |
+
β β
|
| 71 |
+
ββββββββββββββ¬βββββββββββ
|
| 72 |
+
β
|
| 73 |
+
βββββββββ΄βββββββββ
|
| 74 |
+
β β
|
| 75 |
+
Safe Unsafe
|
| 76 |
+
β β
|
| 77 |
+
βΌ βΌ
|
| 78 |
+
Helper --load Helper --load
|
| 79 |
+
(audio routing) (audio routing)
|
| 80 |
+
β β
|
| 81 |
+
βΌ βΌ
|
| 82 |
+
loadInProcess() showAudioOnlyUI()
|
| 83 |
+
createEditor() (MCP still works)
|
| 84 |
+
embed in window
|
| 85 |
+
```
|
| 86 |
|
| 87 |
+
**Key constraint:** Only ONE Helper process at a time. Scanner must wait for live Helper to finish before spawning a scan subprocess (same shared memory name β conflict).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
## File Structure
|
| 90 |
+
|
| 91 |
```
|
| 92 |
pluginbridge/
|
| 93 |
+
βββ CMakeLists.txt
|
| 94 |
+
βββ JUCE/ (git submodule)
|
| 95 |
βββ libs/
|
| 96 |
+
β βββ httplib.h (MIT, header-only HTTP server)
|
| 97 |
+
β βββ json.hpp (MIT, header-only JSON)
|
| 98 |
βββ Source/
|
| 99 |
+
β βββ Plugin/ β VST3/AU (runs inside Ableton)
|
| 100 |
+
β β βββ PluginBridgeProcessor.h/.cpp β main logic
|
| 101 |
+
β β βββ PluginBridgeEditor.h/.mm β editor window
|
| 102 |
+
β β βββ PluginPickerComponent.h/.mm β plugin browser UI (new v0.5)
|
| 103 |
+
β β βββ HelperConnection.h/.cpp β IPC + shared memory
|
| 104 |
+
β β βββ McpServer.h/.cpp β HTTP MCP server
|
| 105 |
+
β β βββ PluginSafetyDB.h β safe/unsafe JSON DB
|
| 106 |
+
β β βββ Blocklist.h β persistent crash blocklist
|
| 107 |
+
β β βββ PBLog.h β timestamped logger
|
| 108 |
+
β βββ Helper/ β Standalone exe (separate process)
|
| 109 |
+
β β βββ main.cpp β --load and --scan modes
|
| 110 |
+
β β βββ HelperPluginHost.h/.mm
|
| 111 |
β β βββ HelperIPC.h/.cpp
|
| 112 |
+
β βββ Shared/
|
| 113 |
+
β βββ SharedAudioBuffer.h β shared memory layout
|
| 114 |
+
β βββ IPCProtocol.h β JSON message formats
|
| 115 |
β βββ Constants.h
|
| 116 |
+
βββ CLAUDE.md β this file
|
| 117 |
+
βββ MACHINE-CONTEXT.md β system paths, verified APIs, plugin behaviors
|
| 118 |
+
βββ SYNC.md β what was built/fixed and when
|
| 119 |
+
βββ FAILED-APPROACHES.md β what didn't work and why
|
| 120 |
+
βββ ROADMAP.md
|
| 121 |
+
```
|
| 122 |
+
|
| 123 |
+
## Build
|
| 124 |
+
|
| 125 |
+
```bash
|
| 126 |
+
cd "/Volumes/T7 Shield/Users/Aditya/pluginbridge/build"
|
| 127 |
+
cmake --build . --target PluginBridge_VST3 -- -j4
|
| 128 |
+
# Auto-installs to ~/Library/Audio/Plug-Ins/VST3/PluginBridge.vst3
|
| 129 |
```
|
| 130 |
|
| 131 |
+
**Two targets:**
|
| 132 |
+
- `PluginBridge_VST3` β builds + installs both plugin and Helper
|
| 133 |
+
- `PluginBridgeHelper` β Helper standalone only
|
| 134 |
+
|
| 135 |
+
**CMake 3.28 required** (system CMake 4.x incompatible with JUCE 8).
|
| 136 |
+
|
| 137 |
+
## Key Runtime Data
|
| 138 |
+
|
| 139 |
+
| File | Purpose |
|
| 140 |
+
|---|---|
|
| 141 |
+
| `~/Library/PluginBridge/safety_db.json` | Safe/unsafe status per plugin path+mtime |
|
| 142 |
+
| `~/Library/PluginBridge/debug.log` | Session-tagged timestamped log |
|
| 143 |
+
| `~/Library/PluginBridge/scan.log` | Background scan output |
|
| 144 |
+
| `~/Library/PluginBridge/blocklist.txt` | Plugins that crashed Helper during use |
|
| 145 |
+
|
| 146 |
+
## PluginPickerComponent (v0.5)
|
| 147 |
+
|
| 148 |
+
`Source/Plugin/PluginPickerComponent.h/.mm`
|
| 149 |
+
|
| 150 |
+
- `Entry` struct: `{ file, name, manufacturer, blocked, safety }`
|
| 151 |
+
- Callbacks: `onPluginSelected`, `onClose`, `onClearBlocklist`, `onClearSafetyCache`
|
| 152 |
+
- `static constexpr int kWidth = 420, kHeight = 500`
|
| 153 |
+
- Tree: `ManufacturerTreeItem` (folder) β `PluginTreeItem` (leaf, 24px height)
|
| 154 |
+
- Safety badges: `Safety::Safe` β `β` green; `Safety::Unsafe` β `audio only` orange; `blocked` β `β ` red
|
| 155 |
+
- Search: `addChildComponent(searchList)` β NOT `addAndMakeVisible` (see FAILED-APPROACHES)
|
| 156 |
+
- Custom `TreeLF` LookAndFeel overrides `drawTreeviewPlusMinusBox` for Ableton-style arrows
|
| 157 |
+
|
| 158 |
+
## JUCE 8 APIs β Verified
|
| 159 |
+
|
| 160 |
+
### Working
|
| 161 |
+
- `formatManager.addFormat(new juce::VST3PluginFormat())`
|
| 162 |
+
- `formatManager.addFormat(new juce::AudioUnitPluginFormat())`
|
| 163 |
+
- `plugin->createEditor()` β message thread only
|
| 164 |
+
- `plugin->hasEditor()`
|
| 165 |
+
- `plugin->getParameters()` β `Array<AudioProcessorParameter*>`
|
| 166 |
+
- `juce::MessageManager::callAsync(lambda)` β cross-thread UI updates
|
| 167 |
+
- `addChildComponent(comp)` β adds hidden; use instead of `addAndMakeVisible` when starting invisible
|
| 168 |
+
- `juce::TreeView` + `juce::TreeViewItem` with custom `LookAndFeel`
|
| 169 |
+
- `juce::File::findChildFiles(findDirectories, false, "*.vst3")`
|
| 170 |
+
|
| 171 |
+
### Broken β Do NOT Use
|
| 172 |
+
- β `addDefaultFormats()` β deleted in JUCE 8
|
| 173 |
+
- β `juce::Thread::setCurrentThreadPriority()` β removed, use native pthread
|
| 174 |
+
- β `getParameter(int)` / `setParameter(int, float)` β deprecated
|
| 175 |
+
- β `getStringWidth()` β use `getStringWidthFloat()`
|
| 176 |
+
- β `juce::Rectangle<float>::getTransformToFit()` β does not exist
|
| 177 |
+
- β `juce::Array<CustomStruct>` initializer list β use `std::vector<CustomStruct>`
|
| 178 |
+
|
| 179 |
+
## macOS Gotchas
|
| 180 |
+
|
| 181 |
+
- β `MSG_NOSIGNAL` β Linux only β `signal(SIGPIPE, SIG_IGN)` + `SO_NOSIGPIPE`
|
| 182 |
+
- β Two processes with same `shm_open` name β both crash β scanner must wait for Helper
|
| 183 |
+
- β `addAndMakeVisible(comp)` after `comp.setVisible(false)` β `addAndMakeVisible` forces visible, invisible comp sits on top intercepting all mouse events β use `addChildComponent(comp)` instead
|
| 184 |
+
- β `CAContext` + `CALayerHost` for cross-process GUI β doesn't work in Ableton (see FAILED-APPROACHES #10)
|
| 185 |
+
- β
`posix_spawn` β safe for spawning from plugin process
|
| 186 |
+
- β
`shm_open` / `mmap` β reliable cross-process shared memory
|
| 187 |
+
|
| 188 |
+
## MCP Protocol
|
| 189 |
+
|
| 190 |
+
- Endpoint: `POST http://127.0.0.1:16620/mcp`
|
| 191 |
+
- Health: `GET http://127.0.0.1:16620/health`
|
| 192 |
+
- Wire format: JSON-RPC 2.0
|
| 193 |
+
- Tools: `list_plugins`, `search_param`, `get_params`, `set_params`, `get_analysis`
|
| 194 |
+
|
| 195 |
## What NOT To Do
|
| 196 |
+
|
| 197 |
+
- Don't load plugins in-process without safety scan β crash-prone plugins (iZotope, some Waves) call `abort()` and kill Ableton
|
| 198 |
+
- Don't use `PluginDirectoryScanner` β triggers plugin code on scan β crash
|
| 199 |
+
- Don't spawn Helper --scan while live Helper is running β shared memory conflict β false UNSAFE results
|
| 200 |
+
- Don't use `addAndMakeVisible` on a component that should start hidden β use `addChildComponent`
|
| 201 |
+
- Don't try CAContext/CALayerHost for cross-process GUI β see FAILED-APPROACHES #10
|
|
@@ -171,6 +171,51 @@ v0.3 (Sprint 3): In-process hosting (like SnappySnap)
|
|
| 171 |
|
| 172 |
---
|
| 173 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 174 |
## Revisit Candidates (Future)
|
| 175 |
|
| 176 |
| Problem | Possible Future Solution | Difficulty |
|
|
|
|
| 171 |
|
| 172 |
---
|
| 173 |
|
| 174 |
+
## 10. CAContext + CALayerHost β Cross-Process GUI Embedding (v0.4, 2026-05)
|
| 175 |
+
|
| 176 |
+
**What we tried:**
|
| 177 |
+
After the floating-window fullscreen failure (see #1), we tried the macOS private API for sharing a `CALayer` tree across processes:
|
| 178 |
+
- **Helper (server):** `CAContext contextWithCGSConnection:options:` β wraps the plugin's GUI layer and exposes it as a `uint32_t contextId`
|
| 179 |
+
- **Plugin (client):** `CALayerHost.contextId = contextId` β a `CALayer` subclass that renders the remote context inline in JUCE's NSView
|
| 180 |
+
|
| 181 |
+
The contextId was sent over the existing IPC socket. No Mach port bootstrapping needed.
|
| 182 |
+
|
| 183 |
+
Also fixed a secondary bug: the Helper's NSWindow was never ordered front. Fix was to position it off-screen at `(-32000, -32000)` and call `orderFront:`.
|
| 184 |
+
|
| 185 |
+
**What happened:**
|
| 186 |
+
- Build succeeded, API calls compiled and ran without errors
|
| 187 |
+
- `CALayerHost` was created and added as a sublayer to JUCE's NSView
|
| 188 |
+
- The plugin GUI never appeared inside the PluginBridge editor window
|
| 189 |
+
- Debug showed: `contextId` was valid (non-zero), `CALayerHost` was attached, but nothing rendered
|
| 190 |
+
|
| 191 |
+
**Why it failed:**
|
| 192 |
+
Ableton Live runs inside the macOS app sandbox / process isolation model. `CAContext` cross-process layer sharing requires both processes to be in the same WindowServer session with compatible entitlements. Ableton's process environment appears to block this β the compositor receives the layer tree but doesn't render it into a foreign process's view hierarchy.
|
| 193 |
+
|
| 194 |
+
This is the same fundamental limitation as the floating-window approach: Apple does not provide a reliable, supported API for one process to embed rendering from another process's view hierarchy.
|
| 195 |
+
|
| 196 |
+
**Attempts made:**
|
| 197 |
+
| Attempt | Result |
|
| 198 |
+
|---|---|
|
| 199 |
+
| `CAContext` + off-screen NSWindow + `orderFront:` at (-32000,-32000) | API worked, nothing rendered β |
|
| 200 |
+
| `CALayerHost` added as sublayer to JUCE NSView root layer | Layer hierarchy correct, blank β |
|
| 201 |
+
| Explicit `setNeedsDisplay` / `setNeedsLayout` on CALayerHost | No effect β |
|
| 202 |
+
| Sending contextId immediately after `createEditor()` | Same result β |
|
| 203 |
+
|
| 204 |
+
**Files changed (then reverted):**
|
| 205 |
+
- `Source/Shared/PrivateCA.h` β declared `CAContext`, `CALayerHost`, `CGSMainConnectionID`
|
| 206 |
+
- `Source/Helper/HelperPluginHost.mm` β `showGui()` using `CAContext`
|
| 207 |
+
- `Source/Plugin/PluginBridgeEditor.mm` β `connectRemoteLayer()` using `CALayerHost`
|
| 208 |
+
|
| 209 |
+
**What we did instead:**
|
| 210 |
+
Moved to the **hybrid safety-scan architecture** (v0.5):
|
| 211 |
+
- Plugins that pass a safety scan are loaded **in-process** β their `createEditor()` runs inside PluginBridge's process, producing a standard JUCE `AudioProcessorEditor*` that embeds natively
|
| 212 |
+
- Plugins that fail the scan are loaded only in Helper for audio-only mode (no GUI)
|
| 213 |
+
- No cross-process GUI required at all
|
| 214 |
+
|
| 215 |
+
This trades crash isolation for reliability: safe plugins (99% of the library) get full GUI; only crash-prone plugins (iZotope Ozone, Neutron, a few others) are audio-only.
|
| 216 |
+
|
| 217 |
+
---
|
| 218 |
+
|
| 219 |
## Revisit Candidates (Future)
|
| 220 |
|
| 221 |
| Problem | Possible Future Solution | Difficulty |
|
|
@@ -1,155 +1,202 @@
|
|
| 1 |
-
# Machine Context β
|
| 2 |
> ML Intern (HuggingFace) writes code but CANNOT run it.
|
| 3 |
-
> Claude Code (local) CAN run code
|
| 4 |
>
|
| 5 |
> **RULE:** Before ML Intern writes any platform-specific code,
|
| 6 |
> Claude Code fills this file and pushes it. ML Intern reads it first.
|
| 7 |
|
| 8 |
---
|
| 9 |
|
| 10 |
-
## SYSTEM INFO (
|
| 11 |
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
-
**
|
| 23 |
-
|
| 24 |
-
**SDK version:**
|
| 25 |
-
**Architecture:**
|
| 26 |
-
**CPU:**
|
| 27 |
-
**RAM:**
|
| 28 |
|
| 29 |
---
|
| 30 |
|
| 31 |
-
## CURRENT BUILD STATE (
|
| 32 |
|
| 33 |
-
**Last build:**
|
| 34 |
-
**Result:** β
|
| 35 |
-
**
|
| 36 |
|
| 37 |
---
|
| 38 |
|
| 39 |
-
## RUNTIME STATE (
|
| 40 |
|
| 41 |
-
**What happens when plugin loads:**
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
```
|
| 47 |
|
| 48 |
-
**
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
-
-
|
|
|
|
|
|
|
| 52 |
|
| 53 |
-
|
|
|
|
| 54 |
|
| 55 |
-
|
| 56 |
|
| 57 |
-
##
|
| 58 |
-
```
|
| 59 |
-
[Full compiler error β not just the first line. Include 5 lines of context above and below.]
|
| 60 |
-
[The exact file and line number]
|
| 61 |
-
[What the code is trying to do at that line]
|
| 62 |
-
```
|
| 63 |
|
| 64 |
-
|
| 65 |
-
```
|
| 66 |
-
[Exact behavior: what user sees, what should happen instead]
|
| 67 |
-
[Ableton log output (grep for relevant keywords)]
|
| 68 |
-
[Helper process state (running? crashed? exit code?)]
|
| 69 |
-
[Console output if running Helper manually]
|
| 70 |
-
```
|
| 71 |
|
| 72 |
-
### Logic Errors (it runs but does the wrong thing)
|
| 73 |
```
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
|
|
|
|
|
|
| 78 |
```
|
| 79 |
|
| 80 |
-
|
| 81 |
-
```
|
| 82 |
-
[What Claude Code thinks is wrong β its best guess at root cause]
|
| 83 |
-
[What it already tried that didn't work]
|
| 84 |
-
[What APIs it verified work/don't work]
|
| 85 |
-
[Relevant Apple documentation or JUCE header info it found]
|
| 86 |
-
```
|
| 87 |
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
```
|
| 94 |
|
| 95 |
-
**
|
| 96 |
|
| 97 |
---
|
| 98 |
|
| 99 |
-
## API VERIFICATION
|
| 100 |
-
|
| 101 |
-
When ML Intern says "I want to use [API X]", Claude Code:
|
| 102 |
-
1. Write a minimal standalone test (5-10 lines)
|
| 103 |
-
2. Compile and run it
|
| 104 |
-
3. Report: works / doesn't work / crashes / returns what value
|
| 105 |
|
| 106 |
-
###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
| API | Works? | Notes |
|
| 108 |
|---|---|---|
|
| 109 |
-
| `
|
| 110 |
-
| `
|
| 111 |
-
| `
|
| 112 |
-
| `
|
| 113 |
-
| `
|
| 114 |
-
| `
|
| 115 |
-
| `
|
| 116 |
-
| `
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
|
| 118 |
---
|
| 119 |
|
| 120 |
-
## ABLETON LOG COMMANDS
|
| 121 |
|
| 122 |
```bash
|
| 123 |
-
#
|
| 124 |
-
|
| 125 |
-
|
|
|
|
|
|
|
| 126 |
|
| 127 |
-
# Helper process
|
| 128 |
ps aux | grep PluginBridgeHelper
|
| 129 |
|
| 130 |
-
# Blocklist
|
| 131 |
cat ~/Library/PluginBridge/blocklist.txt 2>/dev/null || echo "(empty)"
|
| 132 |
|
| 133 |
-
#
|
| 134 |
-
|
| 135 |
|
| 136 |
# Test helper manually
|
| 137 |
-
~/Library/Audio/Plug-Ins/VST3/PluginBridge.vst3/Contents/Resources/PluginBridgeHelper.app/Contents/MacOS/PluginBridgeHelper --
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 138 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 139 |
|
| 140 |
---
|
| 141 |
|
| 142 |
## WORKFLOW
|
| 143 |
|
| 144 |
```
|
| 145 |
-
1. ML Intern wants to
|
| 146 |
2. ML Intern asks: "Claude Code β verify [API] works. Fill MACHINE-CONTEXT.md"
|
| 147 |
-
3. Claude Code: writes test, runs it, fills
|
| 148 |
-
4. ML Intern: reads this file, writes code
|
| 149 |
5. ML Intern: pushes code to HF repo
|
| 150 |
-
6. Claude Code: pulls, builds, tests, updates
|
| 151 |
7. If error: Claude Code fills ERROR REPORT section with full diagnostic
|
| 152 |
-
8. ML Intern reads diagnostic, writes fix based on FACTS
|
| 153 |
9. Repeat until working
|
| 154 |
```
|
| 155 |
|
|
@@ -157,11 +204,11 @@ find ~/Library/Audio/Plug-Ins/VST3/PluginBridge.vst3 -name "PluginBridgeHelper"
|
|
| 157 |
|
| 158 |
## NOTES FOR ML INTERN
|
| 159 |
|
| 160 |
-
- **Never assume an API works** β always ask Claude Code to verify first
|
| 161 |
- **Never use deprecated macOS APIs** without Claude Code confirming they still function
|
| 162 |
-
- **Always check Ableton log** after runtime test β silent failures are common
|
| 163 |
- **ObjC++ (.mm) files** β only use if Claude Code confirms the ObjC API is available in current SDK
|
| 164 |
- **Window management on macOS** β always ask Claude Code to test the specific approach (fullscreen is tricky)
|
| 165 |
- **JUCE version matters** β APIs change between JUCE 7 and 8. Claude Code can check `JUCE/modules/` headers
|
| 166 |
- **Never push more than ONE conceptual change at a time** β if it breaks, you know exactly what caused it
|
| 167 |
- **If you don't know why something failed, DON'T GUESS** β ask Claude Code to investigate and fill the error report
|
|
|
|
|
|
| 1 |
+
# Machine Context β Verified System Info + ML Intern Workflow
|
| 2 |
> ML Intern (HuggingFace) writes code but CANNOT run it.
|
| 3 |
+
> Claude Code (local) CAN run code and fills this file with verified facts.
|
| 4 |
>
|
| 5 |
> **RULE:** Before ML Intern writes any platform-specific code,
|
| 6 |
> Claude Code fills this file and pushes it. ML Intern reads it first.
|
| 7 |
|
| 8 |
---
|
| 9 |
|
| 10 |
+
## SYSTEM INFO (Last verified: 2026-05-17)
|
| 11 |
|
| 12 |
+
**Machine:** MacBook M4, 24 GB RAM
|
| 13 |
+
**macOS:** macOS 15.x (Sequoia)
|
| 14 |
+
**Architecture:** arm64 (Apple Silicon)
|
| 15 |
+
**Xcode / CLT:** 2410+
|
| 16 |
+
**Compiler:** `/usr/bin/cc` (Apple Clang)
|
| 17 |
+
**CMake:** 3.28 via pip (β οΈ system CMake 4.x is incompatible with JUCE 8 CMake scripts β do not use)
|
| 18 |
+
|
| 19 |
+
---
|
| 20 |
+
|
| 21 |
+
## KEY PATHS
|
| 22 |
+
|
| 23 |
+
| What | Path |
|
| 24 |
+
|---|---|
|
| 25 |
+
| Home directory | `/Volumes/T7 Shield/Users/Aditya/` (external T7 Shield SSD) |
|
| 26 |
+
| User VST3 plugins | `/Volumes/T7 Shield/Users/Aditya/Library/Audio/Plug-Ins/VST3/` |
|
| 27 |
+
| System VST3 plugins | `/Library/Audio/Plug-Ins/VST3/` |
|
| 28 |
+
| User AU plugins | `/Volumes/T7 Shield/Users/Aditya/Library/Audio/Plug-Ins/Components/` |
|
| 29 |
+
| System AU plugins | `/Library/Audio/Plug-Ins/Components/` |
|
| 30 |
+
| PluginBridge install | `~/Library/Audio/Plug-Ins/VST3/PluginBridge.vst3` |
|
| 31 |
+
| PluginBridge data | `~/Library/PluginBridge/` (safety_db.json, debug.log, blocklist.txt, scan.log) |
|
| 32 |
+
| Build dir | `/Volumes/T7 Shield/Users/Aditya/pluginbridge/build/` |
|
| 33 |
+
| Ableton log | `~/Library/Preferences/Ableton/Live 12.4/Log.txt` |
|
| 34 |
+
| Crash reports | `~/Library/Logs/DiagnosticReports/` |
|
| 35 |
|
| 36 |
+
**Note:** `juce::File::getSpecialLocation(juce::File::userHomeDirectory)` returns the T7 Shield path, not `/Users/Aditya/`.
|
| 37 |
+
Both `/Library/` and `~/Library/` must be checked to find all installed plugins β most plugins install to both.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
---
|
| 40 |
|
| 41 |
+
## CURRENT BUILD STATE (Last updated: 2026-05-17)
|
| 42 |
|
| 43 |
+
**Last build:** 2026-05-17 ~08:30
|
| 44 |
+
**Result:** β
Clean β `[100%] Built target PluginBridge_VST3`
|
| 45 |
+
**Build command:** `cd build && cmake --build . --target PluginBridge_VST3 -- -j4`
|
| 46 |
|
| 47 |
---
|
| 48 |
|
| 49 |
+
## RUNTIME STATE (Last verified: 2026-05-17)
|
| 50 |
|
| 51 |
+
**What happens when plugin loads (SAFE path):**
|
| 52 |
+
1. Picker shows manufacturer tree, user selects plugin
|
| 53 |
+
2. Safety DB checked β if Unknown, Helper --scan runs (waits for live Helper to exit first)
|
| 54 |
+
3. Helper --load spawned for audio routing
|
| 55 |
+
4. Plugin loaded in-process β `createEditor()` β embedded in PluginBridge editor window
|
|
|
|
| 56 |
|
| 57 |
+
**Verified working plugins (with full GUI):**
|
| 58 |
+
- FabFilter Pro-Q 4 (737 params, 761Γ405)
|
| 59 |
+
- Cradle The God Particle (11 params, 666Γ302)
|
| 60 |
+
- Little MicroShift (4 params, AU, 855Γ359)
|
| 61 |
+
- LIMITER / Mastering the Mix (13 params, 855Γ572)
|
| 62 |
|
| 63 |
+
**Audio-only (unsafe) plugins:**
|
| 64 |
+
- iZotope Ozone 12, Neutron 5, RX components β SIGABRT during scan
|
| 65 |
+
- ANIMATE, Gullfoss, ZENOLOGY, Cradle The God Particle AU version
|
| 66 |
|
| 67 |
+
**MCP server:** Running on port 16620, verified working
|
| 68 |
+
**Debug log:** `~/Library/PluginBridge/debug.log` β timestamped per-session
|
| 69 |
|
| 70 |
+
---
|
| 71 |
|
| 72 |
+
## VST3 BUNDLE STRUCTURE ON THIS MACHINE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
|
| 74 |
+
Most third-party VST3 bundles do **NOT** include `moduleinfo.json`. Only `Info.plist` is present:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
|
|
|
|
| 76 |
```
|
| 77 |
+
Plugin.vst3/Contents/
|
| 78 |
+
βββ Info.plist β use this for metadata
|
| 79 |
+
βββ MacOS/
|
| 80 |
+
βββ PkgInfo
|
| 81 |
+
βββ Resources/
|
| 82 |
+
βββ _CodeSignature/
|
| 83 |
```
|
| 84 |
|
| 85 |
+
**Verified Info.plist fields for manufacturer name:**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
|
| 87 |
+
| Field | Example | Use |
|
| 88 |
+
|---|---|---|
|
| 89 |
+
| `NSHumanReadableCopyright` | `"Copyright Β© 2002-2025 FabFilter"` | Strip copyright prefix β `"FabFilter"` β
best source |
|
| 90 |
+
| `CFBundleGetInfoString` | `"FabFilter Pro-Q 4.02, Copyright Β© β¦"` | Before comma β first word β `"FabFilter"` β
|
|
| 91 |
+
| `CFBundleIdentifier` | `"com.fabfilter.Pro-Q.Vst3.4"` | Second segment capitalised β backup |
|
| 92 |
+
| `manufacturer` | `FabF` | 4-char code β useless for display |
|
| 93 |
|
| 94 |
+
**Known bad vendor values:** Pure version strings like `"16.7.33.200"` appear as `"vendor"` in some plugins' moduleinfo.json. Detect with `containsOnly("0123456789.")` and fall back to filename heuristic.
|
| 95 |
|
| 96 |
---
|
| 97 |
|
| 98 |
+
## API VERIFICATION TABLE
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 99 |
|
| 100 |
+
### macOS Private APIs
|
| 101 |
+
| API | Works? | Notes |
|
| 102 |
+
|---|---|---|
|
| 103 |
+
| `CAContext contextWithCGSConnection:options:` | β | Compiles, runs, no error β but rendering never appears in Ableton's window. See FAILED-APPROACHES #10 |
|
| 104 |
+
| `CALayerHost` (remote layer client) | β | Same β layer attached, blank. Ableton process isolation blocks cross-process compositor sharing |
|
| 105 |
+
| `CGSMainConnectionID()` | β
compiles | Returns valid ID but CAContext still doesn't render cross-process |
|
| 106 |
+
| `NSMachBootstrapServer` | β deprecated | Not needed; use IPC socket to transfer contextId as string |
|
| 107 |
+
| `CARemoteLayerServer` | β wrong API | Does NOT share a hidden NSWindow. See FAILED-APPROACHES #10 |
|
| 108 |
+
| `posix_spawn` | β
| Reliable for spawning Helper from plugin process |
|
| 109 |
+
| `shm_open` / `mmap` | β
| Cross-process shared memory for audio β works reliably |
|
| 110 |
+
| `sem_open` | β
| Named semaphores for audio sync β works reliably |
|
| 111 |
+
|
| 112 |
+
### JUCE 8 APIs
|
| 113 |
| API | Works? | Notes |
|
| 114 |
|---|---|---|
|
| 115 |
+
| `formatManager.addFormat(new juce::VST3PluginFormat())` | β
| Explicit registration required |
|
| 116 |
+
| `formatManager.addFormat(new juce::AudioUnitPluginFormat())` | β
| Explicit registration required |
|
| 117 |
+
| `addDefaultFormats()` | β | Deleted in JUCE 8 |
|
| 118 |
+
| `plugin->createEditor()` | β
| Message thread only |
|
| 119 |
+
| `juce::Thread::setCurrentThreadPriority()` | β | Removed β use native pthread |
|
| 120 |
+
| `getStringWidthFloat()` | β
| Use this; `getStringWidth()` deprecated |
|
| 121 |
+
| `getStringWidth()` | β | Deprecated β use `getStringWidthFloat()` |
|
| 122 |
+
| `juce::Rectangle::getTransformToFit()` | β | Does not exist |
|
| 123 |
+
| `juce::Array<CustomStruct>` initializer list | β | Not supported β use `std::vector<CustomStruct>` |
|
| 124 |
+
| `addChildComponent(comp)` | β
| Adds without forcing visible. Use instead of `addAndMakeVisible` when component starts hidden |
|
| 125 |
+
| `addAndMakeVisible(comp)` after `setVisible(false)` | β | `addAndMakeVisible` forces `setVisible(true)` β invisible comp sits on top eating mouse events |
|
| 126 |
+
| `juce::TreeView` + `TreeViewItem` | β
| Working; custom `LookAndFeel` needed for arrow rendering |
|
| 127 |
+
| `juce::File::getSpecialLocation(userHomeDirectory)` | β
| Returns T7 Shield path |
|
| 128 |
+
|
| 129 |
+
### macOS Gotchas
|
| 130 |
+
| Issue | Notes |
|
| 131 |
+
|---|---|
|
| 132 |
+
| `MSG_NOSIGNAL` | Linux only β use `signal(SIGPIPE, SIG_IGN)` + `SO_NOSIGPIPE` |
|
| 133 |
+
| Two processes same `shm_open` name | Both crash β scanner must wait for live Helper to exit first |
|
| 134 |
+
| `NSApplicationActivationPolicyRegular` | Steals focus from DAW β use `Accessory` |
|
| 135 |
+
| `activateIgnoringOtherApps:YES` | Switches away from Ableton β use `orderFrontRegardless` |
|
| 136 |
|
| 137 |
---
|
| 138 |
|
| 139 |
+
## ABLETON LOG COMMANDS
|
| 140 |
|
| 141 |
```bash
|
| 142 |
+
# PluginBridge debug log (live)
|
| 143 |
+
tail -f ~/Library/PluginBridge/debug.log
|
| 144 |
+
|
| 145 |
+
# Crash reports
|
| 146 |
+
ls -lt ~/Library/Logs/DiagnosticReports/ | grep -i ableton | head -5
|
| 147 |
|
| 148 |
+
# Helper process
|
| 149 |
ps aux | grep PluginBridgeHelper
|
| 150 |
|
| 151 |
+
# Blocklist
|
| 152 |
cat ~/Library/PluginBridge/blocklist.txt 2>/dev/null || echo "(empty)"
|
| 153 |
|
| 154 |
+
# Safety DB
|
| 155 |
+
cat ~/Library/PluginBridge/safety_db.json | python3 -m json.tool | head -30
|
| 156 |
|
| 157 |
# Test helper manually
|
| 158 |
+
~/Library/Audio/Plug-Ins/VST3/PluginBridge.vst3/Contents/Resources/PluginBridgeHelper.app/Contents/MacOS/PluginBridgeHelper --scan "/Library/Audio/Plug-Ins/VST3/FabFilter Pro-Q 4.vst3"; echo "EXIT: $?"
|
| 159 |
+
```
|
| 160 |
+
|
| 161 |
+
---
|
| 162 |
+
|
| 163 |
+
## ERROR REPORT TEMPLATE (Claude Code fills when something breaks)
|
| 164 |
+
|
| 165 |
+
### Build Errors
|
| 166 |
```
|
| 167 |
+
[Full compiler error β not just the first line. Include file + line number + 5 lines context]
|
| 168 |
+
[What the code is trying to do at that line]
|
| 169 |
+
```
|
| 170 |
+
|
| 171 |
+
### Runtime Errors
|
| 172 |
+
```
|
| 173 |
+
[Exact behavior: what user sees, what should happen instead]
|
| 174 |
+
[Relevant debug.log lines]
|
| 175 |
+
[Helper process state]
|
| 176 |
+
```
|
| 177 |
+
|
| 178 |
+
### Claude Code's Analysis
|
| 179 |
+
```
|
| 180 |
+
[Root cause hypothesis]
|
| 181 |
+
[What was already tried]
|
| 182 |
+
[Verified API facts]
|
| 183 |
+
```
|
| 184 |
+
|
| 185 |
+
**DO NOT just report "it doesn't work." Always provide the above.**
|
| 186 |
|
| 187 |
---
|
| 188 |
|
| 189 |
## WORKFLOW
|
| 190 |
|
| 191 |
```
|
| 192 |
+
1. ML Intern wants to use [API/approach]
|
| 193 |
2. ML Intern asks: "Claude Code β verify [API] works. Fill MACHINE-CONTEXT.md"
|
| 194 |
+
3. Claude Code: writes test, runs it, fills verified API table, pushes
|
| 195 |
+
4. ML Intern: reads this file, writes code based on FACTS not assumptions
|
| 196 |
5. ML Intern: pushes code to HF repo
|
| 197 |
+
6. Claude Code: pulls, builds, tests, updates BUILD STATE + RUNTIME STATE sections
|
| 198 |
7. If error: Claude Code fills ERROR REPORT section with full diagnostic
|
| 199 |
+
8. ML Intern reads diagnostic, writes fix based on FACTS
|
| 200 |
9. Repeat until working
|
| 201 |
```
|
| 202 |
|
|
|
|
| 204 |
|
| 205 |
## NOTES FOR ML INTERN
|
| 206 |
|
| 207 |
+
- **Never assume an API works** β always ask Claude Code to verify first via this file
|
| 208 |
- **Never use deprecated macOS APIs** without Claude Code confirming they still function
|
|
|
|
| 209 |
- **ObjC++ (.mm) files** β only use if Claude Code confirms the ObjC API is available in current SDK
|
| 210 |
- **Window management on macOS** β always ask Claude Code to test the specific approach (fullscreen is tricky)
|
| 211 |
- **JUCE version matters** β APIs change between JUCE 7 and 8. Claude Code can check `JUCE/modules/` headers
|
| 212 |
- **Never push more than ONE conceptual change at a time** β if it breaks, you know exactly what caused it
|
| 213 |
- **If you don't know why something failed, DON'T GUESS** β ask Claude Code to investigate and fill the error report
|
| 214 |
+
- **CAContext/CALayerHost is a dead end** β already tried, documented in FAILED-APPROACHES #10. Do not revisit.
|
|
@@ -2,90 +2,182 @@
|
|
| 2 |
|
| 3 |
---
|
| 4 |
|
| 5 |
-
## STATUS: v0.
|
| 6 |
|
| 7 |
Build: β
Clean
|
| 8 |
-
GUI:
|
|
|
|
|
|
|
| 9 |
|
| 10 |
---
|
| 11 |
|
| 12 |
-
## WHAT WAS FIXED
|
| 13 |
|
| 14 |
-
###
|
| 15 |
|
| 16 |
-
|
| 17 |
|
| 18 |
-
**
|
| 19 |
-
|
| 20 |
-
-
|
| 21 |
-
-
|
|
|
|
|
|
|
| 22 |
|
| 23 |
-
**
|
| 24 |
-
The window was created but `orderFront:` was never called. Even if CARemoteLayerServer had worked, the WindowServer wouldn't composite a window that isn't ordered front. Fix: window is positioned off-screen at `(-32000, -32000)` and `orderFront:` is called.
|
| 25 |
|
| 26 |
-
|
| 27 |
-
Not needed anymore. The `contextId` (uint32_t) is sent directly over the existing IPC socket as the `layerPortName` string field (decimal number). No Mach port bootstrapping required.
|
| 28 |
|
| 29 |
-
###
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
---
|
| 38 |
|
| 39 |
-
##
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
```
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
```
|
|
|
|
|
|
|
|
|
|
|
|
|
| 55 |
|
| 56 |
-
**
|
| 57 |
-
1. Helper loads plugin β creates editor β embeds NSView in off-screen NSWindow
|
| 58 |
-
2. NSWindow ordered front at (-32000, -32000) β invisible to user, visible to compositor
|
| 59 |
-
3. `CAContext.layer = window.contentView.layer` β exposes the layer tree cross-process
|
| 60 |
-
4. Helper sends `contextId` (uint32_t as decimal string) via `gui_ready` IPC notification
|
| 61 |
-
5. Host creates `CALayerHost(contextId)` β a CALayer that renders the remote context
|
| 62 |
-
6. CALayerHost added as sublayer to JUCE editor's NSView β plugin GUI appears embedded
|
| 63 |
|
| 64 |
---
|
| 65 |
|
| 66 |
-
##
|
| 67 |
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
```
|
| 72 |
|
| 73 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
-
|
| 76 |
-
2. Clear blocklist: `rm -f ~/Library/PluginBridge/blocklist.txt`
|
| 77 |
-
3. Load PluginBridge β select Pro-Q 4 β GUI should appear inside the editor window
|
| 78 |
-
4. Test Kickstart 2 β should NOT crash Ableton (Helper crashes, gets blocklisted)
|
| 79 |
|
| 80 |
-
##
|
| 81 |
|
| 82 |
-
Check Ableton log for contextId and CALayerHost messages:
|
| 83 |
```bash
|
| 84 |
-
|
|
|
|
| 85 |
```
|
| 86 |
|
| 87 |
-
|
| 88 |
-
- `HelperPluginHost: GUI ready β contextId=<number> size=<W>x<H>`
|
| 89 |
-
- `PluginBridgeEditor: CALayerHost connected (contextId=<number> <W>x<H>)`
|
| 90 |
|
| 91 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
|
| 3 |
---
|
| 4 |
|
| 5 |
+
## STATUS: v0.5.2 β Plugin Picker UI + Safety System working (2026-05-17)
|
| 6 |
|
| 7 |
Build: β
Clean
|
| 8 |
+
GUI: β
Verified β Pro-Q 4, God Particle, Little MicroShift all load with full GUI
|
| 9 |
+
Crashes: β
None since fix (last was May 16, pre-fix)
|
| 10 |
+
MCP: β
Running on port 16620
|
| 11 |
|
| 12 |
---
|
| 13 |
|
| 14 |
+
## WHAT WAS FIXED (2026-05-17 session)
|
| 15 |
|
| 16 |
+
### 1. Plugin Picker UI β Ableton-style manufacturer tree
|
| 17 |
|
| 18 |
+
**Problem:** Old flat PopupMenu list was cluttered, no search, no grouping.
|
| 19 |
|
| 20 |
+
**Solution:** Replaced with inline `juce::TreeView` browser:
|
| 21 |
+
- Manufacturer folders with βΆ/βΌ triangle arrows (custom `LookAndFeel::drawTreeviewPlusMinusBox`)
|
| 22 |
+
- Search box that hides tree and shows flat filtered list
|
| 23 |
+
- Safety badges: β safe, `audio only`, `β blocked`
|
| 24 |
+
- Embedded inline in the editor (no CallOutBox) β editor resizes to fit
|
| 25 |
+
- Ableton-matching dark theme (`0xff1e1e1e` bg, `0xff2d6299` selection)
|
| 26 |
|
| 27 |
+
**Files:** `Source/Plugin/PluginPickerComponent.h/.mm` (new), `Source/Plugin/PluginBridgeEditor.h/.mm` (modified)
|
|
|
|
| 28 |
|
| 29 |
+
---
|
|
|
|
| 30 |
|
| 31 |
+
### 2. UI Freeze Bug β `addAndMakeVisible` vs `addChildComponent`
|
| 32 |
+
|
| 33 |
+
**Root cause:**
|
| 34 |
+
```cpp
|
| 35 |
+
// WRONG β addAndMakeVisible internally calls setVisible(true),
|
| 36 |
+
// overriding the prior setVisible(false). The empty searchList
|
| 37 |
+
// sits invisibly on top of the entire TreeView, intercepting all
|
| 38 |
+
// mouse events. Only TextEditor (native macOS input) still worked.
|
| 39 |
+
searchList.setVisible(false);
|
| 40 |
+
addAndMakeVisible(searchList);
|
| 41 |
+
|
| 42 |
+
// CORRECT:
|
| 43 |
+
addChildComponent(searchList); // stays hidden until needed
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
**Symptom:** After picker opened β could scroll/click nothing. Only the search box accepted input. Selecting Pro-Q 4 showed "audio only / unsafe" because the picker was broken, not the safety system.
|
| 47 |
+
|
| 48 |
+
**Fix:** `addChildComponent(searchList)` in `PluginPickerComponent` constructor.
|
| 49 |
+
|
| 50 |
+
**File:** `Source/Plugin/PluginPickerComponent.mm`
|
| 51 |
|
| 52 |
---
|
| 53 |
|
| 54 |
+
### 3. Safety DB Corruption β Background Scan vs Live Helper Shared Memory Conflict
|
| 55 |
+
|
| 56 |
+
**Root cause:**
|
| 57 |
+
Background scanner spawns `PluginBridgeHelper --scan <path>` for each plugin.
|
| 58 |
+
If the main Helper is already running (serving an active plugin), both processes fight over the same shared memory name (`shm_open`) β scan subprocess crashes immediately β every plugin gets marked UNSAFE.
|
| 59 |
|
| 60 |
+
**Fix 1 β Wait loop before each scan:**
|
| 61 |
+
```cpp
|
| 62 |
+
// In startBackgroundScan() loop, before runPluginScan():
|
| 63 |
+
while (helper.isHelperRunning() && scanRunning.load())
|
| 64 |
+
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
| 65 |
+
if (!scanRunning.load()) break;
|
| 66 |
```
|
| 67 |
+
|
| 68 |
+
**Fix 2 β DB recovery:**
|
| 69 |
+
Python script wiped all corrupted UNSAFE entries. Only genuine entries kept
|
| 70 |
+
(PluginBridge itself, which correctly fails its own scan).
|
| 71 |
+
|
| 72 |
+
**File:** `Source/Plugin/PluginBridgeProcessor.cpp`
|
| 73 |
+
|
| 74 |
+
---
|
| 75 |
+
|
| 76 |
+
### 4. Duplicate Plugins in Picker β VST3 + AU + System + User dirs
|
| 77 |
+
|
| 78 |
+
**Root cause:**
|
| 79 |
+
`getInstalledPluginFiles()` scanned all four directories:
|
| 80 |
+
- `/Library/Audio/Plug-Ins/VST3` (system)
|
| 81 |
+
- `~/Library/Audio/Plug-Ins/VST3` (user)
|
| 82 |
+
- `/Library/Audio/Plug-Ins/Components` (system AU)
|
| 83 |
+
- `~/Library/Audio/Plug-Ins/Components` (user AU)
|
| 84 |
+
|
| 85 |
+
FabFilter Pro-Q 4 appeared 4Γ (2 VST3 paths Γ 2 AU paths). Showed as 2 per manufacturer folder.
|
| 86 |
+
|
| 87 |
+
**Fix:** Deduplicate by stem name using `std::set<std::string>`, priority order:
|
| 88 |
```
|
| 89 |
+
system VST3 β user VST3 β system AU β user AU
|
| 90 |
+
```
|
| 91 |
+
First match for a given stem name wins. AU entry is skipped if VST3 already seen.
|
| 92 |
+
System paths preserved because safety DB uses `/Library/` paths.
|
| 93 |
|
| 94 |
+
**File:** `Source/Plugin/PluginBridgeProcessor.cpp`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
---
|
| 97 |
|
| 98 |
+
### 5. Wrong Manufacturer Names β Copyright Strings as Folder Names
|
| 99 |
|
| 100 |
+
**Root cause:**
|
| 101 |
+
`getPluginManufacturer()` read `CFBundleGetInfoString` and extracted everything after the comma:
|
| 102 |
+
`"FabFilter Pro-Q 4.02, Copyright Β© 2002-2025 FabFilter"` β `"Copyright Β© 2002-2025 FabFilter"`
|
| 103 |
+
This full copyright string became the folder name, and different copyright years split one vendor into multiple folders.
|
| 104 |
+
|
| 105 |
+
Some plugins put their version string as the `"vendor"` field in `moduleinfo.json` (e.g. `"16.7.33.200"`).
|
| 106 |
+
|
| 107 |
+
**Fix β New extraction priority:**
|
| 108 |
+
1. `NSHumanReadableCopyright` β `cleanManufacturer()` strips "Copyright Β© YYYY-YYYY " prefix β `"FabFilter"`
|
| 109 |
+
2. `CFBundleGetInfoString` β extract **before** the comma β first word β `"FabFilter"`
|
| 110 |
+
3. `CFBundleIdentifier` β second segment capitalised β `"com.fabfilter.Pro-Q.4"` β `"Fabfilter"`
|
| 111 |
+
4. `moduleinfo.json "vendor"` β cleaned
|
| 112 |
+
5. Filename first word fallback
|
| 113 |
+
|
| 114 |
+
`cleanManufacturer()` also rejects pure version strings (containsOnly digits/dots).
|
| 115 |
+
|
| 116 |
+
**File:** `Source/Plugin/PluginBridgeProcessor.cpp`
|
| 117 |
+
|
| 118 |
+
---
|
| 119 |
+
|
| 120 |
+
## ARCHITECTURE (v0.5.2)
|
| 121 |
+
|
| 122 |
+
```
|
| 123 |
+
PluginBridge.vst3 (inside Ableton)
|
| 124 |
+
βββ PluginBridgeProcessor.cpp β plugin lifecycle, safety check, background scan, MCP
|
| 125 |
+
βββ PluginBridgeEditor.mm β top bar + inline PluginPickerComponent + plugin editor
|
| 126 |
+
βββ PluginPickerComponent.mm β manufacturer TreeView, search, safety badges
|
| 127 |
+
βββ HelperConnection.cpp β Unix socket IPC + shared memory audio routing
|
| 128 |
+
βββ McpServer.cpp β HTTP MCP on port 16620
|
| 129 |
+
βββ PluginSafetyDB.h β JSON-backed safe/unsafe/unknown per plugin
|
| 130 |
+
βββ Blocklist.h β persistent blocklist for crash-prone plugins
|
| 131 |
+
|
| 132 |
+
PluginBridgeHelper.app (separate crash-safe process, bundled in .vst3/Resources/)
|
| 133 |
+
βββ main.cpp β --load <path>, --scan <path>
|
| 134 |
+
βββ HelperPluginHost.mm β loads plugin, audio via shared memory
|
| 135 |
+
βββ HelperIPC.cpp β command/response over Unix socket
|
| 136 |
```
|
| 137 |
|
| 138 |
+
**Load flow for SAFE plugin:**
|
| 139 |
+
1. User opens picker β selects plugin
|
| 140 |
+
2. `loadPlugin()` checks PluginSafetyDB:
|
| 141 |
+
- **Unknown** β spawns `PluginBridgeHelper --scan` (waits for live Helper to exit first)
|
| 142 |
+
- **Safe** β proceed
|
| 143 |
+
- **Unsafe** β load in Helper for audio-only mode
|
| 144 |
+
3. Helper also loads plugin for out-of-process audio routing
|
| 145 |
+
4. Plugin loaded in-process β `createEditor()` β embedded in PluginBridge editor window
|
| 146 |
+
|
| 147 |
+
**Load flow for UNSAFE plugin (audio-only mode):**
|
| 148 |
+
1. Plugin marked unsafe in DB (crashed during scan)
|
| 149 |
+
2. Loaded only in Helper process β audio routing via shared memory
|
| 150 |
+
3. No in-process load β no GUI β `showAudioOnlyUI()` shown instead
|
| 151 |
+
4. MCP still works (183 params etc.) β all parameter control via MCP tools
|
| 152 |
|
| 153 |
+
---
|
|
|
|
|
|
|
|
|
|
| 154 |
|
| 155 |
+
## BUILD
|
| 156 |
|
|
|
|
| 157 |
```bash
|
| 158 |
+
cd "/Volumes/T7 Shield/Users/Aditya/pluginbridge/build"
|
| 159 |
+
cmake --build . --target PluginBridge_VST3 -- -j4
|
| 160 |
```
|
| 161 |
|
| 162 |
+
Auto-installs to `~/Library/Audio/Plug-Ins/VST3/PluginBridge.vst3`
|
|
|
|
|
|
|
| 163 |
|
| 164 |
+
## KEY FILES
|
| 165 |
+
|
| 166 |
+
| File | Role |
|
| 167 |
+
|---|---|
|
| 168 |
+
| `Source/Plugin/PluginPickerComponent.h/.mm` | Inline plugin browser (new in v0.5) |
|
| 169 |
+
| `Source/Plugin/PluginBridgeEditor.h/.mm` | Main editor, picker toggle, plugin embed |
|
| 170 |
+
| `Source/Plugin/PluginBridgeProcessor.cpp` | `getInstalledPluginFiles()`, `getPluginManufacturer()`, `startBackgroundScan()` |
|
| 171 |
+
| `~/Library/PluginBridge/safety_db.json` | Runtime safety cache (safe/unsafe/unknown per path+mtime) |
|
| 172 |
+
| `~/Library/PluginBridge/debug.log` | Timestamped per-session log |
|
| 173 |
+
|
| 174 |
+
## VERIFIED WORKING (2026-05-17)
|
| 175 |
+
|
| 176 |
+
- β
Pro-Q 4 β full GUI (761Γ405)
|
| 177 |
+
- β
Cradle The God Particle β full GUI (666Γ302)
|
| 178 |
+
- β
Little MicroShift β full GUI (855Γ359)
|
| 179 |
+
- β
LIMITER β full GUI (855Γ572)
|
| 180 |
+
- β
Picker tree: manufacturer folders collapse/expand
|
| 181 |
+
- β
Search: filters list, hides tree
|
| 182 |
+
- β
No duplicates in picker
|
| 183 |
+
- β
Manufacturer names clean (FabFilter, Valhalla, etc.)
|