diff --git a/extensions/qa-channel/src/channel-actions.test.ts b/extensions/qa-channel/src/channel-actions.test.ts index 86850f8fc915..cf81e6ea4d5b 100644 --- a/extensions/qa-channel/src/channel-actions.test.ts +++ b/extensions/qa-channel/src/channel-actions.test.ts @@ -74,10 +74,12 @@ describe("qa-channel direct message actions", () => { const threadPayload = extractToolPayload(threadResult) as { thread: { id: string; title: string }; target: string; + threadId: string; }; expect(threadPayload.thread.id).toMatch(/^thread-/); expect(threadPayload.thread.title).toBe("QA thread"); - expect(threadPayload.target).toContain(threadPayload.thread.id); + expect(threadPayload.target).toBe("channel:qa-room"); + expect(threadPayload.threadId).toBe(threadPayload.thread.id); const replyResult = await handleAction({ channel: "qa-channel", @@ -86,6 +88,7 @@ describe("qa-channel direct message actions", () => { accountId: "default", params: { target: threadPayload.target, + threadId: threadPayload.threadId, message: "thread reply", text: "ignored legacy reply", }, @@ -111,6 +114,7 @@ describe("qa-channel direct message actions", () => { accountId: "default", params: { to: threadPayload.target, + threadId: threadPayload.threadId, messageId: outbound.id, emoji: "white_check_mark", }, @@ -123,6 +127,7 @@ describe("qa-channel direct message actions", () => { accountId: "default", params: { target: threadPayload.target, + threadId: threadPayload.threadId, messageId: outbound.id, message: "message (edited)", text: "ignored legacy edit", @@ -136,6 +141,7 @@ describe("qa-channel direct message actions", () => { accountId: "default", params: { to: threadPayload.target, + threadId: threadPayload.threadId, messageId: outbound.id, }, }); @@ -165,6 +171,7 @@ describe("qa-channel direct message actions", () => { accountId: "default", params: { to: threadPayload.target, + threadId: threadPayload.threadId, messageId: outbound.id, }, }); @@ -188,7 +195,7 @@ describe("qa-channel direct message actions", () => { cfg, accountId: "default", params: { - target: "channel:canonical-room", + target: "channel:canonical/room", to: "channel:legacy-to-room", channelId: "legacy-channel-id-room", threadName: "Canonical target thread", @@ -197,10 +204,12 @@ describe("qa-channel direct message actions", () => { const threadPayload = extractToolPayload(threadResult) as { thread: { id: string; conversationId: string }; target: string; + threadId: string; }; - expect(threadPayload.thread.conversationId).toBe("canonical-room"); - expect(threadPayload.target).toBe(`thread:canonical-room/${threadPayload.thread.id}`); + expect(threadPayload.thread.conversationId).toBe("canonical/room"); + expect(threadPayload.target).toBe("channel:canonical/room"); + expect(threadPayload.threadId).toBe(threadPayload.thread.id); const replyResult = await handleAction({ channel: "qa-channel", @@ -209,13 +218,14 @@ describe("qa-channel direct message actions", () => { accountId: "default", params: { target: threadPayload.target, + threadId: threadPayload.threadId, channelId: "legacy-reply-room", message: "canonical target reply", }, }); expect(extractToolPayload(replyResult)).toMatchObject({ message: { - conversation: { id: "canonical-room", kind: "channel" }, + conversation: { id: "canonical/room", kind: "channel" }, text: "canonical target reply", threadId: threadPayload.thread.id, }, @@ -246,6 +256,7 @@ describe("qa-channel direct message actions", () => { const threadPayload = extractToolPayload(threadResult) as { thread: { id: string; title: string }; target: string; + threadId: string; }; expect(threadPayload.thread.title).toBe("Legacy thread"); @@ -279,6 +290,7 @@ describe("qa-channel direct message actions", () => { accountId: "default", params: { to: threadPayload.target, + threadId: threadPayload.threadId, messageId: outbound.id, text: "legacy edit", }, diff --git a/extensions/qa-channel/src/channel-actions.ts b/extensions/qa-channel/src/channel-actions.ts index 847dd208126d..c2baca8cf507 100644 --- a/extensions/qa-channel/src/channel-actions.ts +++ b/extensions/qa-channel/src/channel-actions.ts @@ -214,7 +214,11 @@ export const qaChannelMessageActions: ChannelMessageActionAdapter = { }); return jsonResult({ thread, - target: `thread:${target.conversationId}/${thread.id}`, + target: buildQaTarget({ + chatType: target.conversationKind, + conversationId: target.conversationId, + }), + threadId: thread.id, }); } case "thread-reply": { diff --git a/extensions/qa-lab/src/self-check-scenario.ts b/extensions/qa-lab/src/self-check-scenario.ts index f039d1e2f7dd..3c4801479745 100644 --- a/extensions/qa-lab/src/self-check-scenario.ts +++ b/extensions/qa-lab/src/self-check-scenario.ts @@ -7,6 +7,7 @@ export function createQaSelfCheckScenario(options?: { }): QaScenarioDefinition { const waitTimeoutMs = options?.waitTimeoutMs ?? 5_000; let lifecycleTarget: string | undefined; + let lifecycleThreadId: string | undefined; return { name: "Synthetic Slack-class roundtrip", steps: [ @@ -39,12 +40,13 @@ export function createQaSelfCheckScenario(options?: { }); const threadPayload = extractQaToolPayload( threadResult as Parameters[0], - ) as { target?: string; thread?: { id?: string } } | undefined; - const threadId = threadPayload?.thread?.id; - if (!threadId || !threadPayload?.target) { + ) as { target?: string; threadId?: string; thread?: { id?: string } } | undefined; + const threadId = threadPayload?.threadId; + if (!threadId || threadId !== threadPayload?.thread?.id || !threadPayload.target) { throw new Error("thread-create did not return thread id and target"); } lifecycleTarget = threadPayload.target; + lifecycleThreadId = threadId; await state.addInboundMessage({ conversation: { id: "qa-room", kind: "channel", title: "QA Room" }, @@ -78,12 +80,13 @@ export function createQaSelfCheckScenario(options?: { if (!outboundMessage) { throw new Error("threaded outbound message not found"); } - if (!lifecycleTarget) { + if (!lifecycleTarget || !lifecycleThreadId) { throw new Error("thread target not found"); } await performAction("react", { to: lifecycleTarget, + threadId: lifecycleThreadId, messageId: outboundMessage.id, emoji: "white_check_mark", }); @@ -97,6 +100,7 @@ export function createQaSelfCheckScenario(options?: { await performAction("edit", { to: lifecycleTarget, + threadId: lifecycleThreadId, messageId: outboundMessage.id, text: "qa-echo: inside thread (edited)", }); @@ -110,6 +114,7 @@ export function createQaSelfCheckScenario(options?: { await performAction("delete", { to: lifecycleTarget, + threadId: lifecycleThreadId, messageId: outboundMessage.id, }); const deleted = await state.readMessage({ messageId: outboundMessage.id }); diff --git a/extensions/qa-lab/src/self-check.test.ts b/extensions/qa-lab/src/self-check.test.ts index 1aff1be8e461..d886d0550ad7 100644 --- a/extensions/qa-lab/src/self-check.test.ts +++ b/extensions/qa-lab/src/self-check.test.ts @@ -93,7 +93,8 @@ describe("createQaSelfCheckScenario", () => { if (action === "thread-create") { return { details: { - target: "thread:qa-room/thread-1", + target: "channel:qa-room", + threadId: "thread-1", thread: { id: "thread-1" }, }, }; @@ -120,11 +121,7 @@ describe("createQaSelfCheckScenario", () => { await threadStep.run({ state: testState, performAction }); await lifecycleStep.run({ state: testState, performAction }); - expect(targets).toEqual([ - "thread:qa-room/thread-1", - "thread:qa-room/thread-1", - "thread:qa-room/thread-1", - ]); + expect(targets).toEqual(["channel:qa-room", "channel:qa-room", "channel:qa-room"]); const deletedMessage = state.getSnapshot().messages.find((message) => message.deleted); if (!deletedMessage) { throw new Error("self-check did not preserve its deleted message tombstone");