Major changes: - Add internal/logging package with field constants, context propagation, sensitive data auto-redaction, and per-component log levels - Add worker timeout constants (TimeoutQuickOp, TimeoutHealthCheck, etc.) - Extend SDLC with callback handlers, generate endpoints, and executor - Add new cookbook trees for aeries and slackpath progression - Add skeleton templates for queue, realtime, and microservices - Add worker component template with async job processing - Refactor services and handlers to use new logging infrastructure - Split component.go into component_infra.go and component_listing.go Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
123 lines
3.8 KiB
Go
123 lines
3.8 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/orchard9/rdev/internal/domain"
|
|
"github.com/orchard9/rdev/internal/logging"
|
|
"github.com/orchard9/rdev/internal/sdlc"
|
|
)
|
|
|
|
// SDLCCallbackService handles build completion callbacks to update SDLC artifact status.
|
|
type SDLCCallbackService struct {
|
|
sdlcService *SDLCService
|
|
}
|
|
|
|
// NewSDLCCallbackService creates a new SDLC callback service.
|
|
func NewSDLCCallbackService(sdlcService *SDLCService) *SDLCCallbackService {
|
|
return &SDLCCallbackService{
|
|
sdlcService: sdlcService,
|
|
}
|
|
}
|
|
|
|
// HandleCallback processes a build completion and updates SDLC artifact status.
|
|
func (s *SDLCCallbackService) HandleCallback(ctx context.Context, payload *domain.SDLCCallbackPayload) error {
|
|
// Validate required fields
|
|
if payload.ProjectID == "" {
|
|
return fmt.Errorf("project_id is required")
|
|
}
|
|
if payload.Feature == "" {
|
|
return fmt.Errorf("feature is required")
|
|
}
|
|
if payload.ArtifactType == "" {
|
|
return fmt.Errorf("artifact_type is required")
|
|
}
|
|
|
|
// Map generate artifact type to SDLC artifact type
|
|
artType, err := s.mapArtifactType(payload.ArtifactType)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid artifact_type: %w", err)
|
|
}
|
|
|
|
log := logging.FromContext(ctx).WithService("sdlc_callback")
|
|
log.Info("handling SDLC callback",
|
|
"task_id", payload.TaskID,
|
|
logging.FieldProjectID, payload.ProjectID,
|
|
"feature", payload.Feature,
|
|
"artifact_type", payload.ArtifactType,
|
|
"success", payload.Success,
|
|
)
|
|
|
|
if payload.Success {
|
|
// QA results get "passed" status, others get "draft" (awaiting human approval)
|
|
if artType == sdlc.ArtifactQAResults {
|
|
if err := s.sdlcService.PassArtifact(ctx, payload.ProjectID, payload.Feature, artType); err != nil {
|
|
log.Error("failed to pass artifact",
|
|
logging.FieldError, err,
|
|
logging.FieldProjectID, payload.ProjectID,
|
|
"feature", payload.Feature,
|
|
"artifact_type", artType,
|
|
)
|
|
return fmt.Errorf("pass artifact: %w", err)
|
|
}
|
|
log.Info("artifact marked as passed",
|
|
logging.FieldProjectID, payload.ProjectID,
|
|
"feature", payload.Feature,
|
|
"artifact_type", artType,
|
|
)
|
|
} else {
|
|
// Mark as draft - awaiting human approval
|
|
// The SDLC service doesn't have a direct setDraft method, but we can
|
|
// achieve this by first ensuring the artifact exists, then the file
|
|
// generation creates the draft status automatically.
|
|
// For now, we'll log that the artifact was generated.
|
|
log.Info("artifact generated successfully (draft status)",
|
|
logging.FieldProjectID, payload.ProjectID,
|
|
"feature", payload.Feature,
|
|
"artifact_type", artType,
|
|
"commit_sha", payload.CommitSHA,
|
|
)
|
|
}
|
|
} else {
|
|
// Mark as needs_fix on failure
|
|
if err := s.sdlcService.NeedsFixArtifact(ctx, payload.ProjectID, payload.Feature, artType); err != nil {
|
|
log.Error("failed to mark artifact as needs_fix",
|
|
logging.FieldError, err,
|
|
logging.FieldProjectID, payload.ProjectID,
|
|
"feature", payload.Feature,
|
|
"artifact_type", artType,
|
|
)
|
|
return fmt.Errorf("needs_fix artifact: %w", err)
|
|
}
|
|
log.Info("artifact marked as needs_fix",
|
|
logging.FieldProjectID, payload.ProjectID,
|
|
"feature", payload.Feature,
|
|
"artifact_type", artType,
|
|
logging.FieldError, payload.Error,
|
|
)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// mapArtifactType converts generate artifact types to SDLC artifact types.
|
|
func (s *SDLCCallbackService) mapArtifactType(genType string) (sdlc.ArtifactType, error) {
|
|
switch genType {
|
|
case "spec":
|
|
return sdlc.ArtifactSpec, nil
|
|
case "design":
|
|
return sdlc.ArtifactDesign, nil
|
|
case "tasks":
|
|
return sdlc.ArtifactTasks, nil
|
|
case "code":
|
|
// Code generation doesn't map to a single artifact type
|
|
// The code is committed directly, no artifact status change needed
|
|
return "", fmt.Errorf("code artifact type does not have a corresponding SDLC artifact")
|
|
case "qa":
|
|
return sdlc.ArtifactQAResults, nil
|
|
default:
|
|
return "", fmt.Errorf("unknown artifact type: %s", genType)
|
|
}
|
|
}
|