Sitar118 Claude Sonnet 4.6 commited on
Commit
dcfc9c9
Β·
1 Parent(s): 553fb05

docs: update all context files for v0.5.2 session (2026-05-17)

Browse files

SYNC.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>

Files changed (4) hide show
  1. CLAUDE.md +178 -103
  2. FAILED-APPROACHES.md +45 -0
  3. MACHINE-CONTEXT.md +144 -97
  4. SYNC.md +148 -56
CLAUDE.md CHANGED
@@ -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 in a **separate crash-safe process**
6
- 2. Exposes ALL hosted plugin parameters via a local MCP server (port 16620)
7
- 3. Audio routes through shared memory (zero added latency)
8
- 4. Any AI that speaks MCP (Claude Code, Codex CLI, Gemini CLI) can control any plugin
 
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
- ## Architecture: OUT-OF-PROCESS (Crash-Safe)
 
 
 
 
 
 
 
17
 
18
  ```
19
- β”Œβ”€ Ableton Process ─────────────────┐ β”Œβ”€ PluginBridgeHelper Process ─────┐
20
- β”‚ β”‚ β”‚ β”‚
21
- β”‚ PluginBridge.vst3 (thin shell) β”‚ β”‚ Standalone executable β”‚
22
- β”‚ β”œβ”€β”€ McpServer (port 16620) β”‚ β”‚ β”œβ”€β”€ Loads third-party plugin β”‚
23
- β”‚ β”œβ”€β”€ HelperConnection β”‚ β”‚ β”œβ”€β”€ processBlock via shared mem β”‚
24
- β”‚ β”‚ β”œβ”€β”€ Shared memory (audio) │◄───►│ β”œβ”€β”€ Shows plugin GUI (floating) β”‚
25
- β”‚ β”‚ β”œβ”€β”€ Semaphores (sync) β”‚ β”‚ └── Handles param get/set β”‚
26
- β”‚ β”‚ └── Unix socket (IPC/cmds) │◄───►│ β”‚
27
- β”‚ └── processBlock(): β”‚ β”‚ If crash β†’ only helper dies β”‚
28
- β”‚ write shm β†’ signal β†’ wait β”‚ β”‚ Ableton stays alive β”‚
29
- β”‚ read output from shm β”‚ β”‚ β”‚
30
- β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  ```
32
 
33
- **Why out-of-process:** Some plugins (Ozone 12, Kickstart 2) call abort() or show NSAlert during loading, which crashes the entire host process. By running in a separate process, crashes are isolated β€” Ableton never dies.
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
- ## MCP Protocol
60
- - Endpoint: `POST http://127.0.0.1:16620/mcp`
61
- - Health: `GET http://127.0.0.1:16620/health`
62
- - Wire format: JSON-RPC 2.0
63
- - 5 tools: `list_plugins`, `search_param`, `get_params`, `set_params`, `get_analysis`
64
-
65
- ## Plugin GUI Behavior
66
- - GUI runs in Helper process as a **floating window** (always-on-top)
67
- - Uses `NSApplicationActivationPolicyAccessory` β€” no Dock icon, no focus steal
68
- - `orderFrontRegardless` β€” shows without switching away from Ableton
69
- - Toggle via "Open GUI" / "Close GUI" button in PluginBridge's panel
70
- - If plugin crashes β†’ GUI disappears, PluginBridge shows "Plugin crashed - click Reload"
71
-
72
- ## Key JUCE 8 APIs (Verified Working)
73
- - `formatManager.addFormat(new juce::VST3PluginFormat())` β€” explicit registration
74
- - `formatManager.addFormat(new juce::AudioUnitPluginFormat())` β€” explicit registration
75
- - `instance->enableAllBuses()` β€” MUST call before using
76
- - `plugin->getParameters()` β†’ `Array<AudioProcessorParameter*>`
77
- - `dynamic_cast<juce::AudioPluginInstance::HostedParameter*>(param)` for stable ID
78
- - `param->beginChangeGesture()` / `setValueNotifyingHost(val)` / `endChangeGesture()`
79
- - `juce_add_gui_app()` for Helper target in CMake
80
-
81
- ## Key JUCE 8 APIs (BROKEN β€” do NOT use)
82
- - ❌ `addDefaultFormats()` β€” deleted
83
- - ❌ `juce::Thread::setCurrentThreadPriority()` β€” removed, use native pthread
84
- - ❌ `getParameter(int)` / `setParameter(int, float)` β€” deprecated, asserts
 
 
 
 
 
 
85
 
86
- ## macOS-Specific Gotchas (Learned the Hard Way)
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 (builds PluginBridge + PluginBridgeHelper)
97
- β”œβ”€β”€ JUCE/ (git submodule)
98
  β”œβ”€β”€ libs/
99
- β”‚ β”œβ”€β”€ httplib.h
100
- β”‚ └── json.hpp
101
  β”œβ”€β”€ Source/
102
- β”‚ β”œβ”€β”€ Plugin/ ← VST3/AU (runs inside Ableton)
103
- β”‚ β”‚ β”œβ”€β”€ PluginBridgeProcessor.h/.cpp
104
- β”‚ β”‚ β”œβ”€β”€ PluginBridgeEditor.h/.cpp
105
- β”‚ β”‚ β”œβ”€β”€ HelperConnection.h/.cpp
106
- β”‚ β”‚ └── McpServer.h/.cpp
107
- β”‚ β”œβ”€β”€ Helper/ ← Standalone exe (separate process)
108
- β”‚ β”‚ β”œβ”€β”€ main.cpp
109
- β”‚ β”‚ β”œβ”€β”€ HelperPluginHost.h/.cpp
 
 
 
 
110
  β”‚ β”‚ └── HelperIPC.h/.cpp
111
- β”‚ └── Shared/ ← Used by both targets
112
- β”‚ β”œβ”€β”€ SharedAudioBuffer.h
113
- β”‚ β”œβ”€β”€ IPCProtocol.h
114
  β”‚ └── Constants.h
115
- β”œβ”€β”€ CLAUDE.md
116
- β”œβ”€β”€ ROADMAP.md
117
- └── SYNC.md
 
 
 
 
 
 
 
 
 
 
118
  ```
119
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  ## What NOT To Do
121
- - Don't load plugins in-process β€” crashes DAW with misbehaving plugins
122
- - Don't use `PluginDirectoryScanner` β€” triggers plugin code execution β†’ crash
123
- - Don't use `addDefaultFormats()` β€” deleted in JUCE 8
124
- - Don't steal focus when showing GUI β€” use Accessory policy
125
- - Don't use `MSG_NOSIGNAL` β€” macOS doesn't have it
126
- - Don't use `juce::Thread::setCurrentThreadPriority()` β€” removed in JUCE 8
 
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
FAILED-APPROACHES.md CHANGED
@@ -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 |
MACHINE-CONTEXT.md CHANGED
@@ -1,155 +1,202 @@
1
- # Machine Context β€” Fill This Before Remote Coding
2
  > ML Intern (HuggingFace) writes code but CANNOT run it.
3
- > Claude Code (local) CAN run code but follows this file for coordination.
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 (Claude Code: fill once, update when OS/Xcode changes)
11
 
12
- ```bash
13
- # Run these and paste output below:
14
- sw_vers
15
- xcodebuild -version
16
- xcrun --show-sdk-version
17
- uname -m
18
- sysctl -n machdep.cpu.brand_string
19
- system_profiler SPMemoryDataType | head -5
20
- ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
 
22
- **macOS version:**
23
- **Xcode version:**
24
- **SDK version:**
25
- **Architecture:**
26
- **CPU:**
27
- **RAM:**
28
 
29
  ---
30
 
31
- ## CURRENT BUILD STATE (Claude Code: update after every build)
32
 
33
- **Last build:** [timestamp]
34
- **Result:** βœ… or ❌
35
- **Errors (if any):**
36
 
37
  ---
38
 
39
- ## RUNTIME STATE (Claude Code: update after every test)
40
 
41
- **What happens when plugin loads:**
42
- **What user sees:**
43
- **Ableton log (relevant lines):**
44
- ```
45
- [paste grep output here]
46
- ```
47
 
48
- **Helper process running:** yes/no
49
- **Helper PID:**
 
 
 
50
 
51
- ---
 
 
52
 
53
- ## ERROR REPORT (Claude Code: fill this when something doesn't work)
 
54
 
55
- When ML Intern's code fails, Claude Code provides ALL of the following:
56
 
57
- ### Build Errors
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
- ### Runtime Errors
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
- [What was expected to happen]
75
- [What actually happened]
76
- [DBG output from the code path (add prints if needed)]
77
- [Variable values at the point of failure]
 
 
78
  ```
79
 
80
- ### Claude Code's Own Analysis
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
- ### Minimal Reproduction
89
- ```
90
- [The smallest code snippet that reproduces the issue]
91
- [Tested independently outside the full project if possible]
92
- [Expected output vs actual output]
93
- ```
94
 
95
- **DO NOT just report "it doesn't work." Always provide the above.**
96
 
97
  ---
98
 
99
- ## API VERIFICATION (Claude Code: test before ML Intern uses any API)
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
- ### Verified APIs:
 
 
 
 
 
 
 
 
 
 
 
 
107
  | API | Works? | Notes |
108
  |---|---|---|
109
- | `CAContext contextWithCGSConnection:options:` | | |
110
- | `CALayerHost` | | |
111
- | `CGSMainConnectionID()` | | |
112
- | `NSMachBootstrapServer registerPort:name:` | | |
113
- | `CARemoteLayerServer sharedServer` | | |
114
- | `posix_spawn` | | |
115
- | `shm_open` / `mmap` | | |
116
- | `sem_open` | | |
 
 
 
 
 
 
 
 
 
 
 
 
 
117
 
118
  ---
119
 
120
- ## ABLETON LOG COMMANDS (Claude Code: run when asked)
121
 
122
  ```bash
123
- # Full plugin bridge log
124
- grep -i "pluginbridge\|helper\|contextId\|CALayerHost\|gui_ready\|layer\|GUI\|loaded\|crashed" \
125
- ~/Library/Preferences/Ableton/Live\ 12.4/Log.txt | tail -30
 
 
126
 
127
- # Helper process check
128
  ps aux | grep PluginBridgeHelper
129
 
130
- # Blocklist content
131
  cat ~/Library/PluginBridge/blocklist.txt 2>/dev/null || echo "(empty)"
132
 
133
- # Find helper binary
134
- find ~/Library/Audio/Plug-Ins/VST3/PluginBridge.vst3 -name "PluginBridgeHelper" -type f
135
 
136
  # Test helper manually
137
- ~/Library/Audio/Plug-Ins/VST3/PluginBridge.vst3/Contents/Resources/PluginBridgeHelper.app/Contents/MacOS/PluginBridgeHelper --test "/Library/Audio/Plug-Ins/VST3/FabFilter Pro-Q 4.vst3"; echo "EXIT: $?"
 
 
 
 
 
 
 
138
  ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
  ---
141
 
142
  ## WORKFLOW
143
 
144
  ```
145
- 1. ML Intern wants to write code using [API/approach]
146
  2. ML Intern asks: "Claude Code β€” verify [API] works. Fill MACHINE-CONTEXT.md"
147
- 3. Claude Code: writes test, runs it, fills this file, pushes to HF repo
148
- 4. ML Intern: reads this file, writes code KNOWING it works on the target machine
149
  5. ML Intern: pushes code to HF repo
150
- 6. Claude Code: pulls, builds, tests, updates this file with results
151
  7. If error: Claude Code fills ERROR REPORT section with full diagnostic
152
- 8. ML Intern reads diagnostic, writes fix based on FACTS not guesses
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.
SYNC.md CHANGED
@@ -2,90 +2,182 @@
2
 
3
  ---
4
 
5
- ## STATUS: v0.4.1 β€” GUI fix pushed (2026-05-17)
6
 
7
  Build: βœ… Clean
8
- GUI: Awaiting first real test in Ableton
 
 
9
 
10
  ---
11
 
12
- ## WHAT WAS FIXED
13
 
14
- ### Root cause: Wrong cross-process layer API + invisible window
15
 
16
- Three bugs in the original v0.4 GUI approach:
17
 
18
- **Bug 1 β€” Wrong API (main bug):**
19
- `CARemoteLayerServer` + `NSMachBootstrapServer` does NOT share a hidden NSWindow's content to another process. The correct private-but-stable API is:
20
- - **Helper (server):** `CAContext contextWithCGSConnection:options:` β€” wraps a `CALayer` and exposes it as a `uint32_t contextId`
21
- - **Host (client):** `CALayerHost.contextId = contextId` β€” a `CALayer` subclass that renders the remote context inline
 
 
22
 
23
- **Bug 2 β€” NSWindow never visible:**
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
- **Bug 3 β€” NSMachBootstrapServer deprecated:**
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
- ### Files changed
30
- | File | What changed |
31
- |---|---|
32
- | `Source/Shared/PrivateCA.h` | **New** β€” declares `CAContext`, `CALayerHost`, `CGSMainConnectionID` |
33
- | `Source/Helper/HelperPluginHost.h` | `remoteLayerServer` β†’ `caContext` |
34
- | `Source/Helper/HelperPluginHost.mm` | `showGui()` uses `CAContext`; `hideGui()` releases properly; window `orderFront:` |
35
- | `Source/Plugin/PluginBridgeEditor.mm` | `connectRemoteLayer()` uses `CALayerHost`; no Mach port lookup |
 
 
 
 
 
 
 
 
 
 
 
 
 
36
 
37
  ---
38
 
39
- ## ARCHITECTURE (v0.4.1)
 
 
 
 
40
 
 
 
 
 
 
 
41
  ```
42
- PluginBridge.vst3 (inside Ableton)
43
- β”œβ”€β”€ PluginBridgeProcessor.cpp β€” spawns Helper, shared memory audio, IPC
44
- β”œβ”€β”€ PluginBridgeEditor.mm β€” displays CALayerHost (contextId from Helper)
45
- β”œβ”€β”€ HelperConnection.cpp β€” Unix socket IPC to Helper
46
- β”œβ”€β”€ McpServer.cpp β€” HTTP MCP on port 16620
47
- └── Blocklist.h β€” persistent blocklist
48
-
49
- PluginBridgeHelper.app (separate crash-safe process)
50
- β”œβ”€β”€ main.cpp β€” JUCE app, --pid argument
51
- β”œβ”€β”€ HelperPluginHost.mm β€” loads plugin, CAContext GUI server
52
- β”œβ”€β”€ HelperIPC.cpp β€” receives commands, sends notifications
53
- └── SharedAudioBuffer.h β€” shared memory
 
 
 
 
 
 
 
 
 
54
  ```
 
 
 
 
55
 
56
- **How GUI works (v0.4.1):**
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
- ## BUILD
67
 
68
- ```bash
69
- cd ~/pluginbridge && git pull
70
- cd build && cmake --build . --config Release 2>&1 | tail -30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  ```
72
 
73
- ## TEST AFTER BUILD
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
- 1. Restart Ableton (plugin is auto-installed to `~/Library/Audio/Plug-Ins/VST3/`)
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
- ## IF GUI STILL DOESN'T SHOW
81
 
82
- Check Ableton log for contextId and CALayerHost messages:
83
  ```bash
84
- grep -i "pluginbridge\|contextId\|CALayerHost\|gui_ready" ~/Library/Preferences/Ableton/Live\ 12.4/Log.txt | tail -30
 
85
  ```
86
 
87
- The log should show:
88
- - `HelperPluginHost: GUI ready β€” contextId=<number> size=<W>x<H>`
89
- - `PluginBridgeEditor: CALayerHost connected (contextId=<number> <W>x<H>)`
90
 
91
- If contextId is 0 or either line is missing β†’ the specific step that's failing is identified.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.)