feat(#176): Integrate M4.2 infrastructure with M4.1 coordinator

Add CoordinatorIntegrationModule providing REST API endpoints for the Python
coordinator to communicate with the NestJS API infrastructure:

- POST /coordinator/jobs - Create job from coordinator webhook events
- PATCH /coordinator/jobs/:id/status - Update job status (PENDING -> RUNNING)
- PATCH /coordinator/jobs/:id/progress - Update job progress percentage
- POST /coordinator/jobs/:id/complete - Mark job complete with results
- POST /coordinator/jobs/:id/fail - Mark job failed with gate results
- GET /coordinator/jobs/:id - Get job details with events and steps
- GET /coordinator/health - Integration health check

Integration features:
- Job creation dispatches to BullMQ queues
- Status updates emit JobEvents for audit logging
- Completion/failure events broadcast via Herald to Discord
- Status transition validation (PENDING -> QUEUED -> RUNNING -> COMPLETED/FAILED)
- Health check includes BullMQ connection status and queue counts

Also adds JOB_PROGRESS event type to event-types.ts for progress tracking.

Fixes #176

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-01 21:54:34 -06:00
parent 3cdcbf6774
commit 5a51ee8c30
17 changed files with 1262 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
import { IsOptional, IsObject, IsNumber, Min } from "class-validator";
/**
* DTO for completing a job from the coordinator
*/
export class CompleteJobDto {
@IsOptional()
@IsObject()
result?: Record<string, unknown>;
@IsOptional()
@IsNumber()
@Min(0)
tokensUsed?: number;
@IsOptional()
@IsNumber()
@Min(0)
durationSeconds?: number;
}

View File

@@ -0,0 +1,28 @@
import { IsString, IsOptional, IsNumber, IsObject, Min, Max, IsUUID } from "class-validator";
/**
* DTO for creating a job from the coordinator
*/
export class CreateCoordinatorJobDto {
@IsUUID("4")
workspaceId!: string;
@IsString()
type!: string; // 'code-task', 'git-status', 'priority-calc'
@IsNumber()
issueNumber!: number;
@IsString()
repository!: string;
@IsOptional()
@IsNumber()
@Min(1)
@Max(100)
priority?: number;
@IsOptional()
@IsObject()
metadata?: Record<string, unknown>;
}

View File

@@ -0,0 +1,22 @@
import { IsString, IsOptional, IsObject } from "class-validator";
import type { QualityGateResult } from "../interfaces";
/**
* DTO for failing a job from the coordinator
*/
export class FailJobDto {
@IsString()
error!: string;
@IsOptional()
@IsObject()
gateResults?: QualityGateResult;
@IsOptional()
@IsString()
failedStep?: string;
@IsOptional()
@IsString()
continuationPrompt?: string;
}

View File

@@ -0,0 +1,5 @@
export * from "./create-coordinator-job.dto";
export * from "./update-job-status.dto";
export * from "./update-job-progress.dto";
export * from "./complete-job.dto";
export * from "./fail-job.dto";

View File

@@ -0,0 +1,19 @@
import { IsNumber, IsOptional, IsString, Min, Max } from "class-validator";
/**
* DTO for updating job progress from the coordinator
*/
export class UpdateJobProgressDto {
@IsNumber()
@Min(0)
@Max(100)
progressPercent!: number;
@IsOptional()
@IsString()
currentStep?: string;
@IsOptional()
@IsNumber()
tokensUsed?: number;
}

View File

@@ -0,0 +1,25 @@
import { IsString, IsOptional, IsEnum } from "class-validator";
/**
* Valid status values for coordinator status updates
*/
export enum CoordinatorJobStatus {
RUNNING = "RUNNING",
PENDING = "PENDING",
}
/**
* DTO for updating job status from the coordinator
*/
export class UpdateJobStatusDto {
@IsEnum(CoordinatorJobStatus)
status!: CoordinatorJobStatus;
@IsOptional()
@IsString()
agentId?: string;
@IsOptional()
@IsString()
agentType?: string;
}