feat(task-manager): track published pub/sub topics and expose via API (#329)
Description
There was no easy way to see which events are published over pub/sub in a Juice instance — information needed, for example, to configure the parameter monitor. This MR makes the task manager track every unique pub/sub topic it sees, with a last-seen timestamp, and exposes that list through a task and a client helper.
Topics are observed at the single choke point all events pass through — the task manager's ZMQProxy — rather than instrumenting each publisher (which would miss other services) or running a duplicate subscriber socket (redundant with the proxy).
Closes #329 (closed)
Changes
Core
ZMQProxygains an optional, failure-safeon_message(topic_frame)hook; the proxy stays generic with no task-manager coupling.- New
TopicRegistry: in-memorydict[str, str]that decodes/dedupes topics and records a UTC ISO-8601 last-seen timestamp. Retained for the task-manager process lifetime. - New
GetEventTopicListtask; registered on the task manager to return the snapshot.
Client API
- New
get_active_topics()helper —from orangeqs.juice.client import get_active_topics(deferred imports avoid a client:left_right_arrow:task_manager cycle).
Usage
client.execute("task-manager", GetEventTopicList())
# or, the higher-level helper:
from orangeqs.juice.client import get_active_topics
get_active_topics()
# {"TaskEvent": "2026-06-05T10:30:00+00:00",
# "IPythonServiceState.my-service": "2026-06-05T10:31:12+00:00"}Acceptance criteria
- Tracks all unique pub/sub topics (captured at the proxy)
- Last-seen timestamp per topic
- Deduplicated (dict keys)
- Retained for the task-manager process lifetime (in-memory)
-
GetEventTopicListcallable viaclient.execute(...) - Bonus:
get_active_topics()high-level helper
Testing
- New unit tests:
TopicRegistry(dedup/strip/snapshot-copy) and the proxyon_messagecallback. ruff check+ruff formatclean;pyrightintroduces zero new errors over the baseline.pytest tests/juice/task_manager tests/juice/messaging→ 41 passed, 1 pre-existing skip.
Notes
The actual routing target is "task-manager" (hyphen) — the issue's "task_manager" is informal; get_active_topics() uses the correct name.