diff --git a/apps/web/src/bench/grants-tab.tsx b/apps/web/src/bench/grants-tab.tsx index d76fde299..abf841d50 100644 --- a/apps/web/src/bench/grants-tab.tsx +++ b/apps/web/src/bench/grants-tab.tsx @@ -24,11 +24,14 @@ function resourceName(resource: string): string { if (!resource.startsWith(TOOL_PREFIX)) { return GRANT_RESOURCE_LABEL[resource as GrantResource] ?? resource; } - const words = resource + const text = resource .slice(TOOL_PREFIX.length) - .replace(/[_.-]+/g, " ") - .trim(); - return words.charAt(0).toUpperCase() + words.slice(1); + .split(/[_.\-\s]+/) + .filter( + (word, index, all) => word !== "" && word.toLowerCase() !== all[index - 1]?.toLowerCase(), + ) + .join(" "); + return text.charAt(0).toUpperCase() + text.slice(1); } /** This workbench's own grants; they never inherit from the workspace. */ @@ -40,14 +43,15 @@ export function GrantsTab({ readonly participants: readonly WorkbenchParticipant[]; }) { const names = useGrantNames(workbenchTenantId); - const who = (grant: Grant): string => { + const who = (grant: Grant): string | undefined => { if (grant.roleName !== undefined && grant.roleName !== null) return grant.roleName; const known = participants.find((p) => p.id === grant.principalId); if (known !== undefined) return known.name; if (grant.principalName === undefined || grant.principalName === null) return "Everyone here"; const named = names.replaceTenantIds(grant.principalName); - // A raw run id is never shown, and never turned into words. - return principalLabel(named).raw === null ? named : "A worker"; + // A raw run id is never shown, and never turned into words; with no + // resolvable name the row has no subtitle. + return principalLabel(named).raw === null ? named : undefined; }; const queryClient = useQueryClient(); const query = toAPIQuery( @@ -81,30 +85,38 @@ export function GrantsTab({

No grants here yet.

) : ( ) } diff --git a/apps/web/src/chat/avatar.tsx b/apps/web/src/chat/avatar.tsx index 951a143e3..3f46103c6 100644 --- a/apps/web/src/chat/avatar.tsx +++ b/apps/web/src/chat/avatar.tsx @@ -154,8 +154,10 @@ export function WorkbenchAvatarStack({ return ; } -// Color hashes off principal id, never name — DESIGN.md's avatar identity -// rule. +// A person's color hashes off principal id, never name — DESIGN.md's avatar +// identity rule. A worker keeps the agent fill: each bench deploys its own +// copy with a new principal id, so hashing it would recolor one worker per +// bench. export function IdentityAvatar({ kind, name, @@ -171,7 +173,7 @@ export function IdentityAvatar({ diff --git a/apps/web/src/pages/workers-page.tsx b/apps/web/src/pages/workers-page.tsx index 1463f22ef..381cb0829 100644 --- a/apps/web/src/pages/workers-page.tsx +++ b/apps/web/src/pages/workers-page.tsx @@ -180,8 +180,12 @@ export function WorkersRosterList({ ) : null} - - {(byWorker.get(agent.id) ?? []).map((bench) => bench.name).join(", ")} + + {(byWorker.get(agent.id) ?? []).map((bench) => ( + + {bench.name} + + ))} {agent.liveAddress === null ? "" : "Now"}