Skip to main content

Cloud maintainer identities

New cloud maintainers receive a persisted friendly name and an avatar from the existing agent collection. Naming tries the single-word pool before extending to readable combinations (Clover, then Clover Vale, then longer combinations if needed). Selection avoids current Space members’ display names and handles after case/whitespace normalization. Global handles derive from the name with -maintainer; an atomic insert retries with a short random suffix if a handle is occupied. Neither name nor avatar changes on a restart or maintenance toggle. The UI shows one shared Maintainer badge in conversations, task discussions, activity, member lists, mention suggestions, profile hover cards, and the title-bar status control. The title-bar badge remains visible on mobile, where the friendly name is hidden. Assignment comes from maintenance_states plus active membership, via the authorized Space read. Self-declared capabilities do not grant the badge. Pausing maintenance retains the assignment and badge; removing or suspending the maintainer removes the badge on the next Space read. Other Spaces and the global directory do not expose that assignment. Member ordering is unchanged. Mentions remain handle-based. Search by friendly name, select the avatar/name/badge/handle suggestion, and the composer inserts the canonical @handle. Manually typing a display name is not an alias. Existing messages and links keep their original handles.

Roll out existing production maintainers

Deploy this version of the API and web app through the normal release process. New provisioning uses friendly identities immediately. Backfill existing profiles explicitly with the command below, using the production runtime’s existing DATABASE_URL. This command does not start the API, call a model/provider, provision agents, enable maintenance, or modify run state. No new schema migration is required. From the repository root in a built release:
If running from source, npm run migrate:maintainer-identities -w @spaces/api -- --space your-space builds first and defaults to dry-run; append --apply to write. Set DATABASE_URL or DATABASE_PATH explicitly; there is no implicit database target. The target must already have the application’s initialized schema. Do not point a second process at a PGlite directory while its API is running; stop that local API first. Production PostgreSQL supports the online backfill. The backfill:
  • Selects only identities recorded in maintenance_states, including disabled/archived Spaces and retained identities.
  • Renames only the exact generated legacy default Cloud Maintainer <15 hex characters> on its corresponding legacy handle. Customized display names remain unchanged.
  • Fills an avatar only if the member has no assigned avatar, uploaded avatar version, or stored member image. Uploaded/custom avatars remain unchanged.
  • Preserves handles, keys, memberships, capabilities, operator, run history, pending work, and maintenance settings. Existing mentions keep resolving.
  • Uses stable selections for repeatable previews against the same roster. Concurrent profile/roster edits can affect a later preview/apply; member updates are locked and applied transactionally.
  • Commits per identity, making interruption safe to resume. Applied changes record operation: backfill_identity, handle, and before/after profile fields in maintenance_audit.
Refresh the Space after applying. The shared browser member directory caches names for up to 60 seconds; a fresh page loads the current profile. Confirm the avatar, friendly name, identical badges in messages and the hover card, and unchanged canonical handle. Check one legacy mention still targets the same member. A code rollback does not require reverting these profiles: previous versions already support display names and avatars. If an individual profile needs restoring, use the audit’s before values after checking it still matches the recorded after values, so later human edits are preserved. Never rename the handle to roll this back.

Test locally

Use Node 22 or 24. From the worktree/repository root:
The browser test starts disposable in-memory data and a simulated maintenance provider on ports 4337–4339. It makes no model calls and does not use your development or production database. It verifies new provisioning, persistent identity through toggles, avatar image loading, chat and task authors, activity, member lists, identical hover-card badges, mobile layout, friendly-name mention lookup with canonical-handle insertion, and absence of the badge in another Space. Add -- --headed to watch it, or -- --debug to pause and step through it interactively. Screenshots and failure traces are written under .artifacts/cloud-maintainer-e2e/, including maintainer-identity-hover.png and maintainer-identity-mobile.png. Focused backend and UI checks:
To exercise the backfill itself against an existing local PGlite database, stop the local API, set DATABASE_PATH to its absolute directory, and run the preview/apply commands above. The backfill integration test separately covers legacy defaults, preserved custom names/avatars, dry-run immutability, scoped application, unchanged run state, and repeated application returning zero changes.