feat(#166): Implement Stitcher module structure

Created the mosaic-stitcher module - the workflow orchestration layer
that wraps OpenClaw.

Responsibilities:
- Receive webhooks from @mosaic bot
- Apply Guard Rails (capability permissions)
- Apply Quality Rails (mandatory gates)
- Track all job steps and events
- Dispatch work to OpenClaw with constraints

Implementation:
- StitcherModule: Module definition with PrismaModule and BullMqModule
- StitcherService: Core orchestration logic
  - handleWebhook(): Process webhooks from @mosaic bot
  - dispatchJob(): Create RunnerJob and dispatch to BullMQ queue
  - applyGuardRails(): Check capability permissions for agent profiles
  - applyQualityRails(): Determine mandatory gates for job types
  - trackJobEvent(): Log events to database for audit trail
- StitcherController: HTTP endpoints
  - POST /stitcher/webhook: Webhook receiver
  - POST /stitcher/dispatch: Manual job dispatch
- DTOs and interfaces for type safety

TDD Process:
1. RED: Created failing tests (12 tests)
2. GREEN: Implemented minimal code to pass tests
3. REFACTOR: Fixed TypeScript strict mode issues

Quality Gates: ALL PASS
- Typecheck: PASS
- Lint: PASS
- Build: PASS
- Tests: PASS (12/12)

Token estimate: ~56,000 tokens

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-01 21:08:32 -06:00
parent 65b1dad64f
commit a2cd614e87
12 changed files with 741 additions and 0 deletions

View File

@@ -0,0 +1,37 @@
import { Controller, Post, Body } from "@nestjs/common";
import { StitcherService } from "./stitcher.service";
import { WebhookPayloadDto, DispatchJobDto } from "./dto";
import type { JobDispatchResult, JobDispatchContext } from "./interfaces";
/**
* StitcherController - Webhook and job dispatch endpoints
*
* Handles incoming webhooks from @mosaic bot and provides
* endpoints for manual job dispatch
*/
@Controller("stitcher")
export class StitcherController {
constructor(private readonly stitcherService: StitcherService) {}
/**
* Webhook endpoint for @mosaic bot
*/
@Post("webhook")
async webhook(@Body() payload: WebhookPayloadDto): Promise<JobDispatchResult> {
return this.stitcherService.handleWebhook(payload);
}
/**
* Manual job dispatch endpoint
*/
@Post("dispatch")
async dispatch(@Body() dto: DispatchJobDto): Promise<JobDispatchResult> {
const context: JobDispatchContext = {
workspaceId: dto.workspaceId,
type: dto.type,
...(dto.context !== undefined && { metadata: dto.context }),
};
return this.stitcherService.dispatchJob(context);
}
}