fix(presence): POST /registry/heartbeat keepalive (closes #6, closes molecule-core#24) #7

Merged
claude-ceo-assistant merged 1 commits from fix/6-presence-heartbeat into main 2026-05-07 15:28:54 +00:00

Summary

  • v0.4.0-gitea.1 channel plugin polled /workspaces/:id/activity but never sent /registry/heartbeat. After ~90s the platform's healthsweep (workspace-server internal/registry/healthsweep.go) flipped the workspace back to status='awaiting_agent' even though A2A traffic flowed fine over the long-poll loop.
  • Fix: per-workspace setInterval posting {workspace_id: id} to /registry/heartbeat every 30s (three ticks inside the platform's 90s stale window).
  • New heartbeat.ts pure module + heartbeat.test.ts Bun.serve fixture pin the wire shape (POST + bearer + Origin + body).
  • New env var MOLECULE_HEARTBEAT_INTERVAL_MS (default 30000; set to 0 to disable).
  • Version bump: 0.4.0-gitea.10.4.0-gitea.2 (package.json + plugin.json + marketplace.json).

Why platform-side change skipped

core#24 proposed counting /workspaces/:id/activity GETs as keepalives. That's a deeper semantic change to a hot read endpoint and would mask actual bugs (e.g. a poll-mode workspace where the agent silently died but a stale tail process keeps polling). The plugin-side fix is the smaller blast radius and matches what the legacy server-mode + Python runtime already do. core#24 will be closed as won't-fix with a comment routing to this PR.

Test plan

  • bun test — 18 pass / 0 fail (4 new heartbeat tests).
  • Live verify on Hongming tenant: install v0.4.0-gitea.2, start plugin, observe canvas badge stays on online past 90s.

Closes

  • #6
  • molecule-core#24
## Summary - v0.4.0-gitea.1 channel plugin polled `/workspaces/:id/activity` but never sent `/registry/heartbeat`. After ~90s the platform's `healthsweep` (workspace-server `internal/registry/healthsweep.go`) flipped the workspace back to `status='awaiting_agent'` even though A2A traffic flowed fine over the long-poll loop. - Fix: per-workspace `setInterval` posting `{workspace_id: id}` to `/registry/heartbeat` every 30s (three ticks inside the platform's 90s stale window). - New `heartbeat.ts` pure module + `heartbeat.test.ts` Bun.serve fixture pin the wire shape (POST + bearer + Origin + body). - New env var `MOLECULE_HEARTBEAT_INTERVAL_MS` (default 30000; set to 0 to disable). - Version bump: `0.4.0-gitea.1` → `0.4.0-gitea.2` (package.json + plugin.json + marketplace.json). ## Why platform-side change skipped core#24 proposed counting `/workspaces/:id/activity` GETs as keepalives. That's a deeper semantic change to a hot read endpoint and would mask actual bugs (e.g. a poll-mode workspace where the agent silently died but a stale tail process keeps polling). The plugin-side fix is the smaller blast radius and matches what the legacy server-mode + Python runtime already do. core#24 will be closed as won't-fix with a comment routing to this PR. ## Test plan - [x] `bun test` — 18 pass / 0 fail (4 new heartbeat tests). - [ ] Live verify on Hongming tenant: install v0.4.0-gitea.2, start plugin, observe canvas badge stays on `online` past 90s. ## Closes - #6 - molecule-core#24
claude-ceo-assistant added 1 commit 2026-05-07 15:28:44 +00:00
v0.4.0-gitea.1 polled /workspaces/:id/activity but never sent
/registry/heartbeat. The platform's healthsweep
(workspace-server/internal/registry/healthsweep.go) flipped any
runtime='external' workspace whose last_heartbeat_at was older than
90s back to status='awaiting_agent', so the canvas presence badge
stuck on awaiting_agent within 90s of plugin start even while A2A
traffic flowed fine over the long-poll loop.

Fix: per-workspace heartbeat ticker (default 30s, three ticks inside
the 90s stale window) POSTs the minimal HeartbeatPayload shape
(workspace_id only) — same path the Python runtime in
workspace/heartbeat.py uses when it has nothing else to report.

heartbeat.ts pure module + Bun.serve fixture test pin the wire
shape (POST + bearer + Origin + workspace_id body) so a future
refactor that drops any of those silently re-breaks the badge.

Bump 0.4.0-gitea.1 → 0.4.0-gitea.2 and document
MOLECULE_HEARTBEAT_INTERVAL_MS in README.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
claude-ceo-assistant merged commit 73367764f9 into main 2026-05-07 15:28:54 +00:00
Sign in to join this conversation.
No reviewers
No Label
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: molecule-ai/molecule-mcp-claude-channel#7
No description provided.