diff --git a/apps/decodex-app/Sources/DecodexApp/AccountPanelView.swift b/apps/decodex-app/Sources/DecodexApp/AccountPanelView.swift index 2716401a1..825c746ff 100644 --- a/apps/decodex-app/Sources/DecodexApp/AccountPanelView.swift +++ b/apps/decodex-app/Sources/DecodexApp/AccountPanelView.swift @@ -480,7 +480,8 @@ struct AccountPanelView: View { if let selector = control.accountSelector, !selector.isEmpty { if emailsHidden { - let value = account(matching: selector)?.panelDisplayName(emailsHidden: true) ?? "Account" + let value = account(matching: selector)?.panelDisplayName(emailsHidden: true) + ?? AccountDisplay.alias(forIdentity: selector) return "To \(value)" } @@ -1116,7 +1117,6 @@ private enum AccountPrivacy { private enum AccountDisplay { static let randomNames = [ - "Alex", "Avery", "Bailey", "Blake", diff --git a/apps/decodex/src/accounts.rs b/apps/decodex/src/accounts.rs index a516f940f..50524b435 100644 --- a/apps/decodex/src/accounts.rs +++ b/apps/decodex/src/accounts.rs @@ -21,11 +21,11 @@ use crate::{ }; const ACCOUNT_RANDOM_NAMES: &[&str] = &[ - "Alex", "Avery", "Bailey", "Blake", "Casey", "Charlie", "Clara", "Dana", "Drew", "Eden", - "Elliot", "Emery", "Evan", "Finley", "Harper", "Hayden", "Iris", "Jamie", "Jordan", "Kai", - "Kendall", "Lane", "Liam", "Logan", "Mason", "Maya", "Mia", "Morgan", "Noah", "Nora", "Owen", - "Paige", "Parker", "Quinn", "Reese", "Remy", "Riley", "Rowan", "Sage", "Sasha", "Sidney", - "Taylor", "Theo", "Val", + "Avery", "Bailey", "Blake", "Casey", "Charlie", "Clara", "Dana", "Drew", "Eden", "Elliot", + "Emery", "Evan", "Finley", "Harper", "Hayden", "Iris", "Jamie", "Jordan", "Kai", "Kendall", + "Lane", "Liam", "Logan", "Mason", "Maya", "Mia", "Morgan", "Noah", "Nora", "Owen", "Paige", + "Parker", "Quinn", "Reese", "Remy", "Riley", "Rowan", "Sage", "Sasha", "Sidney", "Taylor", + "Theo", "Val", ]; pub(crate) struct AccountLoginRequest { diff --git a/apps/decodex/src/orchestrator/operator_dashboard.html b/apps/decodex/src/orchestrator/operator_dashboard.html index cd430e217..d75cc6239 100644 --- a/apps/decodex/src/orchestrator/operator_dashboard.html +++ b/apps/decodex/src/orchestrator/operator_dashboard.html @@ -3441,7 +3441,6 @@

Run History

const ACCOUNT_IDENTITY_EDGE_CHARS = 6; const ACCOUNT_IDENTITY_MIN_EDGE_CHARS = 3; const ACCOUNT_RANDOM_NAMES = [ - "Alex", "Avery", "Bailey", "Blake", @@ -4480,6 +4479,12 @@

Run History

: []; } + function configuredDashboardAccounts(snapshot) { + return Array.isArray(snapshot?.accounts) + ? [...accountApiAccounts(), ...snapshot.accounts.filter(Boolean)] + : accountApiAccounts(); + } + function noteAccountApiSnapshot(response) { if (!response || !Array.isArray(response.accounts)) { return false; @@ -5753,9 +5758,7 @@

Run History

return null; } - const accounts = Array.isArray(snapshot.accounts) - ? snapshot.accounts.filter(Boolean) - : []; + const accounts = configuredDashboardAccounts(snapshot); if (!accounts.length) { return null; } @@ -5842,6 +5845,52 @@

Run History

); } + function configuredCodexAccountFor(account, snapshot) { + const identity = codexAccountIdentity(account); + const email = codexAccountEmail(account); + const fingerprint = codexAccountFingerprint(account); + if (!identity && !email && !fingerprint) { + return null; + } + + return ( + configuredDashboardAccounts(snapshot).find( + (candidate) => + (identity && codexAccountIdentity(candidate) === identity) || + (email && codexAccountEmail(candidate) === email) || + (fingerprint && codexAccountFingerprint(candidate) === fingerprint), + ) || null + ); + } + + function codexAccountDisplaySource(account, snapshot) { + const configured = configuredCodexAccountFor(account, snapshot); + if (!configured) { + return account; + } + + const merged = { ...configured, ...account }; + if (!String(merged.random_name || "").trim()) { + merged.random_name = configured.random_name; + } + if (!String(merged.random_name_key || "").trim()) { + merged.random_name_key = configured.random_name_key; + } + const accountOffset = account?.random_name_offset; + const accountHasOffset = + accountOffset != null && + !(typeof accountOffset === "string" && !accountOffset.trim()) && + Number.isInteger(Number(accountOffset)); + if ( + !accountHasOffset && + Number.isInteger(Number(configured.random_name_offset)) + ) { + merged.random_name_offset = configured.random_name_offset; + } + + return merged; + } + function accountSelectionConfirmationKey(action, selector) { return `${String(action || "").trim()}:${String(selector || "").trim()}`; } @@ -6138,6 +6187,17 @@

Run History

: codexAccountDisplayName(account); } + function codexAccountFallbackName(value) { + const selector = String(value || "").trim(); + if (!selector) { + return "account"; + } + + return accountEmailsHidden + ? codexAccountRandomName({ account_fingerprint: selector }) + : compactAccountIdentity(selector); + } + function codexAccountPlanLabel(account) { return account?.plan_type ? humanizeToken(account.plan_type) : "-"; } @@ -6638,7 +6698,7 @@

Run History

return `Fixed · ${label}`; } - return `Fixed · ${accountEmailsHidden ? "account" : compactAccountIdentity(selector)}`; + return `Fixed · ${codexAccountFallbackName(selector)}`; } function renderAccountModeControl(snapshot) { @@ -6678,9 +6738,10 @@

Run History

`; } - const displayTitle = codexAccountDisplayTitle(account); - const visibleName = codexAccountVisibleName(account); - const identityClass = codexAccountShowsEmail(account) ? " is-machine" : ""; + const displayAccount = codexAccountDisplaySource(account, snapshot); + const displayTitle = codexAccountDisplayTitle(displayAccount); + const visibleName = codexAccountVisibleName(displayAccount); + const identityClass = codexAccountShowsEmail(displayAccount) ? " is-machine" : ""; const pendingTitle = capturedAccount ? displayTitle : `${displayTitle || visibleName} · run account capture pending`; @@ -6718,9 +6779,7 @@

Run History

function codexAccountPoolAccounts(snapshot) { const accountsByIdentity = new Map(); - const configuredAccounts = Array.isArray(snapshot?.accounts) - ? [...accountApiAccounts(), ...snapshot.accounts.filter(Boolean)] - : accountApiAccounts(); + const configuredAccounts = configuredDashboardAccounts(snapshot); const runs = [ ...(snapshot?.active_runs ?? []).map((run) => [run, true]), ...(snapshot?.recent_runs ?? []).map((run) => [run, false]), @@ -7468,7 +7527,17 @@

Run History

function runIssueTitle(run, derived) { const issueRecord = runIssueRecord(run, derived); - return run.title || run.issue_title || issueRecord?.title || humanizeToken(run.current_operation || run.phase); + const operationFallback = humanizeToken(run.current_operation || run.phase); + const fallback = issueDisplayKey(run); + + for (const value of [run.title, run.issue_title, issueRecord?.title]) { + const title = String(value || "").trim(); + if (title && !(fallback !== "unknown" && title === operationFallback)) { + return title; + } + } + + return fallback !== "unknown" ? fallback : operationFallback; } function runHealthText(run) { @@ -9312,10 +9381,25 @@

${escapeHtml(worktree.branch_name)}

merged[key] = snapshotRun[key]; } } + if ( + dashboardRunTitleIsOperationFallback(activityRun) && + dashboardRunFieldHasValue(snapshotRun.title) + ) { + merged.title = snapshotRun.title; + } return merged; } + function dashboardRunTitleIsOperationFallback(run) { + const title = String(run?.title || "").trim(); + if (!title || issueDisplayKey(run) === "unknown") { + return false; + } + + return title === humanizeToken(run?.current_operation || run?.phase); + } + function dashboardRunIdentityKeys(run) { const keys = run?.run_id ? [`run:${run.run_id}`] : []; diff --git a/apps/decodex/src/orchestrator/tests/operator/status/dashboard.rs b/apps/decodex/src/orchestrator/tests/operator/status/dashboard.rs index eb4f439c7..a27746754 100644 --- a/apps/decodex/src/orchestrator/tests/operator/status/dashboard.rs +++ b/apps/decodex/src/orchestrator/tests/operator/status/dashboard.rs @@ -532,9 +532,11 @@ fn operator_dashboard_account_privacy_controls_use_compact_identities() { assert!(response.contains("function loadAccountNameOffsets()")); assert!(response.contains("function persistAccountPrivacy(hidden)")); assert!(response.contains("function persistAccountNameOffsets()")); + assert!(response.contains("function configuredDashboardAccounts(snapshot)")); assert!(response.contains("function renderAccountPrivacyToggle()")); assert!(response.contains("function codexAccountRandomNameKey(account)")); assert!(response.contains("function codexAccountRandomNameOffset(account)")); + assert!(response.contains("function codexAccountDisplaySource(account, snapshot)")); assert!(response.contains("function renderCodexAccountRandomNameButton(account)")); assert!(response.contains("function codexAccountShowsEmail(account)")); assert!(response.contains("function codexAccountPrivacyLabel(account)")); @@ -551,12 +553,14 @@ fn operator_dashboard_account_privacy_controls_use_compact_identities() { assert!(response.contains(": codexAccountVisibleName(account);")); assert!(response.contains("return \"Balanced\";")); assert!(response.contains("return `Fixed · ${label}`;")); - assert!(response.contains("return `Fixed · ${accountEmailsHidden ? \"account\" : compactAccountIdentity(selector)}`;")); + assert!(response.contains("function codexAccountFallbackName(value)")); + assert!(response.contains("return `Fixed · ${codexAccountFallbackName(selector)}`;")); assert!(response.contains("const title = codexAccountControlStatusLabel(snapshot);")); assert!(!response.contains("const title = `Mode ${modeLabel}`;")); assert!(response.contains("account-name-reroll")); assert!(response.contains("data-account-name-reroll")); assert!(response.contains("aria-label=\"Change account name\"")); + assert!(!response.contains("\"Alex\"")); assert!(response.contains("return `${local.slice(0, 3)}...${local.slice(-3)}${domain}`;")); assert!(response.contains("return ACCOUNT_RANDOM_NAMES[index];")); assert!(response.contains("return status === \"selected\" ? 1 : 0;")); @@ -1513,6 +1517,10 @@ fn operator_dashboard_run_activity_preserves_snapshot_detail_fields() { assert!(response.contains("function mergeDashboardRunRecord(snapshotRun, activityRun)")); assert!(response.contains("function mergeDashboardActiveRuns(snapshot, activeRunRows)")); + assert!(response.contains("function dashboardRunTitleIsOperationFallback(run)")); + assert!(response.contains("const operationFallback = humanizeToken(run.current_operation || run.phase);")); + assert!(response.contains("!(fallback !== \"unknown\" && title === operationFallback)")); + assert!(response.contains("return fallback !== \"unknown\" ? fallback : operationFallback;")); assert!(response.contains("let dashboardLiveActiveRuns = [];")); assert!(response.contains("let dashboardLiveAccounts = null;")); assert!(response.contains("let dashboardLiveAccountControl = null;")); @@ -1528,6 +1536,8 @@ fn operator_dashboard_run_activity_preserves_snapshot_detail_fields() { assert!(response.contains("\"protocol_activity\"")); assert!(response.contains("!dashboardRunFieldHasValue(activityRun[key])")); assert!(response.contains("merged[key] = snapshotRun[key];")); + assert!(response.contains("dashboardRunTitleIsOperationFallback(activityRun)")); + assert!(response.contains("merged.title = snapshotRun.title;")); assert!( response.contains("const mergedActiveRuns = mergeDashboardActiveRuns(snapshot, activeRunRows);") ); diff --git a/apps/decodex/src/orchestrator/tests/operator/status/http.rs b/apps/decodex/src/orchestrator/tests/operator/status/http.rs index ea649a09b..cd5396265 100644 --- a/apps/decodex/src/orchestrator/tests/operator/status/http.rs +++ b/apps/decodex/src/orchestrator/tests/operator/status/http.rs @@ -1413,7 +1413,7 @@ fn operator_state_endpoint_persists_account_random_name_offset() { assert_eq!(data["accounts"][0]["random_name_offset"], 1); assert_eq!(data["accounts"][0]["random_name_key"], "df65f796"); - assert_eq!(data["accounts"][0]["random_name"], "Logan"); + assert_eq!(data["accounts"][0]["random_name"], "Taylor"); assert!( fs::read_to_string(accounts_dir.join("config.toml")) .expect("global config should read")