Files
Codewalkers/apps/server/trpc/routers/_helpers.ts
Lukas May 7e6921f01e feat: enrich listWaitingAgents with task/phase/initiative context via DB joins
Replaces the in-memory filter (agentManager.list() + filter) with a direct
repository query that LEFT JOINs tasks, phases, and initiatives to return
taskName, phaseName, initiativeName, and taskDescription alongside agent fields.

- Adds AgentWithContext interface and findWaitingWithContext() to AgentRepository port
- Implements findWaitingWithContext() in DrizzleAgentRepository using getTableColumns
- Wires agentRepository into TRPCContext, CreateContextOptions, and TrpcAdapterOptions
- Adds requireAgentRepository() helper following existing pattern
- Updates listWaitingAgents to use repository query instead of agentManager
- Adds 5 unit tests for findWaitingWithContext() covering all FK join edge cases
- Updates existing AgentRepository mocks to satisfy updated interface

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-06 23:29:49 +01:00

250 lines
7.8 KiB
TypeScript

/**
* Router Helpers
*
* Shared require*() helpers that validate context dependencies
* and throw TRPCError when a required dependency is missing.
*/
import { TRPCError } from '@trpc/server';
import type { TRPCContext } from '../context.js';
import type { TaskRepository } from '../../db/repositories/task-repository.js';
import type { MessageRepository } from '../../db/repositories/message-repository.js';
import type { InitiativeRepository } from '../../db/repositories/initiative-repository.js';
import type { PhaseRepository } from '../../db/repositories/phase-repository.js';
import type { PageRepository } from '../../db/repositories/page-repository.js';
import type { ProjectRepository } from '../../db/repositories/project-repository.js';
import type { AccountRepository } from '../../db/repositories/account-repository.js';
import type { ChangeSetRepository } from '../../db/repositories/change-set-repository.js';
import type { LogChunkRepository } from '../../db/repositories/log-chunk-repository.js';
import type { ConversationRepository } from '../../db/repositories/conversation-repository.js';
import type { ChatSessionRepository } from '../../db/repositories/chat-session-repository.js';
import type { ReviewCommentRepository } from '../../db/repositories/review-comment-repository.js';
import type { ErrandRepository } from '../../db/repositories/errand-repository.js';
import type { AgentRepository } from '../../db/repositories/agent-repository.js';
import type { DispatchManager, PhaseDispatchManager } from '../../dispatch/types.js';
import type { CoordinationManager } from '../../coordination/types.js';
import type { BranchManager } from '../../git/branch-manager.js';
import type { ExecutionOrchestrator } from '../../execution/orchestrator.js';
import type { PreviewManager } from '../../preview/index.js';
import type { ProjectSyncManager } from '../../git/remote-sync.js';
export function requireAgentManager(ctx: TRPCContext) {
if (!ctx.agentManager) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Agent manager not available',
});
}
return ctx.agentManager;
}
export function requireTaskRepository(ctx: TRPCContext): TaskRepository {
if (!ctx.taskRepository) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Task repository not available',
});
}
return ctx.taskRepository;
}
export function requireMessageRepository(ctx: TRPCContext): MessageRepository {
if (!ctx.messageRepository) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Message repository not available',
});
}
return ctx.messageRepository;
}
export function requireDispatchManager(ctx: TRPCContext): DispatchManager {
if (!ctx.dispatchManager) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Dispatch manager not available',
});
}
return ctx.dispatchManager;
}
export function requireCoordinationManager(ctx: TRPCContext): CoordinationManager {
if (!ctx.coordinationManager) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Coordination manager not available',
});
}
return ctx.coordinationManager;
}
export function requireInitiativeRepository(ctx: TRPCContext): InitiativeRepository {
if (!ctx.initiativeRepository) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Initiative repository not available',
});
}
return ctx.initiativeRepository;
}
export function requirePhaseRepository(ctx: TRPCContext): PhaseRepository {
if (!ctx.phaseRepository) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Phase repository not available',
});
}
return ctx.phaseRepository;
}
export function requirePhaseDispatchManager(ctx: TRPCContext): PhaseDispatchManager {
if (!ctx.phaseDispatchManager) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Phase dispatch manager not available',
});
}
return ctx.phaseDispatchManager;
}
export function requirePageRepository(ctx: TRPCContext): PageRepository {
if (!ctx.pageRepository) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Page repository not available',
});
}
return ctx.pageRepository;
}
export function requireProjectRepository(ctx: TRPCContext): ProjectRepository {
if (!ctx.projectRepository) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Project repository not available',
});
}
return ctx.projectRepository;
}
export function requireAccountRepository(ctx: TRPCContext): AccountRepository {
if (!ctx.accountRepository) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Account repository not available',
});
}
return ctx.accountRepository;
}
export function requireChangeSetRepository(ctx: TRPCContext): ChangeSetRepository {
if (!ctx.changeSetRepository) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Change set repository not available',
});
}
return ctx.changeSetRepository;
}
export function requireLogChunkRepository(ctx: TRPCContext): LogChunkRepository {
if (!ctx.logChunkRepository) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Log chunk repository not available',
});
}
return ctx.logChunkRepository;
}
export function requireBranchManager(ctx: TRPCContext): BranchManager {
if (!ctx.branchManager) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Branch manager not available',
});
}
return ctx.branchManager;
}
export function requireExecutionOrchestrator(ctx: TRPCContext): ExecutionOrchestrator {
if (!ctx.executionOrchestrator) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Execution orchestrator not available',
});
}
return ctx.executionOrchestrator;
}
export function requirePreviewManager(ctx: TRPCContext): PreviewManager {
if (!ctx.previewManager) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Preview manager not available',
});
}
return ctx.previewManager;
}
export function requireConversationRepository(ctx: TRPCContext): ConversationRepository {
if (!ctx.conversationRepository) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Conversation repository not available',
});
}
return ctx.conversationRepository;
}
export function requireChatSessionRepository(ctx: TRPCContext): ChatSessionRepository {
if (!ctx.chatSessionRepository) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Chat session repository not available',
});
}
return ctx.chatSessionRepository;
}
export function requireReviewCommentRepository(ctx: TRPCContext): ReviewCommentRepository {
if (!ctx.reviewCommentRepository) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Review comment repository not available',
});
}
return ctx.reviewCommentRepository;
}
export function requireProjectSyncManager(ctx: TRPCContext): ProjectSyncManager {
if (!ctx.projectSyncManager) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Project sync manager not available',
});
}
return ctx.projectSyncManager;
}
export function requireErrandRepository(ctx: TRPCContext): ErrandRepository {
if (!ctx.errandRepository) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Errand repository not available',
});
}
return ctx.errandRepository;
}
export function requireAgentRepository(ctx: TRPCContext): AgentRepository {
if (!ctx.agentRepository) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
message: 'Agent repository not available',
});
}
return ctx.agentRepository;
}