refactor(chat): route browser chat through one runtime (P3 Slice-Zero Task 5) (#1172)
ci/woodpecker/push/publish Pipeline failed

Co-authored-by: shaggy <[email protected]>
This commit was merged in pull request #1172.
This commit is contained in:
2026-08-12 20:11:12 +00:00
committed by mos-dt-0
parent 6a8ce66702
commit 216cd72226
33 changed files with 7209 additions and 672 deletions
@@ -417,7 +417,7 @@ describe('ConversationsController — search endpoint', () => {
},
];
brain = createMockBrain({ searchResults });
controller = new ConversationsController(brain as never);
controller = new ConversationsController(brain as never, { runtimeMode: 'legacy' });
});
it('returns matching messages for a valid search query', async () => {
@@ -479,7 +479,7 @@ describe('ConversationsController — search endpoint', () => {
describe('ConversationsController — message CRUD', () => {
it('listMessages returns 404 when conversation is not owned by user', async () => {
const brain = createMockBrain({ conversation: undefined });
const controller = new ConversationsController(brain as never);
const controller = new ConversationsController(brain as never, { runtimeMode: 'legacy' });
await expect(controller.listMessages(CONV_ID, { id: USER_ID })).rejects.toBeInstanceOf(
NotFoundException,
@@ -489,7 +489,7 @@ describe('ConversationsController — message CRUD', () => {
it('listMessages returns the messages for an owned conversation', async () => {
const msgs = [makeMessage('user', 'Test message'), makeMessage('assistant', 'Test reply')];
const brain = createMockBrain({ conversation: makeConversation(), messages: msgs });
const controller = new ConversationsController(brain as never);
const controller = new ConversationsController(brain as never, { runtimeMode: 'legacy' });
const result = await controller.listMessages(CONV_ID, { id: USER_ID });
@@ -500,7 +500,7 @@ describe('ConversationsController — message CRUD', () => {
it('addMessage returns the persisted message', async () => {
const brain = createMockBrain({ conversation: makeConversation() });
const controller = new ConversationsController(brain as never);
const controller = new ConversationsController(brain as never, { runtimeMode: 'legacy' });
const result = await controller.addMessage(
CONV_ID,
@@ -35,6 +35,25 @@ function payload(content: string, messageId: string, correlationId: string): Dis
};
}
/**
* The chat runtime router must never be exercised on the Discord approval/stop control paths —
* those paths run entirely through the command-authorization, runtime-provider and durable-session
* dependencies. Placed in the gateway's chat-runtime-router slot (the former direct `AgentService`
* slot) so any accidental chat-runtime dispatch throws loudly instead of silently passing. Because
* approval/stop never resolve a chat runtime, this fixture is never triggered and the integration
* stays a GREEN cross-surface control.
*/
function failIfUsedChatRuntimeRouter() {
return {
onModuleInit: () => {
throw new Error('chat runtime router must not initialise on the Discord control path');
},
get active(): never {
throw new Error('chat runtime must not be resolved on the Discord approval/stop path');
},
};
}
function authorization(): CommandAuthorizationService {
const entries = new Map<string, string>();
return new CommandAuthorizationService(
@@ -113,7 +132,7 @@ describe('interaction Discord/CLI durable-session integration', () => {
},
);
const gateway = new ChatGateway(
{} as never,
failIfUsedChatRuntimeRouter() as never,
{} as never,
{} as never,
{} as never,
@@ -60,7 +60,7 @@ describe('Resource ownership checks', () => {
// The repo enforces ownership via the WHERE clause; it returns undefined when the
// conversation does not belong to the requesting user.
brain.conversations.findById.mockResolvedValue(undefined);
const controller = new ConversationsController(brain as never);
const controller = new ConversationsController(brain as never, { runtimeMode: 'legacy' });
await expect(controller.findOne('conv-1', { id: 'user-1' })).rejects.toBeInstanceOf(
NotFoundException,