# `PhoenixKitProjects.PubSub`
[🔗](https://github.com/BeamLabEU/phoenix_kit_projects/blob/v0.14.0/lib/phoenix_kit_projects/pub_sub.ex#L1)

Real-time updates for the projects module, backed by
`PhoenixKit.PubSub.Manager`.

## Topics

  * `"projects:all"` — any project/template/task/assignment mutation
  * `"projects:tasks"` — task library mutations
  * `"projects:templates"` — template project mutations
  * `"projects:project:<uuid>"` — updates scoped to one project

## Events

Messages are `{:projects, event_atom, payload_map}` tuples.

## Tenant scoping (deferred)

Topics are global today. PhoenixKit core does not currently expose
a per-tenant `Scope.organization_id` or equivalent, and no other
feature module (`locations`, `staff`, `sync`, …) does multi-tenant
PubSub partitioning either — so this is a framework-wide gap, not
a projects-specific one.

When core grows tenant scoping, the right shape here is to thread
the org/tenant key into every topic
(`"projects:org:<org_id>:all"`, etc.) and have `subscribe/1` raise
if the scope is missing. Until then, the `:project:<uuid>` topic
is the only one safe against cross-tenant fan-out (you need the
UUID to subscribe), and the `:all` / `:tasks` / `:templates`
topics deliberately broadcast across all subscribers.

# `broadcast_assignment`

```elixir
@spec broadcast_assignment(atom(), map()) :: :ok | {:error, term()}
```

Broadcasts an assignment event to the all-projects and the parent project's topic.

# `broadcast_dependency`

```elixir
@spec broadcast_dependency(atom(), map()) :: :ok | {:error, term()}
```

Broadcasts a dependency event to the all-projects and the parent project's topic.

# `broadcast_embed`

```elixir
@spec broadcast_embed(String.t(), atom(), map()) :: :ok
```

Broadcasts an emit-mode UI-intent event to a host-supplied topic.

Used by `PhoenixKitProjects.Web.Helpers` when an embedded LV is in
emit mode (`session["mode"] = "emit"`). The host topic is registered
by whoever mounts `PhoenixKitProjects.Web.PopupHostLive` (or any
custom host that subscribes directly). Unlike the other broadcast
helpers in this module, the topic is NOT one of the canonical
`projects:*` namespaces — it's caller-supplied and opaque to us.

UI-intent verbs (`:opened` / `:closed` / `:saved` / `:deleted`) are
deliberately distinct from the content verbs above
(`:project_created` etc.) so `handle_info` clauses subscribed to
the canonical topics never collide with handlers subscribed to a
host topic.

# `broadcast_project`

```elixir
@spec broadcast_project(atom(), map()) :: :ok | {:error, term()}
```

Broadcasts a project event. Templates also fan out to the templates topic.

# `broadcast_task`

```elixir
@spec broadcast_task(atom(), map()) :: :ok | {:error, term()}
```

Broadcasts a task-library event to the all-projects and tasks topics.

# `subscribe`

```elixir
@spec subscribe(String.t()) :: :ok | {:error, term()}
```

Subscribes the calling process to the given PubSub topic.

# `topic_all`

```elixir
@spec topic_all() :: String.t()
```

Topic for any project, template, task, or assignment mutation.

# `topic_project`

```elixir
@spec topic_project(binary()) :: String.t()
```

Topic scoped to a single project.

# `topic_tasks`

```elixir
@spec topic_tasks() :: String.t()
```

Topic for task-library mutations.

# `topic_templates`

```elixir
@spec topic_templates() :: String.t()
```

Topic for template-project mutations.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
