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 existingDATABASE_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:
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 inmaintenance_audit.
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:-- --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:
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.