refactor: extract resolveActiveRole() as single source of status→role mapping

resolveCursorKey and resolveChannel both duplicated the same
taskStatus→role switch. Now resolveActiveRole() is the single
mapping, and both functions derive from it.
This commit is contained in:
Eyejoker
2026-03-30 22:58:00 +09:00
parent a91776b067
commit 14d70f345a
2 changed files with 27 additions and 30 deletions

View File

@@ -39,28 +39,30 @@ export function pairedCursorKey(
return isReviewerTurn ? `${chatJid}:reviewer` : chatJid;
}
/**
* Auto-resolve the cursor key based on current paired task status.
* Non-paired rooms always return chatJid.
* Paired rooms return role-specific keys so owner/reviewer/arbiter
* cursors are independent.
*/
/** Map task status to the active role. */
export function resolveActiveRole(
taskStatus?: string | null,
): 'owner' | 'reviewer' | 'arbiter' {
switch (taskStatus) {
case 'review_ready':
case 'in_review':
return 'reviewer';
case 'arbiter_requested':
case 'in_arbitration':
return 'arbiter';
default:
return 'owner';
}
}
/** Cursor key for a role. Owner uses chatJid, others use chatJid:role. */
export function resolveCursorKey(
chatJid: string,
taskStatus?: string | null,
): string {
if (!isPairedRoomJid(chatJid)) return chatJid;
if (!taskStatus) return chatJid;
switch (taskStatus) {
case 'review_ready':
case 'in_review':
return `${chatJid}:reviewer`;
case 'arbiter_requested':
case 'in_arbitration':
return `${chatJid}:arbiter`;
default:
return chatJid;
}
const role = resolveActiveRole(taskStatus);
return role === 'owner' ? chatJid : `${chatJid}:${role}`;
}
export function createImplicitContinuationTracker(idleTimeout: number) {

View File

@@ -41,6 +41,7 @@ import { isTriggerAllowed, loadSenderAllowlist } from './sender-allowlist.js';
import {
advanceLastAgentCursor,
createImplicitContinuationTracker,
resolveActiveRole,
resolveCursorKey,
filterLoopingPairedBotMessages,
getProcessableMessages,
@@ -536,18 +537,13 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
const arbiterChannel = foundArbiterChannel || channel;
// Resolve the correct Discord channel for a given task status.
const resolveChannel = (taskStatus?: string | null): Channel => {
switch (taskStatus) {
case 'review_ready':
case 'in_review':
return reviewerChannel;
case 'arbiter_requested':
case 'in_arbitration':
return arbiterChannel;
default:
return channel;
}
const roleToChannel: Record<string, Channel> = {
owner: channel,
reviewer: reviewerChannel,
arbiter: arbiterChannel,
};
const resolveChannel = (taskStatus?: string | null): Channel =>
roleToChannel[resolveActiveRole(taskStatus)] ?? channel;
if (isPairedRoomJid(chatJid)) {
logger.info(
@@ -832,8 +828,7 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): {
// Arbiter turns use a dedicated context prompt; regular turns use formatted messages.
const isArbiterTurn =
taskStatus === 'arbiter_requested' ||
taskStatus === 'in_arbitration';
taskStatus === 'arbiter_requested' || taskStatus === 'in_arbitration';
let prompt: string;
if (isArbiterTurn && pendingTaskForChannel) {
const arbiterMsgs = labelPairedSenders(