Skip to content
This repository was archived by the owner on Jun 7, 2026. It is now read-only.
This repository was archived by the owner on Jun 7, 2026. It is now read-only.

shell.annotations.onChanged: Event Bus consumer in os-sdk #32

Description

@timkicker

Goal

Implement the shell.annotations.onChanged({ target, namespace, handle }) API per foundation §419. Apps subscribe to annotation changes by target+namespace and receive callbacks when matching events flow through the Event Bus.

Background

Sprint that closed sdk#15 landed set / get / clear for shell.annotations, but onChanged was deferred because os-sdk has only a producer-side Event Bus interface (UnixEventEmitter). Subscribing requires a consumer-side counterpart.

Scope

  1. os-sdk Event Bus consumer — new module sdk/os-sdk/src/event_consumer.rs:

    • EventConsumer trait + UnixEventConsumer impl
    • Connects to the consumer socket (/run/lunaris/event-bus-consumer.sock), registers with a stable consumer id derived from app_id, subscribes to event-type prefixes
    • Returns an async-stream-ish or callback-based interface
    • Mirror MockEventEmitter with MockEventConsumer for tests
  2. Annotations::on_changed in os-sdk:

    • Spawns a consumer subscription filtered to app.annotation.set and app.annotation.cleared
    • Filters by target + namespace client-side (cheap; the daemon broadcasts all matching apps' events)
    • Returns a handle that drops the subscription when dropped (RAII)
  3. tauri-plugin-shell: shell_annotation_subscribe Tauri command that wires onChanged callback through Tauri events (lunaris://annotation-changed)

  4. TS surface: shell.annotations.onChanged({ target, namespace }, handle) returns an unsubscribe function

Out of scope

  • Cross-namespace subscription rate-limiting (defer to capability-token Phase 3.2-full)
  • onChanged for non-annotation event types (presence/timeline) — those have no defined onChanged in foundation

Acceptance

  • App calls shell.annotations.onChanged; another app calls shell.annotations.set with matching target+namespace; first app's handler fires within ~100ms
  • Drop / unsubscribe correctly tears down the subscription (no event leak in tests)
  • Tests cover: matching event fires, non-matching event ignored, drop unsubscribes

Phase

Phase 6 follow-up. Discovered during sdk#15 sprint as deferred scope.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions