LearnNewsExamplesServices
Frontmatter
id10443
titleBridge daemon's `meta.appName || 'Claude'` silent fallback masks sub-config bugs
stateClosed
labels
bugaiarchitecture
assigneesneo-gemini-pro
createdAtApr 27, 2026, 9:46 PM
updatedAtJun 7, 2026, 7:22 PM
githubUrlhttps://github.com/neomjs/neo/issues/10443
authorneo-opus-ada
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtApr 28, 2026, 1:26 PM

Bridge daemon's meta.appName || 'Claude' silent fallback masks sub-config bugs

Closed v13.0.0/archive-v13-0-0-chunk-6 bugaiarchitecture
neo-opus-ada
neo-opus-ada commented on Apr 27, 2026, 9:46 PM

Context

Surfaced empirically during post-#10441 wake-substrate routing diagnosis (2026-04-27, session c68a7d4b). When WAKE_SUB:b3d1179c-... was created with empty harnessTargetMetadata: {} (per the parallel #10442 ticket on update action's overwrite behavior), wake delivery should have failed visibly. Instead it succeeded — but routed to the wrong harness.

The empirical pattern:

  1. Wake event for @neo-gemini-pro fires through bridge daemon
  2. Bridge daemon reads sub's harnessTargetMetadata{} (empty)
  3. bridge-daemon.mjs:476: const appName = meta.appName || 'Claude' → defaults to 'Claude'
  4. Bridge daemon dispatches osascript -e 'tell application "Claude" to activate' → activates @tobiu's Claude desktop
  5. Wake payload [WAKE] 1 events for @neo-gemini-pro: ... lands on the wrong harness
  6. Bridge daemon log shows Delivered ... via osascript to Claude — looks like success

The wake didn't reach Antigravity (Gemini's intended harness). The misroute went undetected until inspection of bridge.log's to <appName> field revealed it.

The Problem

The current fallback semantics are dangerously permissive:

// ai/scripts/bridge-daemon.mjs:476 (paraphrased)
const appName = meta.appName || 'Claude';

When meta.appName is missing/empty/null, the daemon silently defaults to 'Claude'. Three problems:

  1. Severity tier-up over the prior 'Cursor' bug: before #10440, Gemini's sub had appName: 'Cursor' which failed visibly (osascript exit 1 — easy to diagnose). Now the empty-metadata case routes to 'Claude' silently — looks like success but isn't reaching the intended recipient.

  2. Misroute to the host's Claude desktop is operationally noisy. @tobiu is the human commander running both harnesses on one machine; misrouted wakes inject text into HIS active Claude session, polluting cross-agent context.

  3. The default mass effect: 'Claude' as fallback is host-specific (Claude Desktop must be installed). For agents with neither Claude nor a configured appName, the failure mode is osascript exit 1 (visible). For Tobi's host (Claude installed), the misroute is silent. Same broken sub, different observable behavior depending on host config — exactly the property that masks bugs.

The Architectural Reality

  • ai/scripts/bridge-daemon.mjs:466-510 (osascript adapter, exact line per current dev)
  • ai/scripts/bridge-daemon.mjs:476: the offending fallback meta.appName || 'Claude'
  • ai/graph/identityRoots.mjs — canonical source for subscriptionTemplate.harnessTargetMetadata.appName
  • ai/mcp/server/memory-core/services/WakeSubscriptionService.mjs:bootstrap() — copies metadata from template to sub at subscribe-time

The Phase 3 wake substrate explicitly designed harnessTargetMetadata.appName as a required, identity-template-derived field per #10402's footgun-closure scope. The fallback || 'Claude' is a footgun that survives the Phase 3 fix because the template-driven path always provides a value — until something (like #10442's update bug) leaves the field empty.

The Fix

Replace the silent fallback with a fail-fast + observable path:

const appName = meta.appName;
if (!appName) {
    writeLog('ERROR',
        `[Bridge Daemon] Cannot deliver subscription ${subscription.id}: ` +
        `harnessTargetMetadata.appName is missing/empty. ` +
        `Skipping delivery to avoid misrouting. ` +
        `Verify subscription template via 'manage_wake_subscription({action: 'list'})' or fix the AgentIdentity subscriptionTemplate.`
    );
    return; // skip delivery; log surfaces the misconfiguration
}

Properties of the fix:

  • Fail-fast: missing appName → no delivery attempt, no silent misroute
  • Observable: bridge.log (per #10425) captures the skip reason with the offending sub-id + corrective hint
  • Diagnostic-friendly: the agent investigating later sees exactly which sub is misconfigured
  • No regression on configured subs: valid meta.appName (the dominant path post-#10402) still delivers normally

Acceptance Criteria

  • bridge-daemon.mjs removes the || 'Claude' fallback
  • Empty/missing meta.appName triggers an ERROR-level log line + return (no delivery attempt)
  • Log line includes: subscription ID, the missing-appName diagnosis, suggested corrective action
  • Existing bridge-daemon.spec.mjs tests still pass (no regression on the configured-appName path)
  • New regression test asserting empty-metadata subs result in a logged skip + no osascript invocation
  • Post-merge validation: create a WAKE_SUB with empty harnessTargetMetadata; trigger a wake event; verify bridge.log shows the ERROR skip line + no misroute to host's Claude

Out of Scope

  • Migration of existing empty-metadata subs (the parallel #10442 fix on update action is the canonical-source fix; this ticket addresses the daemon-side defense)
  • A retry-with-different-app-name strategy (introduces magic; the agent should fix the sub config explicitly)
  • Validating appName against a known-good list (e.g., ['Claude', 'Antigravity', 'Cursor']) — out of scope; let the OS-level osascript exit 1 handle unknown apps; this ticket focuses on the missing-field case only
  • Equivalent fail-fast for missing harnessTarget (likely already handled higher up; verify but don't expand scope unless gap surfaces)

Avoided Traps

  • Trap: Replace fallback with a configurable env var for the default. Avoided: the substrate already provides identity-template-derived metadata as the canonical source; layering a host-specific env var fallback would fragment the contract.
  • Trap: Add a "smart" detection (e.g., probe running apps to guess the right one). Avoided: wake substrate routing is a contract, not a heuristic. Misconfiguration should fail visibly; agents have manage_wake_subscription to fix it.
  • Trap: Keep the fallback but log a warning. Avoided: delivery still misroutes; the log line might not be checked. Skipping the delivery entirely is the substrate-grounded fail-fast that drives diagnostic visibility.

Related

  • Empirical anchor: post-#10441 routing diagnosis where WAKE_SUB:b3d1179c (Gemini's empty-metadata sub) misrouted to host's Claude
  • Parallel root cause: #10442 (update action's overwrite-vs-merge semantics that produced the empty metadata)
  • Persistent log substrate that captured the misroute: #10419 / PR #10425
  • Original footgun-closure scope: #10402 (Phase 1 wake substrate self-healing — addressed the appName template path; this addresses the missing-template fallback)

Origin Session ID: c68a7d4b-909a-4965-9bf9-116906d271a3

Retrieval Hint: "bridge daemon silent fallback Claude default empty harnessTargetMetadata appName misroute fail-fast skip-delivery"

tobiu referenced in commit c7e9999 - "feat(bridge-daemon): fail-fast on empty appName metadata (#10443) (#10446) on Apr 28, 2026, 1:26 PM
tobiu closed this issue on Apr 28, 2026, 1:26 PM