Skip to content

fix(workspace): make agent removal sticky so removed agents can't reappear (#347)#560

Open
nolanchic wants to merge 1 commit into
openagents-org:developfrom
nolanchic:fix/issue-347-sticky-agent-removal
Open

fix(workspace): make agent removal sticky so removed agents can't reappear (#347)#560
nolanchic wants to merge 1 commit into
openagents-org:developfrom
nolanchic:fix/issue-347-sticky-agent-removal

Conversation

@nolanchic

Copy link
Copy Markdown

Fixes #347

A removed agent reappeared in the workspace sidebar. The online count was correct ("Agents (2/4)"), but the agent list kept showing every agent that had ever been added.

Root cause

POST /v1/remove hard-deleted the WorkspaceMember row, but a still-running agent daemon reconnects and re-POSTs /v1/join, and _handle_agent_join blindly upserted the row back to status='online'. So removal wasn't sticky — the daemon resurrected its own membership, and /v1/discover (which the sidebar renders) listed it again. /v1/profile's online count already filtered status='online', so the count was right while the list was wrong — exactly the reported symptom.

Fix

Make removal a soft-delete (status='removed') and refuse self-resurrection:

  • _handle_agent_remove — mark status='removed' (keep the row) instead of db.delete; exclude removed members when reassigning a channel master.
  • _handle_agent_join — raise EventRejected when a removed agent tries to re-join, so /v1/join 401s instead of upserting the membership back to online.
  • _handle_ping — don't flip a removed member back to online; surface session_revoked so the daemon stops its adapter for that agent.
  • /v1/discover — hide removed members. Offline members are intentionally kept (mobile clients key the "connect an agent" prompt off membership, not online status — pinned by test_discover_keeps_stale_members_as_offline).
  • POST /v1/cloud-agents — re-adding a previously-removed cloud agent reactivates it (refreshes its retained CloudAgentConfig) instead of rejecting with "already exists", so removal is reversible by a human re-invite rather than permanent.

Re-adding a self-joined (token) agent remains intentionally blocked — only a human re-invite (cloud-agents) clears the removed state, which is what makes removal sticky against a rogue/reconnecting daemon.

Test plan

  • pytest tests/test_agent_removal.py — 6/6 new tests pass: removed agent excluded from discover; re-join refused (401) and doesn't resurrect; heartbeat doesn't resurrect; removed row retained as status='removed'; offline member still listed (back-compat); cloud re-add reactivates.
  • pytest workspace/backend/tests/ — no new regressions. 20 tests fail on this machine both on develop and with this branch (browser-contexts, event-routing auth, migration-schema) — they're pre-existing local-env failures (SQLAlchemy/alembic/SQLite version drift), unrelated to this change; verified identical before/after via git stash diff.
  • ruff — the changed source files introduce zero new violations (rule counts equal-or-fewer than develop); the new test file is ruff-clean.
  • Manual: connect an agent to a workspace, remove it from the sidebar, and confirm it disappears and stays gone (no reappearance on the next discover poll) even while its daemon is still running; re-add it via "add cloud agent" and confirm it returns.

…ppear

/v1/remove hard-deleted the WorkspaceMember, but a still-running agent daemon
reconnects and re-POSTs /v1/join, and _handle_agent_join blindly upserted the
row back to status='online' — so a removed agent reappeared in /v1/discover
(and thus the workspace sidebar). The discover list showed every member while
the online count stayed correct, matching the reported "Agents (2/4)" symptom.

Make removal a soft-delete (status='removed') and refuse self-resurrection:
- _handle_agent_remove: mark status='removed' (keep the row) instead of
  deleting; exclude removed members when reassigning a channel master.
- _handle_agent_join: raise EventRejected when a removed agent tries to
  re-join, so /v1/join 401s instead of resurrecting the membership.
- _handle_ping: don't flip a removed member back to online; surface
  session_revoked so the daemon stops its adapter.
- /v1/discover: hide removed members. Offline members are intentionally kept
  — mobile clients key the "connect an agent" prompt off membership, not
  online status.
- POST /v1/cloud-agents: re-adding a previously-removed cloud agent
  reactivates it (refreshes its retained config) instead of rejecting with
  "already exists".

Closes openagents-org#347
@vercel

vercel Bot commented Jul 10, 2026

Copy link
Copy Markdown

@nolanchic is attempting to deploy a commit to the Raphael's projects Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Removed agent still appears in workspace sidebar

1 participant