fix(audio): gate the executor's chain-plan startAudio behind the user-stop latch

Second field finding: a user-stopped engine came back when a song load
ran a rig_builder chain plan with startAudio:true - the executor calls
native startAudio directly in the main process, bypassing the IPC latch
check entirely (plan 6.1's second writer).

Decision (strict 8.3): only the device screen's user start resumes.
lease-bridge gains gateNativeAudio(), a proxy over the native accessor
that suppresses startAudio while latched (log-once telemetry line);
audio-bridge hands the executor the gated accessor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
OmikronApex
2026-07-15 01:37:34 +02:00
co-authored by Claude Fable 5
parent bb0f79e0be
commit a142b0f3de
3 changed files with 57 additions and 1 deletions
+28
View File
@@ -47,6 +47,10 @@ export type LeaseBridge = {
// keep-alive watchdog, any unmigrated caller) must be suppressed — only
// an explicit user start clears it.
isUserStopLatched(): boolean;
// Wrap a native-audio accessor so its startAudio honors the user-stop
// latch — for main-process callers that bypass the IPC handlers (the
// audio-effects executor's `startAudio: true` chain plans, plan 6.1).
gateNativeAudio<T extends Record<string, (...args: any[]) => any>>(getAudio: () => T | null): () => T | null;
// true = swallow the raw disarm because a demand holder still needs
// detection armed (the 6.3 "last disarmer kills a concurrent consumer" fix).
shouldIgnoreRawDetectionDisarm(): boolean;
@@ -249,6 +253,30 @@ export function initLeaseBridge(getAudio: () => AudioModule, options: { broadcas
return userStopLatch;
},
gateNativeAudio(getAudioFn) {
return () => {
const target = getAudioFn();
if (!target) return null;
return new Proxy(target, {
get(t, prop, receiver) {
if (prop === 'startAudio') {
return (...args: unknown[]) => {
if (userStopLatch) {
// Strict §8.3: only the device screen's
// user start resumes — a chain plan's
// startAudio waits it out.
console.info('[leases] executor startAudio suppressed — user stop latched (plan §8.3)');
return undefined;
}
return t.startAudio?.(...args);
};
}
return Reflect.get(t, prop, receiver);
},
});
};
},
shouldIgnoreRawDetectionDisarm() {
return anyDetectionActive();
},