fix: resolve integration issues after phase branch merges

- Register errandProcedures in appRouter (was defined but never spread)
- Fix nullable projectId guard in errand delete/abandon procedures
- Add sendUserMessage stub to MockAgentManager in headquarters and
  radar-procedures tests (AgentManager interface gained this method)
- Add missing qualityReview field to Initiative fixture in file-io test
  (schema gained this column from the quality-review phase)
- Cast conflictFiles access in CLI errand resolve command

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lukas May
2026-03-06 22:16:53 +01:00
parent 0211cdb8a6
commit 753b2e9fb8
6 changed files with 22 additions and 13 deletions

View File

@@ -52,6 +52,7 @@ describe('writeInputFiles', () => {
status: 'active', status: 'active',
branch: 'cw/test-initiative', branch: 'cw/test-initiative',
executionMode: 'review_per_phase', executionMode: 'review_per_phase',
qualityReview: false,
createdAt: new Date('2026-01-01'), createdAt: new Date('2026-01-01'),
updatedAt: new Date('2026-01-02'), updatedAt: new Date('2026-01-02'),
}; };

View File

@@ -1878,7 +1878,7 @@ See the Codewalkers documentation for .cw-preview.yml format and options.`;
: `.cw-worktrees/${id}`; : `.cw-worktrees/${id}`;
console.log(`Resolve conflicts in worktree: ${worktreePath}`); console.log(`Resolve conflicts in worktree: ${worktreePath}`);
console.log('Conflicting files:'); console.log('Conflicting files:');
for (const f of errand.conflictFiles ?? []) { for (const f of (errand as any).conflictFiles ?? []) {
console.log(` ${f}`); console.log(` ${f}`);
} }
console.log('After resolving: stage and commit changes in the worktree, then run:'); console.log('After resolving: stage and commit changes in the worktree, then run:');

View File

@@ -63,6 +63,7 @@ class MockAgentManager implements AgentManager {
async delete(): Promise<void> { throw new Error('Not implemented'); } async delete(): Promise<void> { throw new Error('Not implemented'); }
async dismiss(): Promise<void> { throw new Error('Not implemented'); } async dismiss(): Promise<void> { throw new Error('Not implemented'); }
async resumeForConversation(): Promise<boolean> { return false; } async resumeForConversation(): Promise<boolean> { return false; }
async sendUserMessage(): Promise<void> { throw new Error('Not implemented'); }
} }
// ============================================================================= // =============================================================================

View File

@@ -69,6 +69,7 @@ class MockAgentManager implements AgentManager {
async delete(): Promise<void> { throw new Error('Not implemented'); } async delete(): Promise<void> { throw new Error('Not implemented'); }
async dismiss(): Promise<void> { throw new Error('Not implemented'); } async dismiss(): Promise<void> { throw new Error('Not implemented'); }
async resumeForConversation(): Promise<boolean> { return false; } async resumeForConversation(): Promise<boolean> { return false; }
async sendUserMessage(): Promise<void> { throw new Error('Not implemented'); }
} }
// ============================================================================= // =============================================================================

View File

@@ -25,6 +25,7 @@ import { previewProcedures } from './routers/preview.js';
import { conversationProcedures } from './routers/conversation.js'; import { conversationProcedures } from './routers/conversation.js';
import { chatSessionProcedures } from './routers/chat-session.js'; import { chatSessionProcedures } from './routers/chat-session.js';
import { headquartersProcedures } from './routers/headquarters.js'; import { headquartersProcedures } from './routers/headquarters.js';
import { errandProcedures } from './routers/errand.js';
// Re-export tRPC primitives (preserves existing import paths) // Re-export tRPC primitives (preserves existing import paths)
export { router, publicProcedure, middleware, createCallerFactory } from './trpc.js'; export { router, publicProcedure, middleware, createCallerFactory } from './trpc.js';
@@ -65,6 +66,7 @@ export const appRouter = router({
...conversationProcedures(publicProcedure), ...conversationProcedures(publicProcedure),
...chatSessionProcedures(publicProcedure), ...chatSessionProcedures(publicProcedure),
...headquartersProcedures(publicProcedure), ...headquartersProcedures(publicProcedure),
...errandProcedures(publicProcedure),
}); });
export type AppRouter = typeof appRouter; export type AppRouter = typeof appRouter;

View File

@@ -350,12 +350,14 @@ export function errandProcedures(publicProcedure: ProcedureBuilder) {
} }
// Remove worktree and branch (best-effort) // Remove worktree and branch (best-effort)
const project = await requireProjectRepository(ctx).findById(errand.projectId); if (errand.projectId) {
if (project) { const project = await requireProjectRepository(ctx).findById(errand.projectId);
const clonePath = await resolveClonePath(project, ctx); if (project) {
const worktreeManager = new SimpleGitWorktreeManager(clonePath); const clonePath = await resolveClonePath(project, ctx);
try { await worktreeManager.remove(errand.id); } catch { /* no-op if already gone */ } const worktreeManager = new SimpleGitWorktreeManager(clonePath);
try { await requireBranchManager(ctx).deleteBranch(clonePath, errand.branch); } catch { /* no-op */ } try { await worktreeManager.remove(errand.id); } catch { /* no-op if already gone */ }
try { await requireBranchManager(ctx).deleteBranch(clonePath, errand.branch); } catch { /* no-op */ }
}
} }
await repo.delete(errand.id); await repo.delete(errand.id);
@@ -426,12 +428,14 @@ export function errandProcedures(publicProcedure: ProcedureBuilder) {
} }
// Remove worktree and branch (best-effort) // Remove worktree and branch (best-effort)
const project = await requireProjectRepository(ctx).findById(errand.projectId); if (errand.projectId) {
if (project) { const project = await requireProjectRepository(ctx).findById(errand.projectId);
const clonePath = await resolveClonePath(project, ctx); if (project) {
const worktreeManager = new SimpleGitWorktreeManager(clonePath); const clonePath = await resolveClonePath(project, ctx);
try { await worktreeManager.remove(errand.id); } catch { /* no-op if already gone */ } const worktreeManager = new SimpleGitWorktreeManager(clonePath);
try { await branchManager.deleteBranch(clonePath, errand.branch); } catch { /* no-op */ } try { await worktreeManager.remove(errand.id); } catch { /* no-op if already gone */ }
try { await branchManager.deleteBranch(clonePath, errand.branch); } catch { /* no-op */ }
}
} }
const updated = await repo.update(input.id, { status: 'abandoned' }); const updated = await repo.update(input.id, { status: 'abandoned' });