Summary
resolveIrcHostFromConfig in memory/plugins/turn-context/src/entity-resolver.ts (added in #525, closing #522) skips the account-specific host lookup whenever the supplied accountId equals channels.irc.defaultAccount, falling back straight to the top-level channels.irc.host instead.
export function resolveIrcHostFromConfig(accountId?: string, config?: Record<string, unknown>): string | undefined {
const cfg = (config ?? readOpenClawConfig()) as OpenClawConfigShape | null | undefined;
const irc = cfg?.channels?.irc;
if (!irc) return undefined;
// If an explicit account id is provided, prefer its host, then fall back to top-level.
if (accountId && accountId !== irc.defaultAccount) {
const accountHost = irc.accounts?.[accountId]?.host?.trim();
if (accountHost) return accountHost;
}
return irc.host?.trim();
}
Repro
Given:
{
"channels": {
"irc": {
"host": "irc.stale-legacy.net",
"defaultAccount": "primary",
"accounts": {
"primary": { "host": "irc.primary-correct.net" },
"secondary": { "host": "irc.libera.chat" }
}
}
}
}
resolveIrcHostFromConfig("primary", config) returns "irc.stale-legacy.net" (the stale top-level value) instead of "irc.primary-correct.net" (the account-specific value), because accountId ("primary") === irc.defaultAccount ("primary") short-circuits the account lookup entirely.
Verified locally against the PR #525 branch (commit 2a98674) with a standalone regression test — deepEqual assertion fails, returning the stale host.
Why this is wrong
This contradicts the account-merge precedence used everywhere else in the codebase for IRC config (resolveMergedAccountConfig / mergeAccountConfig in src/channels/plugins/account-helpers.ts, used by extensions/irc/src/accounts.ts): account-specific config always overrides channel-level defaults, unconditionally, regardless of whether the accountId happens to match defaultAccount. defaultAccount only determines which accountId is selected when the caller omits one — it should have no bearing on precedence once an accountId is already known.
Impact
- Current production is not affected. The live
channels.irc config has no accounts map and no defaultAccount set, so this branch cannot be reached today.
- Future risk: any multi-account IRC setup that sets
defaultAccount explicitly (matching one of the accounts keys) with a differing account-level host will silently resolve entity identities against the wrong network string, producing <wrong-network>/<nick> composite keys that never match production irc_username facts. Silent failure — no crash, just no entity resolution.
Suggested fix
Drop the accountId !== irc.defaultAccount guard entirely:
export function resolveIrcHostFromConfig(accountId?: string, config?: Record<string, unknown>): string | undefined {
const cfg = (config ?? readOpenClawConfig()) as OpenClawConfigShape | null | undefined;
const irc = cfg?.channels?.irc;
if (!irc) return undefined;
if (accountId) {
const accountHost = irc.accounts?.[accountId]?.host?.trim();
if (accountHost) return accountHost;
}
return irc.host?.trim();
}
Add a regression test asserting that an explicit defaultAccount matching the passed accountId still prefers accounts[accountId].host over a differing top-level host.
Origin
Found during Gem's step-6 re-verification desk review of PR #525 (SE Run #499). Filed as a non-blocking follow-up per DEFECT_MANAGEMENT triage (S3/P2 — no production impact today, narrow future-facing edge case, workaround is simply not setting defaultAccount + differing per-account host until fixed).
Summary
resolveIrcHostFromConfiginmemory/plugins/turn-context/src/entity-resolver.ts(added in #525, closing #522) skips the account-specific host lookup whenever the suppliedaccountIdequalschannels.irc.defaultAccount, falling back straight to the top-levelchannels.irc.hostinstead.Repro
Given:
{ "channels": { "irc": { "host": "irc.stale-legacy.net", "defaultAccount": "primary", "accounts": { "primary": { "host": "irc.primary-correct.net" }, "secondary": { "host": "irc.libera.chat" } } } } }resolveIrcHostFromConfig("primary", config)returns"irc.stale-legacy.net"(the stale top-level value) instead of"irc.primary-correct.net"(the account-specific value), becauseaccountId ("primary") === irc.defaultAccount ("primary")short-circuits the account lookup entirely.Verified locally against the PR #525 branch (commit 2a98674) with a standalone regression test —
deepEqualassertion fails, returning the stale host.Why this is wrong
This contradicts the account-merge precedence used everywhere else in the codebase for IRC config (
resolveMergedAccountConfig/mergeAccountConfiginsrc/channels/plugins/account-helpers.ts, used byextensions/irc/src/accounts.ts): account-specific config always overrides channel-level defaults, unconditionally, regardless of whether the accountId happens to matchdefaultAccount.defaultAccountonly determines which accountId is selected when the caller omits one — it should have no bearing on precedence once an accountId is already known.Impact
channels.ircconfig has noaccountsmap and nodefaultAccountset, so this branch cannot be reached today.defaultAccountexplicitly (matching one of theaccountskeys) with a differing account-levelhostwill silently resolve entity identities against the wrong network string, producing<wrong-network>/<nick>composite keys that never match productionirc_usernamefacts. Silent failure — no crash, just no entity resolution.Suggested fix
Drop the
accountId !== irc.defaultAccountguard entirely:Add a regression test asserting that an explicit
defaultAccountmatching the passedaccountIdstill prefersaccounts[accountId].hostover a differing top-levelhost.Origin
Found during Gem's step-6 re-verification desk review of PR #525 (SE Run #499). Filed as a non-blocking follow-up per DEFECT_MANAGEMENT triage (S3/P2 — no production impact today, narrow future-facing edge case, workaround is simply not setting
defaultAccount+ differing per-account host until fixed).